From 07d2942df868bbed234e7c7c3c23ba7356c977f4 Mon Sep 17 00:00:00 2001 From: markster Date: Wed, 5 Dec 2001 18:38:19 +0000 Subject: Version 0.1.4 from FTP git-svn-id: http://svn.digium.com/svn/zaptel/trunk@34 5390a7c7-147a-4af0-8ec9-7488f05a26cb --- wcfxs.c | 834 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ wct1xxp.c | 569 +++++++++++++++++++++++++++++++----------- wctdm.c | 834 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 2090 insertions(+), 147 deletions(-) create mode 100755 wcfxs.c create mode 100755 wctdm.c diff --git a/wcfxs.c b/wcfxs.c new file mode 100755 index 0000000..88e39ff --- /dev/null +++ b/wcfxs.c @@ -0,0 +1,834 @@ +/* + * Wilcard S100P FXS Interface Driver for Zapata Telephony interface + * + * Written by Mark Spencer + * Matthew Fredrickson + * + * Copyright (C) 2001, Linux Support Services, Inc. + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "proslic.h" + +static alpha indirect_regs[] = +{ +{0,"DTMF_ROW_0_PEAK",0x55C2}, +{1,"DTMF_ROW_1_PEAK",0x51E6}, +{2,"DTMF_ROW2_PEAK",0x4B85}, +{3,"DTMF_ROW3_PEAK",0x4937}, +{4,"DTMF_COL1_PEAK",0x3333}, +{5,"DTMF_FWD_TWIST",0x0202}, +{6,"DTMF_RVS_TWIST",0x0202}, +{7,"DTMF_ROW_RATIO_TRES",0x0198}, +{8,"DTMF_COL_RATIO_TRES",0x0198}, +{9,"DTMF_ROW_2ND_ARM",0x0611}, +{10,"DTMF_COL_2ND_ARM",0x0202}, +{11,"DTMF_PWR_MIN_TRES",0x00E5}, +{12,"DTMF_OT_LIM_TRES",0x0A1C}, +{13,"OSC1_COEF",0x6D40}, +{14,"OSC1X",0x0470}, +{15,"OSC1Y",0x0000}, +{16,"OSC2_COEF",0x4A80}, +{17,"OSC2X",0x0830}, +{18,"OSC2Y",0x0000}, +{19,"RING_V_OFF",0x0000}, +{20,"RING_OSC",0x7EF0}, +{21,"RING_X",0x0160}, +{22,"RING_Y",0x0000}, +{23,"PULSE_ENVEL",0x2000}, +{24,"PULSE_X",0x2000}, +{25,"PULSE_Y",0x0000}, +//{26,"RECV_DIGITAL_GAIN",0x4000}, // playback volume set lower +{26,"RECV_DIGITAL_GAIN",0x2000}, // playback volume set lower +{27,"XMIT_DIGITAL_GAIN",0x4000}, +{28,"LOOP_CLOSE_TRES",0x1000}, +{29,"RING_TRIP_TRES",0x3600}, +{30,"COMMON_MIN_TRES",0x1000}, +{31,"COMMON_MAX_TRES",0x0200}, +{32,"PWR_ALARM_Q1Q2",0x0550}, +{33,"PWR_ALARM_Q3Q4",0x2600}, +{34,"PWR_ALARM_Q5Q6",0x1B80}, +{35,"LOOP_CLOSURE_FILTER",0x8000}, +{36,"RING_TRIP_FILTER",0x0320}, +{37,"TERM_LP_POLE_Q1Q2",0x0100}, +{38,"TERM_LP_POLE_Q3Q4",0x0100}, +{39,"TERM_LP_POLE_Q5Q6",0x0010}, +{40,"CM_BIAS_RINGING",0x0C00}, +{41,"DCDC_MIN_V",0x0C00}, +{42,"DCDC_XTRA",0x1000}, +}; + +#ifdef STANDALONE_ZAPATA +#include "zaptel.h" +#else +#include +#endif + +#define WC_MAX_IFACES 128 + +#define WC_CNTL 0x00 +#define WC_OPER 0x01 +#define WC_AUXC 0x02 +#define WC_AUXD 0x03 +#define WC_MASK0 0x04 +#define WC_MASK1 0x05 +#define WC_INTSTAT 0x06 +#define WC_AUXR 0x07 + +#define WC_DMAWS 0x08 +#define WC_DMAWI 0x0c +#define WC_DMAWE 0x10 +#define WC_DMARS 0x18 +#define WC_DMARI 0x1c +#define WC_DMARE 0x20 + +#define WC_AUXFUNC 0x2b +#define WC_SERCTL 0x2d +#define WC_FSCDELAY 0x2f + +#define BIT_CS (1 << 2) +#define BIT_SCLK (1 << 3) +#define BIT_SDI (1 << 4) +#define BIT_SDO (1 << 5) + +#define FLAG_EMPTY 0 +#define FLAG_WRITE 1 +#define FLAG_READ 2 + +#define RING_DEBOUNCE 64 /* Ringer Debounce (in ms) */ +#define BATT_DEBOUNCE 8 /* Battery debounce (in ms) */ + +#define FLAG_DOUBLE_CLOCK (1 << 0) + +struct wcfxs { + struct pci_dev *dev; + char *variety; + struct zt_span span; + struct zt_chan chan; + unsigned char ios; + int usecount; + int intcount; + int dead; + int pos; + int flags; + int freeregion; + int alt; + + /* Receive hook state and debouncing */ + int oldrxhook; + int debouncehook; + int lastrxhook; + int debounce; + + int idletxhookstate; /* IDLE changing hook state */ + unsigned long ioaddr; + dma_addr_t readdma; + dma_addr_t writedma; + volatile int *writechunk; /* Double-word aligned write memory */ + volatile int *readchunk; /* Double-word aligned read memory */ +}; + + +struct wcfxs_desc { + char *name; + int flags; +}; + +static struct wcfxs_desc wcfxs = { "Wildcard Prototype", 0 }; + +static struct wcfxs *ifaces[WC_MAX_IFACES]; + +static void wcfxs_release(struct wcfxs *wc); + +static int debug = 0; + +static inline void wcfxs_transmitprep(struct wcfxs *wc, unsigned char ints) +{ + volatile unsigned int *writechunk; + int x; + if (ints & 0x01) + /* Write is at interrupt address. Start writing from normal offset */ + writechunk = wc->writechunk; + else + writechunk = wc->writechunk + ZT_CHUNKSIZE; + /* Calculate Transmission */ + zt_transmit(&wc->span); + + for (x=0;xchan.writechunk[x] << 24; + + } + +} + +static inline void wcfxs_receiveprep(struct wcfxs *wc, unsigned char ints) +{ + volatile unsigned int *readchunk; + int x; + + if (ints & 0x08) + /* Read is at interrupt address. Valid data is available at normal offset */ + readchunk = wc->readchunk; + else + readchunk = wc->readchunk + ZT_CHUNKSIZE; + for (x=0;xchan.readchunk[x] = (readchunk[x] >> 24) & 0xff; + } + + zt_receive(&wc->span); +} + +static inline void wcfxs_check_hook(struct wcfxs *wc); + +static void wcfxs_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct wcfxs *wc = dev_id; + unsigned char ints; + + ints = inb(wc->ioaddr + WC_INTSTAT); + outb(ints, wc->ioaddr + WC_INTSTAT); + + + if (!ints) + return; + if (ints & 0x10) { + printk("PCI Master abort\n"); + return; + } + + if (ints & 0x20) { + printk("PCI Target abort\n"); + return; + } + if (ints & 0x0f) { + wc->intcount++; + if (!(wc->intcount % 10)) + wcfxs_check_hook(wc); + wcfxs_transmitprep(wc, ints); + wcfxs_receiveprep(wc, ints); + } + +} + +static inline void write_8bits(struct wcfxs *wc, unsigned char bits) +{ + /* Drop chip select */ + int x; + wc->ios &= ~BIT_CS; + outb(wc->ios, wc->ioaddr + WC_AUXD); + for (x=0;x<8;x++) { + /* Send out each bit, MSB first, drop SCLK as we do so */ + if (bits & 0x80) + wc->ios |= BIT_SDI; + else + wc->ios &= ~BIT_SDI; + wc->ios &= ~BIT_SCLK; + outb(wc->ios, wc->ioaddr + WC_AUXD); + /* Now raise SCLK high again and repeat */ + wc->ios |= BIT_SCLK; + outb(wc->ios, wc->ioaddr + WC_AUXD); + bits <<= 1; + } + /* Finally raise CS back high again */ + wc->ios |= BIT_CS; + outb(wc->ios, wc->ioaddr + WC_AUXD); + outb(wc->ios, wc->ioaddr + WC_AUXD); + outb(wc->ios, wc->ioaddr + WC_AUXD); + outb(wc->ios, wc->ioaddr + WC_AUXD); + +} +static inline unsigned char read_8bits(struct wcfxs *wc) +{ + unsigned char res=0, c; + int x; + /* Drop chip select */ + wc->ios &= ~BIT_CS; + outb(wc->ios, wc->ioaddr + WC_AUXD); + for (x=0;x<8;x++) { + res <<= 1; + /* Get SCLK */ + wc->ios &= ~BIT_SCLK; + outb(wc->ios, wc->ioaddr + WC_AUXD); + /* Now raise SCLK high again */ + wc->ios |= BIT_SCLK; + outb(wc->ios, wc->ioaddr + WC_AUXD); + + /* Read back the value */ + c = inb(wc->ioaddr + WC_AUXR); + if (c & BIT_SDO) + res |= 1; + } + /* Finally raise CS back high again */ + wc->ios |= BIT_CS; + outb(wc->ios, wc->ioaddr + WC_AUXD); + outb(wc->ios, wc->ioaddr + WC_AUXD); + outb(wc->ios, wc->ioaddr + WC_AUXD); + outb(wc->ios, wc->ioaddr + WC_AUXD); + + /* And return our result */ + return res; +} + +static void wcfxs_setreg(struct wcfxs *wc, unsigned char reg, unsigned char value) +{ + write_8bits(wc, reg & 0x7f); + write_8bits(wc, value); +} + +static unsigned char wcfxs_getreg(struct wcfxs *wc, unsigned char reg) +{ + write_8bits(wc, reg | 0x80); + return read_8bits(wc); +} + +static int wait_access(struct wcfxs *wc) +{ + unsigned char count, data; + count = 0; + #define MAX 60 + + + /* Wait for indirect access */ + while (count++ < MAX) + { + data = wcfxs_getreg(wc, I_STATUS); + + if (!data) + return 0; + + } + + if(count > (MAX-1)) printk(" ##### Loop error #####\n"); + + return -1; +} + +static int wcfxs_setreg_indirect(struct wcfxs *wc, unsigned char address, unsigned short data) +{ + + if(!wait_access(wc)) + { + wcfxs_setreg(wc, IDA_LO,(unsigned char)(data & 0xFF)); + wcfxs_setreg(wc, IDA_HI,(unsigned char)((data & 0xFF00)>>8)); + wcfxs_setreg(wc, IAA,address); + return 0; + } + + return -1; +} + +static int wcfxs_getreg_indirect(struct wcfxs *wc, unsigned char address) +{ + if (!wait_access(wc)) { + wcfxs_setreg(wc, IAA, address); + if (!wait_access(wc)) { + unsigned char data1, data2; + data1 = wcfxs_getreg(wc, IDA_LO); + data2 = wcfxs_getreg(wc, IDA_HI); + return data1 | (data2 << 8); + } else + printk("Failed to wait inside\n"); + } else + printk("failed to wait\n"); + + return -1; +} + +static int wcfxs_init_indirect_regs(struct wcfxs *wc) +{ + unsigned char i; + + for (i=0; i<43; i++) + { + if(wcfxs_setreg_indirect(wc, i,indirect_regs[i].initial)) + return -1; + } + + return 0; +} + +static int wcfxs_verify_indirect_regs(struct wcfxs *wc) +{ + int passed = 1; + unsigned short i, initial; + int j; + + for (i=0; i<43; i++) + { + if((j = wcfxs_getreg_indirect(wc, (unsigned char) i)) < 0) { + printk("Failed to read indirect register %d\n", i); + return -1; + } + initial= indirect_regs[i].initial; + + if ( j != initial ) + { + printk("!!!!!!! %s iREG %X = %X should be %X\n", + indirect_regs[i].name,i,j,initial ); + passed = 0; + } + } + + if (passed) { + if (debug) + printk("Init Indirect Registers completed successfully.\n"); + } else { + printk(" !!!!! Init Indirect Registers UNSUCCESSFULLY.\n"); + return -1; + } + return 0; +} + +static int wcfxs_calibrate(struct wcfxs *wc) +{ + unsigned char x; + + wcfxs_setreg(wc, 92, 0xc8); + wcfxs_setreg(wc, 97, 0); + wcfxs_setreg(wc, 93, 0x19); + wcfxs_setreg(wc, 14, 0); + wcfxs_setreg(wc, 93, 0x99); + + x = wcfxs_getreg(wc, 93); + if (debug) + printk("DC Cal x=%x\n",x); + wcfxs_setreg(wc, 97, 0); + wcfxs_setreg(wc, CALIBR1, CALIBRATE_LINE); + x = wcfxs_getreg(wc, CALIBR1); + wcfxs_setreg(wc, LINE_STATE, ACTIVATE_LINE); + + return 0; +} + +static int wcfxs_init_proslic(struct wcfxs *wc) +{ + int blah; + + /* By default, always send on hook */ + wc->idletxhookstate = 2; + + /* Disable Auto Power Alarm Detect and other "features" */ + wcfxs_setreg(wc, 67, 0x0e); + blah = wcfxs_getreg(wc, 67); + + if (wcfxs_init_indirect_regs(wc)) { + printk(KERN_INFO "Indirect Registers failed to initialize.\n"); + return -1; + } + if (wcfxs_verify_indirect_regs(wc)) { + printk(KERN_INFO "Indirect Registers failed verification.\n"); + return -1; + } + if (wcfxs_calibrate(wc)) { + printk(KERN_INFO "ProSlic Died on Activation.\n"); + return -1; + } + if (wcfxs_setreg_indirect(wc, 97, 0x0)) { // Stanley: for the bad recording fix + printk(KERN_INFO "ProSlic IndirectReg Died.\n"); + return -1; + } + wcfxs_setreg(wc, 1, 0x2a); + // U-Law GCI 8-bit interface + wcfxs_setreg(wc, 2, 0); // Tx Start count low byte 0 + wcfxs_setreg(wc, 3, 0); // Tx Start count high byte 0 + wcfxs_setreg(wc, 4, 0); // Rx Start count low byte 0 + wcfxs_setreg(wc, 5, 0); // Rx Start count high byte 0 + wcfxs_setreg(wc, 8, 0x0); // disable loopback + wcfxs_setreg(wc, 18, 0xff); // clear all interrupt + wcfxs_setreg(wc, 19, 0xff); + wcfxs_setreg(wc, 20, 0xff); + wcfxs_setreg(wc, 21, 0x00); // enable interrupt + wcfxs_setreg(wc, 22, 0x02); // Loop detection interrupt + wcfxs_setreg(wc, 23, 0x01); // DTMF detection interrupt + wcfxs_setreg(wc, 72, 0x20); +#ifdef BOOST_RINGER + /* Beef up Ringing voltage to 89V */ + if (wcfxs_setreg_indirect(wc, 23, 0x1d1)) + return -1; +#endif + return 0; +} + +static inline void wcfxs_check_hook(struct wcfxs *wc) +{ + char res; + int hook; + + /* For some reason we have to debounce the + hook detector. */ + + res = wcfxs_getreg(wc, 68); + hook = (res & 1); + if (hook != wc->lastrxhook) { + /* Reset the debounce */ + wc->debounce = 3; + } else { + if (wc->debounce > -1) + wc->debounce--; + } + wc->lastrxhook = hook; + if (!wc->debounce) + wc->debouncehook = hook; + + if (!wc->oldrxhook && wc->debouncehook) { + /* Off hook */ + if (debug) + printk("wcfxs: Going off hook\n"); + zt_hooksig(&wc->chan, ZT_RXSIG_OFFHOOK); + wc->oldrxhook = 1; + + } else if (wc->oldrxhook && !wc->debouncehook) { + /* On hook */ + if (debug) + printk("wcfxs: Going on hook\n"); + zt_hooksig(&wc->chan, ZT_RXSIG_ONHOOK); + wc->oldrxhook = 0; + } + +} + +static int wcfxs_open(struct zt_chan *chan) +{ + struct wcfxs *wc = chan->pvt; + if (wc->dead) + return -ENODEV; + wc->usecount++; + MOD_INC_USE_COUNT; + return 0; +} + +static int wcfxs_close(struct zt_chan *chan) +{ + struct wcfxs *wc = chan->pvt; + wc->usecount--; + MOD_DEC_USE_COUNT; + /* If we're dead, release us now */ + if (!wc->usecount && wc->dead) + wcfxs_release(wc); + return 0; +} + +static int wcfxs_hooksig(struct zt_chan *chan, zt_txsig_t txsig) +{ + struct wcfxs *wc = chan->pvt; + int reg=0; + unsigned char txhook = 0; + switch(txsig) { + case ZT_TXSIG_ONHOOK: + switch(chan->sig) { + case ZT_SIG_FXOKS: + case ZT_SIG_FXOLS: + txhook = wc->idletxhookstate; + break; + case ZT_SIG_FXOGS: + txhook = 3; + break; + } + break; + case ZT_TXSIG_OFFHOOK: + txhook = wc->idletxhookstate; + break; + case ZT_TXSIG_START: + txhook = 4; + break; + case ZT_TXSIG_KEWL: + txhook = 0; + break; + default: + printk("wcfxs: Can't set tx state to %d\n", txsig); + } + if (debug) + printk("Setting hook state to %d (%02x)\n", txsig, reg); + + wcfxs_setreg(wc, 64, txhook); + return 0; +} + +static int wcfxs_initialize(struct wcfxs *wc) +{ + /* Zapata stuff */ + sprintf(wc->span.name, "WCFXS/%d", wc->pos); + sprintf(wc->span.desc, "%s Board %d\n", wc->variety, wc->pos + 1); + wc->span.deflaw = ZT_LAW_MULAW; + sprintf(wc->chan.name, "WCFXS/%d/%d", wc->pos, 0); + wc->chan.sigcap = ZT_SIG_FXOKS | ZT_SIG_FXOLS | ZT_SIG_FXOGS; + wc->chan.chanpos = 1; + wc->span.chans = &wc->chan; + wc->span.channels = 1; + wc->span.hooksig = wcfxs_hooksig; + wc->span.open = wcfxs_open; + wc->span.close = wcfxs_close; + wc->span.flags = ZT_FLAG_RBS; + init_waitqueue_head(&wc->span.maintq); + + wc->span.pvt = wc; + wc->chan.pvt = wc; + if (zt_register(&wc->span, 0)) { + printk("Unable to register span with zaptel\n"); + return -1; + } + return 0; +} + +static int wcfxs_hardware_init(struct wcfxs *wc) +{ + /* Hardware stuff */ + long oldjiffies; + /* Reset PCI Interface chip and registers (and serial) */ + outb(0x0e, wc->ioaddr + WC_CNTL); + /* Setup our proper outputs for when we switch for our "serial" port */ + wc->ios = BIT_CS | BIT_SCLK | BIT_SDI; + + outb(wc->ios, wc->ioaddr + WC_AUXD); + + /* Set all to outputs except AUX 5 and 0, which are inputs */ + outb(0xde, wc->ioaddr + WC_AUXC); + + /* Wait a sec */ + oldjiffies = jiffies; + while(jiffies - oldjiffies < 2); + /* Back to normal, with automatic DMA wrap around */ + outb(0x30 | 0x01, wc->ioaddr + WC_CNTL); + + /* Make sure serial port and DMA are out of reset */ + outb(inb(wc->ioaddr + WC_CNTL) & 0xf9, WC_CNTL); + + /* Configure serial port for MSB->LSB operation */ + if (wc->flags & FLAG_DOUBLE_CLOCK) + outb(0xc1, wc->ioaddr + WC_SERCTL); + else + outb(0xc0, wc->ioaddr + WC_SERCTL); + + /* Delay FSC by 0 so it's properly aligned */ + outb(0x0, wc->ioaddr + WC_FSCDELAY); + + /* Setup DMA Addresses */ + outl(wc->writedma, wc->ioaddr + WC_DMAWS); /* Write start */ + outl(wc->writedma + ZT_CHUNKSIZE * 4, wc->ioaddr + WC_DMAWI); /* Middle (interrupt) */ + outl(wc->writedma + ZT_CHUNKSIZE * 8 - 4, wc->ioaddr + WC_DMAWE); /* End */ + + outl(wc->readdma, wc->ioaddr + WC_DMARS); /* Read start */ + outl(wc->readdma + ZT_CHUNKSIZE * 4, wc->ioaddr + WC_DMARI); /* Middle (interrupt) */ + outl(wc->readdma + ZT_CHUNKSIZE * 8 - 4, wc->ioaddr + WC_DMARE); /* End */ + + /* Clear interrupts */ + outb(0xff, wc->ioaddr + WC_INTSTAT); + return wcfxs_init_proslic(wc); +} + +static void wcfxs_enable_interrupts(struct wcfxs *wc) +{ + /* Enable interrupts (we care about all of them) */ + outb(0x3f, wc->ioaddr + WC_MASK0); + /* No external interrupts */ + outb(0x00, wc->ioaddr + WC_MASK1); +} + +static void wcfxs_start_dma(struct wcfxs *wc) +{ + /* Reset Master and TDM */ + outb(0x0f, wc->ioaddr + WC_CNTL); + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(1); + outb(0x01, wc->ioaddr + WC_CNTL); + outb(0x01, wc->ioaddr + WC_OPER); +} + +static void wcfxs_stop_dma(struct wcfxs *wc) +{ + outb(0x00, wc->ioaddr + WC_OPER); +} + +static void wcfxs_disable_interrupts(struct wcfxs *wc) +{ + outb(0x00, wc->ioaddr + WC_MASK0); + outb(0x00, wc->ioaddr + WC_MASK1); +} + +static int __devinit wcfxs_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) +{ + int res; + struct wcfxs *wc; + struct wcfxs_desc *d = (struct wcfxs_desc *)ent->driver_data; + int x; + static int initd_ifaces=0; + + if(initd_ifaces){ + memset((void *)ifaces,0,(sizeof(struct wcfxs *))*WC_MAX_IFACES); + initd_ifaces=1; + } + for (x=0;x= WC_MAX_IFACES) { + printk("Too many interfaces\n"); + return -EIO; + } + + if (pci_enable_device(pdev)) { + res = -EIO; + } else { + wc = kmalloc(sizeof(struct wcfxs), GFP_KERNEL); + if (wc) { + ifaces[x] = wc; + memset(wc, 0, sizeof(struct wcfxs)); + wc->ioaddr = pci_resource_start(pdev, 0); + wc->dev = pdev; + wc->pos = x; + wc->variety = d->name; + wc->flags = d->flags; + /* Keep track of whether we need to free the region */ + if (request_region(wc->ioaddr, 0xff, "wcfxs")) + wc->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 */ + wc->writechunk = (int *)pci_alloc_consistent(pdev, ZT_MAX_CHUNKSIZE * 2 * 2 * 2 * 4, &wc->writedma); + if (!wc->writechunk) { + printk("wcfxs: Unable to allocate DMA-able memory\n"); + if (wc->freeregion) + release_region(wc->ioaddr, 0xff); + return -ENOMEM; + } + + wc->readchunk = wc->writechunk + ZT_MAX_CHUNKSIZE * 2; /* in doublewords */ + wc->readdma = wc->writedma + ZT_MAX_CHUNKSIZE * 8; /* in bytes */ + + if (wcfxs_initialize(wc)) { + printk("wcfxs: Unable to intialize FXS\n"); + if (wc->freeregion) + release_region(wc->ioaddr, 0xff); + kfree(wc); + return -EIO; + } + + /* Enable bus mastering */ + pci_set_master(pdev); + + /* Keep track of which device we are */ + pci_set_drvdata(pdev, wc); + + if (request_irq(pdev->irq, wcfxs_interrupt, SA_SHIRQ, "wcfxs", wc)) { + printk("wcfxs: Unable to request IRQ %d\n", pdev->irq); + if (wc->freeregion) + release_region(wc->ioaddr, 0xff); + kfree(wc); + return -EIO; + } + + + if (wcfxs_hardware_init(wc)) { + zt_unregister(&wc->span); + if (wc->freeregion) + release_region(wc->ioaddr, 0xff); + kfree(wc); + return -EIO; + } + /* Enable interrupts */ + wcfxs_enable_interrupts(wc); + /* Initialize Write/Buffers to all blank data */ + memset((void *)wc->writechunk,0,ZT_MAX_CHUNKSIZE * 2 * 2 * 4); + + /* Start DMA */ + wcfxs_start_dma(wc); + + printk("Found a Wildcard FXS: %s\n", wc->variety); + res = 0; + } else + res = -ENOMEM; + } + return res; +} + +static void wcfxs_release(struct wcfxs *wc) +{ + zt_unregister(&wc->span); + if (wc->freeregion) + release_region(wc->ioaddr, 0xff); + kfree(wc); + printk("Freed a Wildcard\n"); +} + +static void __devexit wcfxs_remove_one(struct pci_dev *pdev) +{ + struct wcfxs *wc = pci_get_drvdata(pdev); + if (wc) { + + /* Stop any DMA */ + wcfxs_stop_dma(wc); + + /* In case hardware is still there */ + wcfxs_disable_interrupts(wc); + + /* Immediately free resources */ + pci_free_consistent(pdev, ZT_MAX_CHUNKSIZE * 2 * 2 * 2 * 4, (void *)wc->writechunk, wc->writedma); + free_irq(pdev->irq, wc); + + /* Reset PCI chip and registers */ + outb(0x0e, wc->ioaddr + WC_CNTL); + + /* Release span, possibly delayed */ + if (!wc->usecount) + wcfxs_release(wc); + else + wc->dead = 1; + } +} + +static struct pci_device_id wcfxs_pci_tbl[] __devinitdata = { + { 0xe159, 0x0001, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) &wcfxs }, +}; + +static struct pci_driver wcfxs_driver = { + name: "wcfxs", + probe: wcfxs_init_one, + remove: wcfxs_remove_one, + suspend: NULL, + resume: NULL, + id_table: wcfxs_pci_tbl, +}; + +static int __init wcfxs_init(void) +{ + int res; + res = pci_module_init(&wcfxs_driver); + if (res) + return -ENODEV; + return 0; +} + +static void __exit wcfxs_cleanup(void) +{ + pci_unregister_driver(&wcfxs_driver); +} + +MODULE_PARM(debug, "i"); +MODULE_DESCRIPTION("Wildcard S100P Zaptel Driver"); +MODULE_AUTHOR("Mark Spencer "); + +module_init(wcfxs_init); +module_exit(wcfxs_cleanup); diff --git a/wct1xxp.c b/wct1xxp.c index a173941..82a634c 100755 --- a/wct1xxp.c +++ b/wct1xxp.c @@ -32,13 +32,23 @@ #include #include #include +#include #ifdef STANDALONE_ZAPATA #include "zaptel.h" #else #include #endif -#define DELAY 0x30 /* 30 = 15 cycles, 10 = 8 cycles, 0 = 3 cycles */ +#define WC_MAX_CARDS 32 + +#define TEST_REGS + +/* Define to get more attention-grabbing but slightly more I/O using + alarm status */ +#define FANCY_ALARM + + +#define DELAY 0x0 /* 30 = 15 cycles, 10 = 8 cycles, 0 = 3 cycles */ #define WC_CNTL 0x00 #define WC_OPER 0x01 @@ -68,7 +78,7 @@ /* Offset between transmit and receive */ #define WC_OFFSET 4 -#define BIT_CS (1 << 5) +#define BIT_CS (1 << 7) #define BIT_OK (1 << 0) #define BIT_TEST (1 << 1) @@ -76,26 +86,41 @@ #define BIT_ALARM (1 << 3) static int chanmap[] = -{ 1,2,3, - 5,6,7, - 9,10,11, - 13,14,15, - 17,18,19, - 21,22,23, - 25,26,27, - 29,30,31 }; +{ 2,1,0, + 6,5,4, + 10,9,8, + 14,13,12, + 18,17,16, + 22,21,20, + 26,25,24, + 30,29,28 }; + +#ifdef FANCY_ALARM +static int altab[] = { +0, 0, 0, 1, 2, 3, 4, 6, 8, 9, 11, 13, 16, 18, 20, 22, 24, 25, 27, 28, 29, 30, 31, 31, 32, 31, 31, 30, 29, 28, 27, 25, 23, 22, 20, 18, 16, 13, 11, 9, 8, 6, 4, 3, 2, 1, 0, 0, +}; +#endif struct t1xxp { struct pci_dev *dev; + spinlock_t lock; + int num; + /* Our offset for finding channel 1 */ + int offset; char *variety; int usecount; int dead; int alarmtimer; int alarm; - int alarmthreshold; + int alreadyrunning; +#ifdef FANCY_ALARM + int alarmpos; +#endif unsigned char ledtestreg; unsigned char outbyte; unsigned long ioaddr; + unsigned char txsiga[3]; + unsigned char txsigb[3]; dma_addr_t readdma; dma_addr_t writedma; volatile unsigned char *writechunk; /* Double-word aligned write memory */ @@ -104,6 +129,26 @@ struct t1xxp { struct zt_chan chans[24]; /* Channels */ }; +static struct t1xxp *cards[WC_MAX_CARDS]; + +static inline void start_alarm(struct t1xxp *wc) +{ + wc->alarm = 1; +#ifdef FANCY_ALARM + wc->alarmpos = 0; +#endif + wc->alarmtimer = 0; +} + +static inline void stop_alarm(struct t1xxp *wc) +{ + wc->alarm = 0; +#ifdef FANCY_ALARM + wc->alarmpos = 0; +#endif + wc->alarmtimer = 0; +} + static inline void select_framer(struct t1xxp *wc) { if (wc->outbyte & BIT_CS) { @@ -120,23 +165,6 @@ static inline void select_control(struct t1xxp *wc) } } -static inline void select_page(struct t1xxp *wc, unsigned char reg) -{ - int page = (reg & 0x30) << 2; - if (wc->outbyte != page) { - /* Clear high bits */ - wc->outbyte &= 0x3f; - /* Set page */ - wc->outbyte |= page; - /* Make sure we've turned on the framer */ - wc->outbyte &= ~BIT_CS; - outb(wc->outbyte, wc->ioaddr + WC_AUXD); - } else if (wc->outbyte & BIT_CS) { - /* Check to be sure we don't still have to enable the framer */ - select_framer(wc); - } -} - static int t1xxp_open(struct zt_chan *chan) { struct t1xxp *wc = chan->pvt; @@ -150,15 +178,20 @@ static int t1xxp_open(struct zt_chan *chan) static int t1_get_reg(struct t1xxp *wc, int reg) { unsigned char res; - select_page(wc, reg); - res = inb(wc->ioaddr + WC_USERREG + ((reg & 0xf) << 2)); + select_framer(wc); + /* Send address */ + outb(reg & 0xff, wc->ioaddr + WC_USERREG + 4); + /* Get value */ + res = inb(wc->ioaddr + WC_USERREG); return res; } static int t1_set_reg(struct t1xxp *wc, int reg, unsigned char val) { - select_page(wc, reg); - outb(val, wc->ioaddr + WC_USERREG + ((reg & 0xf) << 2)); + select_framer(wc); + /* Send address */ + outb(reg & 0xff, wc->ioaddr + WC_USERREG + 4); + outb(val, wc->ioaddr + WC_USERREG); return 0; } @@ -179,6 +212,7 @@ static int control_get_reg(struct t1xxp *wc, int reg) static void t1xxp_release(struct t1xxp *wc) { + zt_unregister(&wc->span); kfree(wc); printk("Freed a Wildcard\n"); } @@ -194,19 +228,314 @@ static int t1xxp_close(struct zt_chan *chan) return 0; } +static void t1xxp_enable_interrupts(struct t1xxp *wc) +{ + /* Clear interrupts */ + outb(0xff, wc->ioaddr + WC_INTSTAT); + /* Enable interrupts (we care about all of them) */ + outb(0x3f, wc->ioaddr + WC_MASK0); + /* No external interrupts */ + outb(0x00, wc->ioaddr + WC_MASK1); +} + +static void t1xxp_start_dma(struct t1xxp *wc) +{ + /* Reset Master and TDM */ + outb(DELAY | 0x0f, wc->ioaddr + WC_CNTL); + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(1); + outb(DELAY | 0x01, wc->ioaddr + WC_CNTL); + outb(0x01, wc->ioaddr + WC_OPER); + printk("Started DMA\n"); +} + +static void t1xxp_stop_dma(struct t1xxp *wc) +{ + outb(0x00, wc->ioaddr + WC_OPER); +} + +static void t1xxp_disable_interrupts(struct t1xxp *wc) +{ + outb(0x00, wc->ioaddr + WC_MASK0); + outb(0x00, wc->ioaddr + WC_MASK1); +} + +static void t1xxp_framer_start(struct t1xxp *wc) +{ + int i; + char *coding, *framing; + unsigned long endjiffies; + /* Build up config */ + i = 0x20; + if (wc->span.lineconfig & ZT_CONFIG_ESF) { + coding = "ESF"; + i = 0x88; + } else { + coding = "SF"; + } + if (wc->span.lineconfig & ZT_CONFIG_B8ZS) { + framing = "B8ZS"; + i |= 0x44; + } else { + framing = "AMI"; + } + t1_set_reg(wc, 0x38, i); + if (!(wc->span.lineconfig & ZT_CONFIG_ESF)) { + /* 1c in FDL bit */ + t1_set_reg(wc, 0x7e, 0x1c); + } else { + t1_set_reg(wc, 0x7e, 0x00); + } + + /* Set outgoing LBO */ + t1_set_reg(wc, 0x7c, wc->span.txlevel << 5); + + printk("Using %s/%s coding/framing\n", coding, framing); + if (!wc->alreadyrunning) { + /* Set LIRST bit to 1 */ + t1_set_reg(wc, 0x30, 0x1); + + /* Wait 100ms to give plenty of time for reset */ + endjiffies = jiffies + 10; + while(endjiffies < jiffies); + + /* Reset elastic stores */ + t1_set_reg(wc, 0x30, 0x40); + wc->alreadyrunning = 1; + } +#if 0 + { + t1_set_reg(wc, 0x3c, 0xff); + t1_set_reg(wc, 0x3d, 0xff); + t1_set_reg(wc, 0x3e, 0xff); + int x; + for (i=0x20;i<0x40;i++) { + x = t1_get_reg(wc, i); + if (x) + printk("%02x: %02x\n", i, x); + } + for (i=0x60;i<0x80;i++) { + x = t1_get_reg(wc, i); + if (x) + printk("%02x: %02x\n", i, x); + } + } +#endif +} + +static int t1xxp_framer_hard_reset(struct t1xxp *wc) +{ + int x; + /* Initialize all registers to 0 */ + for (x=0x20;x<0x40;x++) + t1_set_reg(wc, x, 0); + for (x=0x60;x<0x80;x++) + t1_set_reg(wc, x, 0); + + /* Full-on sync required */ + t1_set_reg(wc, 0x2b, 0x08); + + /* RSYNC is an input */ + t1_set_reg(wc, 0x2c, 0x08); + + /* Enable tx RBS bits */ + t1_set_reg(wc, 0x35, 0x10); + + /* TSYNC is output */ + t1_set_reg(wc, 0x36, 0x04); + + /* Tx and Rx elastic store enabled, 2.048 Mhz (in theory) */ + t1_set_reg(wc, 0x37, 0x8c); +#ifdef TEST_REGS + printk("Testing framer registers...\n"); + t1_set_reg(wc, 0x6c, 0xff); + printk("Expecting ff, got %02x\n", t1_get_reg(wc, 0x6c)); + t1_set_reg(wc, 0x6c, 0x00); + printk("Expecting 00, got %02x\n", t1_get_reg(wc, 0x6c)); + t1_set_reg(wc, 0x6c, 0xaa); + printk("Expecting aa, got %02x\n", t1_get_reg(wc, 0x6c)); + t1_set_reg(wc, 0x6c, 0x55); + printk("Expecting 55, got %02x\n", t1_get_reg(wc, 0x6c)); + t1_set_reg(wc, 0x6c, 0x00); + printk("Testing control registers...\n"); + control_set_reg(wc, WC_LEDTEST, 0xff); + printk("Expecting ff, got %02x\n", control_get_reg(wc, WC_LEDTEST)); + control_set_reg(wc, WC_LEDTEST, 0x00); + printk("Expecting 00, got %02x\n", control_get_reg(wc, WC_LEDTEST)); + control_set_reg(wc, WC_LEDTEST, 0xaa); + printk("Expecting aa, got %02x\n", control_get_reg(wc, WC_LEDTEST)); + control_set_reg(wc, WC_LEDTEST, 0x55); + printk("Expecting 55, got %02x\n", control_get_reg(wc, WC_LEDTEST)); + control_set_reg(wc, WC_LEDTEST, 0x00); +#endif + return 0; +} + +static int t1xxp_rbsbits(struct zt_chan *chan, int bits) +{ + struct t1xxp *wc = chan->pvt; + /* Byte offset */ + int b = (chan->chanpos - 1) / 8; + int o = (chan->chanpos - 1) % 8; + unsigned char mask = (1 << o); + if (bits & ZT_ABIT) { + /* Set A-bit */ + wc->txsiga[b] |= mask; + } else { + /* Clear A-bit */ + wc->txsiga[b] &= ~mask; + } + if (bits & ZT_BBIT) { + /* Set B-bit */ + wc->txsigb[b] |= mask; + } else { + wc->txsigb[b] &= ~mask; + } + /* Output new values */ + t1_set_reg(wc, 0x70 + b, wc->txsiga[b]); + t1_set_reg(wc, 0x73 + b, wc->txsigb[b]); + t1_set_reg(wc, 0x76 + b, wc->txsiga[b]); + t1_set_reg(wc, 0x79 + b, wc->txsigb[b]); + return 0; +} + +static int t1xxp_ioctl(struct zt_chan *chan, unsigned int cmd, unsigned long data) +{ + switch(cmd) { + default: + return -ENOTTY; + } +} + +static int t1xxp_startup(struct zt_span *span) +{ + struct t1xxp *wc = span->pvt; + /* Reset framer with proper parameters and start */ + t1xxp_framer_start(wc); + if (!(span->flags & ZT_FLAG_RUNNING)) { + /* Only if we're not already going */ + t1xxp_enable_interrupts(wc); + t1xxp_start_dma(wc); + span->flags |= ZT_FLAG_RUNNING; + } + return 0; +} + +static int t1xxp_shutdown(struct zt_span *span) +{ + struct t1xxp *wc = span->pvt; + t1xxp_stop_dma(wc); + t1xxp_disable_interrupts(wc); + span->flags &= ~ZT_FLAG_RUNNING; + return 0; +} + +static int t1xxp_maint(struct zt_span *span, int mode) +{ + /* XXX Implement me XXX */ + return -1; +} + +static void t1xxp_set_clear(struct t1xxp *wc) +{ + /* Setup registers */ + int x,y; + unsigned char b; + for (x=0;x<3;x++) { + b = 0; + for (y=0;y<8;y++) + if (wc->chans[x * 8 + y].sig & ZT_SIG_CLEAR) + b |= (1 << y); + t1_set_reg(wc, 0x39 + x, b); + } +} + +static int t1xxp_chanconfig(struct zt_chan *chan, int sigtype) +{ + struct t1xxp *wc = chan->pvt; + int flags; + int alreadyrunning = chan->span->flags & ZT_FLAG_RUNNING; + spin_lock_irqsave(&wc->lock, flags); + if (alreadyrunning) + t1xxp_set_clear(wc); + spin_unlock_irqrestore(&wc->lock, flags); + return 0; +} + +static int t1xxp_spanconfig(struct zt_span *span, struct zt_lineconfig *lc) +{ + span->lineconfig = lc->lineconfig; + span->txlevel = lc->lbo; + span->rxlevel = 0; + /* Do we want to SYNC on receive or not */ + span->syncsrc = lc->sync; + /* If already running, apply changes immediately */ + if (span->flags & ZT_FLAG_RUNNING) + return t1xxp_startup(span); + return 0; +} + +static int t1xxp_software_init(struct t1xxp *wc) +{ + int x; + /* Find position */ + for (x=0;x= WC_MAX_CARDS) + return -1; + wc->num = x; + sprintf(wc->span.name, "WCT1/%d", wc->num); + sprintf(wc->span.desc, "%s card %d", wc->variety, wc->num); + wc->span.spanconfig = t1xxp_spanconfig; + wc->span.chanconfig = t1xxp_chanconfig; + wc->span.startup = t1xxp_startup; + wc->span.shutdown = t1xxp_shutdown; + wc->span.rbsbits = t1xxp_rbsbits; + wc->span.maint = t1xxp_maint; + wc->span.open = t1xxp_open; + wc->span.close = t1xxp_close; + wc->span.channels = 24; + wc->span.chans = wc->chans; + wc->span.flags = ZT_FLAG_RBS; + wc->span.linecompat = ZT_CONFIG_AMI | ZT_CONFIG_B8ZS | ZT_CONFIG_D4 | ZT_CONFIG_ESF; + wc->span.ioctl = t1xxp_ioctl; + wc->span.pvt = wc; + init_waitqueue_head(&wc->span.maintq); + for (x=0;x<24;x++) { + sprintf(wc->chans[x].name, "WCT1/%d/%d", wc->num, x + 1); + wc->chans[x].sigcap = ZT_SIG_EM | ZT_SIG_CLEAR | + ZT_SIG_FXSLS | ZT_SIG_FXSGS | + ZT_SIG_FXSKS | ZT_SIG_FXOLS | + ZT_SIG_FXOGS | ZT_SIG_FXOKS; + wc->chans[x].pvt = wc; + wc->chans[x].chanpos = x + 1; + } + if (zt_register(&wc->span, 0)) { + printk("Unable to register span with zaptel\n"); + return -1; + } + return 0; +} + static inline void handle_leds(struct t1xxp *wc) { if (wc->alarm) { #ifdef FANCY_ALARM - if (wc->alarmtimer == wc->alarmthreshold << 1) { - wc->ledtestreg = wc->ledtestreg & ~BIT_ALARM; + if (wc->alarmtimer == (altab[wc->alarmpos] >> 1)) { + wc->ledtestreg = wc->ledtestreg | BIT_ALARM; control_set_reg(wc, WC_LEDTEST, wc->ledtestreg); } - if (wc->alarmtimer == 0x1f) { - wc->ledtestreg = wc->ledtestreg | BIT_ALARM; + if (wc->alarmtimer == 0xf) { + wc->ledtestreg = wc->ledtestreg & ~BIT_ALARM; control_set_reg(wc, WC_LEDTEST, wc->ledtestreg); wc->alarmtimer = -1; - wc->alarmthreshold = (wc->alarmthreshold + 1 & 0x1f); + wc->alarmpos++; + if (wc->alarmpos >= (sizeof(altab) / sizeof(altab[0]))) + wc->alarmpos = 0; } wc->alarmtimer++; #else @@ -235,10 +564,12 @@ static void t1xxp_transmitprep(struct t1xxp *wc, int ints) /* Just finished sending second buffer, fill it now */ txbuf = wc->writechunk + 32 * ZT_CHUNKSIZE; } + zt_transmit(&wc->span); for (y=0;yoffset) & 0x1f)] = + wc->chans[x].writechunk[y]; } } } @@ -246,47 +577,78 @@ static void t1xxp_transmitprep(struct t1xxp *wc, int ints) static void t1xxp_receiveprep(struct t1xxp *wc, int ints) { volatile unsigned char *rxbuf; - static int looper=0; int x; - unsigned char tmpbuf[32]; + int y; if (ints & 0x08) { /* Just received first buffer */ rxbuf = wc->readchunk; } else { rxbuf = wc->readchunk + ZT_CHUNKSIZE * 32; } - for (x=0;x<24;x++) { - /* Must map received channels into appropriate data */ - tmpbuf[x] = rxbuf[(chanmap[x] + WC_OFFSET) & 0x1f]; + for (x=3;x<32;x+=4) { + if (rxbuf[(x + WC_OFFSET) & 0x1f] == 0x7f) { + if (wc->offset != (x-3)) { + wc->offset = x - 3; +#if 1 + printk("New offset: %d\n", wc->offset); +#endif + } + } } - if (!(looper++ % 1000)) { - memcpy(tmpbuf, rxbuf, 32); - for (x=0;x<24;x++) - printk("%d: %d\n", x, (int)tmpbuf[x]); + for (y=0;ychans[x].readchunk[y] = + rxbuf[32 * y + ((chanmap[x] + WC_OFFSET + wc->offset) & 0x1f)]; + } } - + zt_receive(&wc->span); } static void t1xxp_interrupt(int irq, void *dev_id, struct pt_regs *regs) { struct t1xxp *wc = dev_id; - unsigned char ints; - unsigned char b; + unsigned char ints, a,b, rxs; static int gotint = 0; + int x,y,i; ints = inb(wc->ioaddr + WC_INTSTAT); + if (!ints) + return; + outb(ints, wc->ioaddr + WC_INTSTAT); if (!gotint) { printk("Got interrupt: 0x%04x\n", ints); - gotint++; } + gotint++; if (ints & 0x0f) { t1xxp_transmitprep(wc, ints); t1xxp_receiveprep(wc, ints); } handle_leds(wc); - +#if 1 + if (!(gotint & 0xf0)) { + x = gotint & 0xf; + if (x < 3) { + a = t1_get_reg(wc, 0x60 + x); + b = t1_get_reg(wc, 0x64 + x); + for (y=0;y<8;y++) { + i = x * 8 + y; + rxs = 0; + if (a & (1 << y)) + rxs |= ZT_ABIT; + if (b & (1 << y)) + rxs |= ZT_BBIT; + if (!(wc->chans[i].sig & ZT_SIG_CLEAR)) { + if (wc->chans[i].rxsig != rxs) + zt_rbsbits(&wc->chans[i], rxs); + } + } + } + } +#endif if (ints & 0x10) printk("PCI Master abort\n"); @@ -296,30 +658,8 @@ static void t1xxp_interrupt(int irq, void *dev_id, struct pt_regs *regs) } -static int t1xxp_reset(struct t1xxp *wc) -{ - char c; - c = t1_get_reg(wc, 0x30); - /* Reset the Line Interface */ - t1_set_reg(wc, 0x30, c | 0x1); - - /* Wait 50ms for this to finish */ - mdelay(50); - - /* Clear it */ - t1_set_reg(wc, 0x30, c); - - /* Reset the elastic stores */ - t1_set_reg(wc, 0x30, c | 0x40); - mdelay(1); - t1_set_reg(wc, 0x30, c); - - return 0; -} - static int t1xxp_hardware_init(struct t1xxp *wc) { - int x; /* Hardware PCI stuff */ /* Reset chip and registers */ outb(DELAY | 0x0e, wc->ioaddr + WC_CNTL); @@ -354,83 +694,21 @@ static int t1xxp_hardware_init(struct t1xxp *wc) /* Second frame */ outl(wc->readdma + ZT_CHUNKSIZE * 32 * 2 - 4, wc->ioaddr + WC_DMARE); /* End */ - printk("Setting up DMA (write/read = %08lx/%08lx)\n", wc->writedma, wc->readdma); - - /* Clear interrupts */ - outb(0xff, wc->ioaddr + WC_INTSTAT); + printk("Setting up DMA (write/read = %08x/%08x)\n", wc->writedma, wc->readdma); /* Check out the controller */ printk("Controller version: %02x\n", control_get_reg(wc, WC_VERSION)); control_set_reg(wc, WC_LEDTEST, 0x00); control_set_reg(wc, WC_CLOCK, 0x00); + + /* Reset the T1 and report */ + t1xxp_framer_hard_reset(wc); /* Pretend we're in alarm */ - wc->alarm = 1; + start_alarm(wc); return 0; -#if 0 - /* Reset all registers on the 2151 */ - for (x=0x20;x<0x40;x++) - t1_set_reg(wc, x, 0); - for (x=0x60;x<0x80;x++) - t1_set_reg(wc, x, 0); - /* Sanity check the T1 */ - if (t1_get_reg(wc, 0x7c) != 0) { - printk("Unable to talk to T1 part\n"); - return -ENOSYS; - } - /* Enable elastic store for transmit and receive, - 2.048 Mhz bus */ - t1_set_reg(wc, 0x37, 0x8c | 0x01); - - /* TSync is an output */ - t1_set_reg(wc, 0x36, 0x04); - - /* RSync is an input (Elastic Store) */ - t1_set_reg(wc, 0x2c, 0x08); - - /* ESF/B8ZS */ - t1_set_reg(wc, 0x38, 0xcc); - { - /* XXX Take me out XXX */ - t1_set_reg(wc, 0x2d, 0xff); - t1_set_reg(wc, 0x2e, 0xff); - t1_set_reg(wc, 0x2f, 0xff); - } - return t1xxp_reset(wc); -#endif } -static void t1xxp_enable_interrupts(struct t1xxp *wc) -{ - /* Enable interrupts (we care about all of them) */ - outb(0x3f, wc->ioaddr + WC_MASK0); - /* No external interrupts */ - outb(0x00, wc->ioaddr + WC_MASK1); -} - -static void t1xxp_start_dma(struct t1xxp *wc) -{ - /* Reset Master and TDM */ - outb(DELAY | 0x0f, wc->ioaddr + WC_CNTL); - set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(1); - outb(DELAY | 0x01, wc->ioaddr + WC_CNTL); - outb(0x01, wc->ioaddr + WC_OPER); - printk("Started DMA\n"); -} - -static void t1xxp_stop_dma(struct t1xxp *wc) -{ - outb(0x00, wc->ioaddr + WC_OPER); -} - -static void t1xxp_disable_interrupts(struct t1xxp *wc) -{ - outb(0x00, wc->ioaddr + WC_MASK0); - outb(0x00, wc->ioaddr + WC_MASK1); -} - - static int __devinit t1xxp_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) { int res; @@ -474,14 +752,11 @@ static int __devinit t1xxp_init_one(struct pci_dev *pdev, const struct pci_devic kfree(wc); return -EIO; } - - + /* Initialize hardware */ t1xxp_hardware_init(wc); - /* Enable interrupts */ - t1xxp_enable_interrupts(wc); - /* Start DMA */ - t1xxp_start_dma(wc); + /* Misc. software stuff */ + t1xxp_software_init(wc); printk("Found a Wildcard: %s\n", wc->variety); res = 0; diff --git a/wctdm.c b/wctdm.c new file mode 100755 index 0000000..88e39ff --- /dev/null +++ b/wctdm.c @@ -0,0 +1,834 @@ +/* + * Wilcard S100P FXS Interface Driver for Zapata Telephony interface + * + * Written by Mark Spencer + * Matthew Fredrickson + * + * Copyright (C) 2001, Linux Support Services, Inc. + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "proslic.h" + +static alpha indirect_regs[] = +{ +{0,"DTMF_ROW_0_PEAK",0x55C2}, +{1,"DTMF_ROW_1_PEAK",0x51E6}, +{2,"DTMF_ROW2_PEAK",0x4B85}, +{3,"DTMF_ROW3_PEAK",0x4937}, +{4,"DTMF_COL1_PEAK",0x3333}, +{5,"DTMF_FWD_TWIST",0x0202}, +{6,"DTMF_RVS_TWIST",0x0202}, +{7,"DTMF_ROW_RATIO_TRES",0x0198}, +{8,"DTMF_COL_RATIO_TRES",0x0198}, +{9,"DTMF_ROW_2ND_ARM",0x0611}, +{10,"DTMF_COL_2ND_ARM",0x0202}, +{11,"DTMF_PWR_MIN_TRES",0x00E5}, +{12,"DTMF_OT_LIM_TRES",0x0A1C}, +{13,"OSC1_COEF",0x6D40}, +{14,"OSC1X",0x0470}, +{15,"OSC1Y",0x0000}, +{16,"OSC2_COEF",0x4A80}, +{17,"OSC2X",0x0830}, +{18,"OSC2Y",0x0000}, +{19,"RING_V_OFF",0x0000}, +{20,"RING_OSC",0x7EF0}, +{21,"RING_X",0x0160}, +{22,"RING_Y",0x0000}, +{23,"PULSE_ENVEL",0x2000}, +{24,"PULSE_X",0x2000}, +{25,"PULSE_Y",0x0000}, +//{26,"RECV_DIGITAL_GAIN",0x4000}, // playback volume set lower +{26,"RECV_DIGITAL_GAIN",0x2000}, // playback volume set lower +{27,"XMIT_DIGITAL_GAIN",0x4000}, +{28,"LOOP_CLOSE_TRES",0x1000}, +{29,"RING_TRIP_TRES",0x3600}, +{30,"COMMON_MIN_TRES",0x1000}, +{31,"COMMON_MAX_TRES",0x0200}, +{32,"PWR_ALARM_Q1Q2",0x0550}, +{33,"PWR_ALARM_Q3Q4",0x2600}, +{34,"PWR_ALARM_Q5Q6",0x1B80}, +{35,"LOOP_CLOSURE_FILTER",0x8000}, +{36,"RING_TRIP_FILTER",0x0320}, +{37,"TERM_LP_POLE_Q1Q2",0x0100}, +{38,"TERM_LP_POLE_Q3Q4",0x0100}, +{39,"TERM_LP_POLE_Q5Q6",0x0010}, +{40,"CM_BIAS_RINGING",0x0C00}, +{41,"DCDC_MIN_V",0x0C00}, +{42,"DCDC_XTRA",0x1000}, +}; + +#ifdef STANDALONE_ZAPATA +#include "zaptel.h" +#else +#include +#endif + +#define WC_MAX_IFACES 128 + +#define WC_CNTL 0x00 +#define WC_OPER 0x01 +#define WC_AUXC 0x02 +#define WC_AUXD 0x03 +#define WC_MASK0 0x04 +#define WC_MASK1 0x05 +#define WC_INTSTAT 0x06 +#define WC_AUXR 0x07 + +#define WC_DMAWS 0x08 +#define WC_DMAWI 0x0c +#define WC_DMAWE 0x10 +#define WC_DMARS 0x18 +#define WC_DMARI 0x1c +#define WC_DMARE 0x20 + +#define WC_AUXFUNC 0x2b +#define WC_SERCTL 0x2d +#define WC_FSCDELAY 0x2f + +#define BIT_CS (1 << 2) +#define BIT_SCLK (1 << 3) +#define BIT_SDI (1 << 4) +#define BIT_SDO (1 << 5) + +#define FLAG_EMPTY 0 +#define FLAG_WRITE 1 +#define FLAG_READ 2 + +#define RING_DEBOUNCE 64 /* Ringer Debounce (in ms) */ +#define BATT_DEBOUNCE 8 /* Battery debounce (in ms) */ + +#define FLAG_DOUBLE_CLOCK (1 << 0) + +struct wcfxs { + struct pci_dev *dev; + char *variety; + struct zt_span span; + struct zt_chan chan; + unsigned char ios; + int usecount; + int intcount; + int dead; + int pos; + int flags; + int freeregion; + int alt; + + /* Receive hook state and debouncing */ + int oldrxhook; + int debouncehook; + int lastrxhook; + int debounce; + + int idletxhookstate; /* IDLE changing hook state */ + unsigned long ioaddr; + dma_addr_t readdma; + dma_addr_t writedma; + volatile int *writechunk; /* Double-word aligned write memory */ + volatile int *readchunk; /* Double-word aligned read memory */ +}; + + +struct wcfxs_desc { + char *name; + int flags; +}; + +static struct wcfxs_desc wcfxs = { "Wildcard Prototype", 0 }; + +static struct wcfxs *ifaces[WC_MAX_IFACES]; + +static void wcfxs_release(struct wcfxs *wc); + +static int debug = 0; + +static inline void wcfxs_transmitprep(struct wcfxs *wc, unsigned char ints) +{ + volatile unsigned int *writechunk; + int x; + if (ints & 0x01) + /* Write is at interrupt address. Start writing from normal offset */ + writechunk = wc->writechunk; + else + writechunk = wc->writechunk + ZT_CHUNKSIZE; + /* Calculate Transmission */ + zt_transmit(&wc->span); + + for (x=0;xchan.writechunk[x] << 24; + + } + +} + +static inline void wcfxs_receiveprep(struct wcfxs *wc, unsigned char ints) +{ + volatile unsigned int *readchunk; + int x; + + if (ints & 0x08) + /* Read is at interrupt address. Valid data is available at normal offset */ + readchunk = wc->readchunk; + else + readchunk = wc->readchunk + ZT_CHUNKSIZE; + for (x=0;xchan.readchunk[x] = (readchunk[x] >> 24) & 0xff; + } + + zt_receive(&wc->span); +} + +static inline void wcfxs_check_hook(struct wcfxs *wc); + +static void wcfxs_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct wcfxs *wc = dev_id; + unsigned char ints; + + ints = inb(wc->ioaddr + WC_INTSTAT); + outb(ints, wc->ioaddr + WC_INTSTAT); + + + if (!ints) + return; + if (ints & 0x10) { + printk("PCI Master abort\n"); + return; + } + + if (ints & 0x20) { + printk("PCI Target abort\n"); + return; + } + if (ints & 0x0f) { + wc->intcount++; + if (!(wc->intcount % 10)) + wcfxs_check_hook(wc); + wcfxs_transmitprep(wc, ints); + wcfxs_receiveprep(wc, ints); + } + +} + +static inline void write_8bits(struct wcfxs *wc, unsigned char bits) +{ + /* Drop chip select */ + int x; + wc->ios &= ~BIT_CS; + outb(wc->ios, wc->ioaddr + WC_AUXD); + for (x=0;x<8;x++) { + /* Send out each bit, MSB first, drop SCLK as we do so */ + if (bits & 0x80) + wc->ios |= BIT_SDI; + else + wc->ios &= ~BIT_SDI; + wc->ios &= ~BIT_SCLK; + outb(wc->ios, wc->ioaddr + WC_AUXD); + /* Now raise SCLK high again and repeat */ + wc->ios |= BIT_SCLK; + outb(wc->ios, wc->ioaddr + WC_AUXD); + bits <<= 1; + } + /* Finally raise CS back high again */ + wc->ios |= BIT_CS; + outb(wc->ios, wc->ioaddr + WC_AUXD); + outb(wc->ios, wc->ioaddr + WC_AUXD); + outb(wc->ios, wc->ioaddr + WC_AUXD); + outb(wc->ios, wc->ioaddr + WC_AUXD); + +} +static inline unsigned char read_8bits(struct wcfxs *wc) +{ + unsigned char res=0, c; + int x; + /* Drop chip select */ + wc->ios &= ~BIT_CS; + outb(wc->ios, wc->ioaddr + WC_AUXD); + for (x=0;x<8;x++) { + res <<= 1; + /* Get SCLK */ + wc->ios &= ~BIT_SCLK; + outb(wc->ios, wc->ioaddr + WC_AUXD); + /* Now raise SCLK high again */ + wc->ios |= BIT_SCLK; + outb(wc->ios, wc->ioaddr + WC_AUXD); + + /* Read back the value */ + c = inb(wc->ioaddr + WC_AUXR); + if (c & BIT_SDO) + res |= 1; + } + /* Finally raise CS back high again */ + wc->ios |= BIT_CS; + outb(wc->ios, wc->ioaddr + WC_AUXD); + outb(wc->ios, wc->ioaddr + WC_AUXD); + outb(wc->ios, wc->ioaddr + WC_AUXD); + outb(wc->ios, wc->ioaddr + WC_AUXD); + + /* And return our result */ + return res; +} + +static void wcfxs_setreg(struct wcfxs *wc, unsigned char reg, unsigned char value) +{ + write_8bits(wc, reg & 0x7f); + write_8bits(wc, value); +} + +static unsigned char wcfxs_getreg(struct wcfxs *wc, unsigned char reg) +{ + write_8bits(wc, reg | 0x80); + return read_8bits(wc); +} + +static int wait_access(struct wcfxs *wc) +{ + unsigned char count, data; + count = 0; + #define MAX 60 + + + /* Wait for indirect access */ + while (count++ < MAX) + { + data = wcfxs_getreg(wc, I_STATUS); + + if (!data) + return 0; + + } + + if(count > (MAX-1)) printk(" ##### Loop error #####\n"); + + return -1; +} + +static int wcfxs_setreg_indirect(struct wcfxs *wc, unsigned char address, unsigned short data) +{ + + if(!wait_access(wc)) + { + wcfxs_setreg(wc, IDA_LO,(unsigned char)(data & 0xFF)); + wcfxs_setreg(wc, IDA_HI,(unsigned char)((data & 0xFF00)>>8)); + wcfxs_setreg(wc, IAA,address); + return 0; + } + + return -1; +} + +static int wcfxs_getreg_indirect(struct wcfxs *wc, unsigned char address) +{ + if (!wait_access(wc)) { + wcfxs_setreg(wc, IAA, address); + if (!wait_access(wc)) { + unsigned char data1, data2; + data1 = wcfxs_getreg(wc, IDA_LO); + data2 = wcfxs_getreg(wc, IDA_HI); + return data1 | (data2 << 8); + } else + printk("Failed to wait inside\n"); + } else + printk("failed to wait\n"); + + return -1; +} + +static int wcfxs_init_indirect_regs(struct wcfxs *wc) +{ + unsigned char i; + + for (i=0; i<43; i++) + { + if(wcfxs_setreg_indirect(wc, i,indirect_regs[i].initial)) + return -1; + } + + return 0; +} + +static int wcfxs_verify_indirect_regs(struct wcfxs *wc) +{ + int passed = 1; + unsigned short i, initial; + int j; + + for (i=0; i<43; i++) + { + if((j = wcfxs_getreg_indirect(wc, (unsigned char) i)) < 0) { + printk("Failed to read indirect register %d\n", i); + return -1; + } + initial= indirect_regs[i].initial; + + if ( j != initial ) + { + printk("!!!!!!! %s iREG %X = %X should be %X\n", + indirect_regs[i].name,i,j,initial ); + passed = 0; + } + } + + if (passed) { + if (debug) + printk("Init Indirect Registers completed successfully.\n"); + } else { + printk(" !!!!! Init Indirect Registers UNSUCCESSFULLY.\n"); + return -1; + } + return 0; +} + +static int wcfxs_calibrate(struct wcfxs *wc) +{ + unsigned char x; + + wcfxs_setreg(wc, 92, 0xc8); + wcfxs_setreg(wc, 97, 0); + wcfxs_setreg(wc, 93, 0x19); + wcfxs_setreg(wc, 14, 0); + wcfxs_setreg(wc, 93, 0x99); + + x = wcfxs_getreg(wc, 93); + if (debug) + printk("DC Cal x=%x\n",x); + wcfxs_setreg(wc, 97, 0); + wcfxs_setreg(wc, CALIBR1, CALIBRATE_LINE); + x = wcfxs_getreg(wc, CALIBR1); + wcfxs_setreg(wc, LINE_STATE, ACTIVATE_LINE); + + return 0; +} + +static int wcfxs_init_proslic(struct wcfxs *wc) +{ + int blah; + + /* By default, always send on hook */ + wc->idletxhookstate = 2; + + /* Disable Auto Power Alarm Detect and other "features" */ + wcfxs_setreg(wc, 67, 0x0e); + blah = wcfxs_getreg(wc, 67); + + if (wcfxs_init_indirect_regs(wc)) { + printk(KERN_INFO "Indirect Registers failed to initialize.\n"); + return -1; + } + if (wcfxs_verify_indirect_regs(wc)) { + printk(KERN_INFO "Indirect Registers failed verification.\n"); + return -1; + } + if (wcfxs_calibrate(wc)) { + printk(KERN_INFO "ProSlic Died on Activation.\n"); + return -1; + } + if (wcfxs_setreg_indirect(wc, 97, 0x0)) { // Stanley: for the bad recording fix + printk(KERN_INFO "ProSlic IndirectReg Died.\n"); + return -1; + } + wcfxs_setreg(wc, 1, 0x2a); + // U-Law GCI 8-bit interface + wcfxs_setreg(wc, 2, 0); // Tx Start count low byte 0 + wcfxs_setreg(wc, 3, 0); // Tx Start count high byte 0 + wcfxs_setreg(wc, 4, 0); // Rx Start count low byte 0 + wcfxs_setreg(wc, 5, 0); // Rx Start count high byte 0 + wcfxs_setreg(wc, 8, 0x0); // disable loopback + wcfxs_setreg(wc, 18, 0xff); // clear all interrupt + wcfxs_setreg(wc, 19, 0xff); + wcfxs_setreg(wc, 20, 0xff); + wcfxs_setreg(wc, 21, 0x00); // enable interrupt + wcfxs_setreg(wc, 22, 0x02); // Loop detection interrupt + wcfxs_setreg(wc, 23, 0x01); // DTMF detection interrupt + wcfxs_setreg(wc, 72, 0x20); +#ifdef BOOST_RINGER + /* Beef up Ringing voltage to 89V */ + if (wcfxs_setreg_indirect(wc, 23, 0x1d1)) + return -1; +#endif + return 0; +} + +static inline void wcfxs_check_hook(struct wcfxs *wc) +{ + char res; + int hook; + + /* For some reason we have to debounce the + hook detector. */ + + res = wcfxs_getreg(wc, 68); + hook = (res & 1); + if (hook != wc->lastrxhook) { + /* Reset the debounce */ + wc->debounce = 3; + } else { + if (wc->debounce > -1) + wc->debounce--; + } + wc->lastrxhook = hook; + if (!wc->debounce) + wc->debouncehook = hook; + + if (!wc->oldrxhook && wc->debouncehook) { + /* Off hook */ + if (debug) + printk("wcfxs: Going off hook\n"); + zt_hooksig(&wc->chan, ZT_RXSIG_OFFHOOK); + wc->oldrxhook = 1; + + } else if (wc->oldrxhook && !wc->debouncehook) { + /* On hook */ + if (debug) + printk("wcfxs: Going on hook\n"); + zt_hooksig(&wc->chan, ZT_RXSIG_ONHOOK); + wc->oldrxhook = 0; + } + +} + +static int wcfxs_open(struct zt_chan *chan) +{ + struct wcfxs *wc = chan->pvt; + if (wc->dead) + return -ENODEV; + wc->usecount++; + MOD_INC_USE_COUNT; + return 0; +} + +static int wcfxs_close(struct zt_chan *chan) +{ + struct wcfxs *wc = chan->pvt; + wc->usecount--; + MOD_DEC_USE_COUNT; + /* If we're dead, release us now */ + if (!wc->usecount && wc->dead) + wcfxs_release(wc); + return 0; +} + +static int wcfxs_hooksig(struct zt_chan *chan, zt_txsig_t txsig) +{ + struct wcfxs *wc = chan->pvt; + int reg=0; + unsigned char txhook = 0; + switch(txsig) { + case ZT_TXSIG_ONHOOK: + switch(chan->sig) { + case ZT_SIG_FXOKS: + case ZT_SIG_FXOLS: + txhook = wc->idletxhookstate; + break; + case ZT_SIG_FXOGS: + txhook = 3; + break; + } + break; + case ZT_TXSIG_OFFHOOK: + txhook = wc->idletxhookstate; + break; + case ZT_TXSIG_START: + txhook = 4; + break; + case ZT_TXSIG_KEWL: + txhook = 0; + break; + default: + printk("wcfxs: Can't set tx state to %d\n", txsig); + } + if (debug) + printk("Setting hook state to %d (%02x)\n", txsig, reg); + + wcfxs_setreg(wc, 64, txhook); + return 0; +} + +static int wcfxs_initialize(struct wcfxs *wc) +{ + /* Zapata stuff */ + sprintf(wc->span.name, "WCFXS/%d", wc->pos); + sprintf(wc->span.desc, "%s Board %d\n", wc->variety, wc->pos + 1); + wc->span.deflaw = ZT_LAW_MULAW; + sprintf(wc->chan.name, "WCFXS/%d/%d", wc->pos, 0); + wc->chan.sigcap = ZT_SIG_FXOKS | ZT_SIG_FXOLS | ZT_SIG_FXOGS; + wc->chan.chanpos = 1; + wc->span.chans = &wc->chan; + wc->span.channels = 1; + wc->span.hooksig = wcfxs_hooksig; + wc->span.open = wcfxs_open; + wc->span.close = wcfxs_close; + wc->span.flags = ZT_FLAG_RBS; + init_waitqueue_head(&wc->span.maintq); + + wc->span.pvt = wc; + wc->chan.pvt = wc; + if (zt_register(&wc->span, 0)) { + printk("Unable to register span with zaptel\n"); + return -1; + } + return 0; +} + +static int wcfxs_hardware_init(struct wcfxs *wc) +{ + /* Hardware stuff */ + long oldjiffies; + /* Reset PCI Interface chip and registers (and serial) */ + outb(0x0e, wc->ioaddr + WC_CNTL); + /* Setup our proper outputs for when we switch for our "serial" port */ + wc->ios = BIT_CS | BIT_SCLK | BIT_SDI; + + outb(wc->ios, wc->ioaddr + WC_AUXD); + + /* Set all to outputs except AUX 5 and 0, which are inputs */ + outb(0xde, wc->ioaddr + WC_AUXC); + + /* Wait a sec */ + oldjiffies = jiffies; + while(jiffies - oldjiffies < 2); + /* Back to normal, with automatic DMA wrap around */ + outb(0x30 | 0x01, wc->ioaddr + WC_CNTL); + + /* Make sure serial port and DMA are out of reset */ + outb(inb(wc->ioaddr + WC_CNTL) & 0xf9, WC_CNTL); + + /* Configure serial port for MSB->LSB operation */ + if (wc->flags & FLAG_DOUBLE_CLOCK) + outb(0xc1, wc->ioaddr + WC_SERCTL); + else + outb(0xc0, wc->ioaddr + WC_SERCTL); + + /* Delay FSC by 0 so it's properly aligned */ + outb(0x0, wc->ioaddr + WC_FSCDELAY); + + /* Setup DMA Addresses */ + outl(wc->writedma, wc->ioaddr + WC_DMAWS); /* Write start */ + outl(wc->writedma + ZT_CHUNKSIZE * 4, wc->ioaddr + WC_DMAWI); /* Middle (interrupt) */ + outl(wc->writedma + ZT_CHUNKSIZE * 8 - 4, wc->ioaddr + WC_DMAWE); /* End */ + + outl(wc->readdma, wc->ioaddr + WC_DMARS); /* Read start */ + outl(wc->readdma + ZT_CHUNKSIZE * 4, wc->ioaddr + WC_DMARI); /* Middle (interrupt) */ + outl(wc->readdma + ZT_CHUNKSIZE * 8 - 4, wc->ioaddr + WC_DMARE); /* End */ + + /* Clear interrupts */ + outb(0xff, wc->ioaddr + WC_INTSTAT); + return wcfxs_init_proslic(wc); +} + +static void wcfxs_enable_interrupts(struct wcfxs *wc) +{ + /* Enable interrupts (we care about all of them) */ + outb(0x3f, wc->ioaddr + WC_MASK0); + /* No external interrupts */ + outb(0x00, wc->ioaddr + WC_MASK1); +} + +static void wcfxs_start_dma(struct wcfxs *wc) +{ + /* Reset Master and TDM */ + outb(0x0f, wc->ioaddr + WC_CNTL); + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(1); + outb(0x01, wc->ioaddr + WC_CNTL); + outb(0x01, wc->ioaddr + WC_OPER); +} + +static void wcfxs_stop_dma(struct wcfxs *wc) +{ + outb(0x00, wc->ioaddr + WC_OPER); +} + +static void wcfxs_disable_interrupts(struct wcfxs *wc) +{ + outb(0x00, wc->ioaddr + WC_MASK0); + outb(0x00, wc->ioaddr + WC_MASK1); +} + +static int __devinit wcfxs_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) +{ + int res; + struct wcfxs *wc; + struct wcfxs_desc *d = (struct wcfxs_desc *)ent->driver_data; + int x; + static int initd_ifaces=0; + + if(initd_ifaces){ + memset((void *)ifaces,0,(sizeof(struct wcfxs *))*WC_MAX_IFACES); + initd_ifaces=1; + } + for (x=0;x= WC_MAX_IFACES) { + printk("Too many interfaces\n"); + return -EIO; + } + + if (pci_enable_device(pdev)) { + res = -EIO; + } else { + wc = kmalloc(sizeof(struct wcfxs), GFP_KERNEL); + if (wc) { + ifaces[x] = wc; + memset(wc, 0, sizeof(struct wcfxs)); + wc->ioaddr = pci_resource_start(pdev, 0); + wc->dev = pdev; + wc->pos = x; + wc->variety = d->name; + wc->flags = d->flags; + /* Keep track of whether we need to free the region */ + if (request_region(wc->ioaddr, 0xff, "wcfxs")) + wc->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 */ + wc->writechunk = (int *)pci_alloc_consistent(pdev, ZT_MAX_CHUNKSIZE * 2 * 2 * 2 * 4, &wc->writedma); + if (!wc->writechunk) { + printk("wcfxs: Unable to allocate DMA-able memory\n"); + if (wc->freeregion) + release_region(wc->ioaddr, 0xff); + return -ENOMEM; + } + + wc->readchunk = wc->writechunk + ZT_MAX_CHUNKSIZE * 2; /* in doublewords */ + wc->readdma = wc->writedma + ZT_MAX_CHUNKSIZE * 8; /* in bytes */ + + if (wcfxs_initialize(wc)) { + printk("wcfxs: Unable to intialize FXS\n"); + if (wc->freeregion) + release_region(wc->ioaddr, 0xff); + kfree(wc); + return -EIO; + } + + /* Enable bus mastering */ + pci_set_master(pdev); + + /* Keep track of which device we are */ + pci_set_drvdata(pdev, wc); + + if (request_irq(pdev->irq, wcfxs_interrupt, SA_SHIRQ, "wcfxs", wc)) { + printk("wcfxs: Unable to request IRQ %d\n", pdev->irq); + if (wc->freeregion) + release_region(wc->ioaddr, 0xff); + kfree(wc); + return -EIO; + } + + + if (wcfxs_hardware_init(wc)) { + zt_unregister(&wc->span); + if (wc->freeregion) + release_region(wc->ioaddr, 0xff); + kfree(wc); + return -EIO; + } + /* Enable interrupts */ + wcfxs_enable_interrupts(wc); + /* Initialize Write/Buffers to all blank data */ + memset((void *)wc->writechunk,0,ZT_MAX_CHUNKSIZE * 2 * 2 * 4); + + /* Start DMA */ + wcfxs_start_dma(wc); + + printk("Found a Wildcard FXS: %s\n", wc->variety); + res = 0; + } else + res = -ENOMEM; + } + return res; +} + +static void wcfxs_release(struct wcfxs *wc) +{ + zt_unregister(&wc->span); + if (wc->freeregion) + release_region(wc->ioaddr, 0xff); + kfree(wc); + printk("Freed a Wildcard\n"); +} + +static void __devexit wcfxs_remove_one(struct pci_dev *pdev) +{ + struct wcfxs *wc = pci_get_drvdata(pdev); + if (wc) { + + /* Stop any DMA */ + wcfxs_stop_dma(wc); + + /* In case hardware is still there */ + wcfxs_disable_interrupts(wc); + + /* Immediately free resources */ + pci_free_consistent(pdev, ZT_MAX_CHUNKSIZE * 2 * 2 * 2 * 4, (void *)wc->writechunk, wc->writedma); + free_irq(pdev->irq, wc); + + /* Reset PCI chip and registers */ + outb(0x0e, wc->ioaddr + WC_CNTL); + + /* Release span, possibly delayed */ + if (!wc->usecount) + wcfxs_release(wc); + else + wc->dead = 1; + } +} + +static struct pci_device_id wcfxs_pci_tbl[] __devinitdata = { + { 0xe159, 0x0001, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) &wcfxs }, +}; + +static struct pci_driver wcfxs_driver = { + name: "wcfxs", + probe: wcfxs_init_one, + remove: wcfxs_remove_one, + suspend: NULL, + resume: NULL, + id_table: wcfxs_pci_tbl, +}; + +static int __init wcfxs_init(void) +{ + int res; + res = pci_module_init(&wcfxs_driver); + if (res) + return -ENODEV; + return 0; +} + +static void __exit wcfxs_cleanup(void) +{ + pci_unregister_driver(&wcfxs_driver); +} + +MODULE_PARM(debug, "i"); +MODULE_DESCRIPTION("Wildcard S100P Zaptel Driver"); +MODULE_AUTHOR("Mark Spencer "); + +module_init(wcfxs_init); +module_exit(wcfxs_cleanup); -- cgit v1.2.3