📄 wct1xxp.c
字号:
/* * Linux Support Services, Inc. Wildcard T100P T1/PRI card Driver * * Written by Mark Spencer <markster@linux-support.net> * Matthew Fredrickson <creslin@linux-support.net> * William Meadows <wmeadows@linux-support.net> * * 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. * * $Id: wct1xxp.c,v 1.19.2.3 2005/01/17 01:58:09 russell Exp $ */#include <linux/kernel.h>#include <linux/errno.h>#include <linux/module.h>#include <linux/init.h>#include <linux/usb.h>#include <linux/errno.h>#include <linux/pci.h>#include <linux/spinlock.h>#ifdef STANDALONE_ZAPATA#include "zaptel.h"#else#include <linux/zaptel.h>#endif#ifdef LINUX26#include <linux/moduleparam.h>#endif#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#define WC_AUXC 0x02#define WC_AUXD 0x03#define WC_MASK0 0x04#define WC_MASK1 0x05#define WC_INTSTAT 0x06#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_CURPOS 0x24#define WC_SERC 0x2d#define WC_FSCDELAY 0x2f#define WC_USERREG 0xc0#define WC_CLOCK 0x0#define WC_LEDTEST 0x1#define WC_VERSION 0x2/* Offset between transmit and receive */#define WC_OFFSET 4#define BIT_CS (1 << 7)#define BIT_ADDR (0xf << 3)#define BIT_LED0 (1 << 0)#define BIT_LED1 (1 << 1)#define BIT_TEST (1 << 2)static char *chips[] ={ "DS2152", "DS21352", "DS21552", "Unknown Chip (3)", "DS2154", "DS21354", "DS21554", "Unknown Chip (7)",};static int chanmap_t1[] = { 2,1,0, 6,5,4, 10,9,8, 14,13,12, 18,17,16, 22,21,20, 26,25,24, 30,29,28 };static int chanmap_e1[] = { 2,1,0, 7,6,5,4, 11,10,9,8, 15,14,13,12, 19,18,17,16, 23,22,21,20, 27,26,25,24, 31,30,29,28 };#ifdef FANCY_ALARMstatic 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, };#endifstruct t1xxp { struct pci_dev *dev; spinlock_t lock; int ise1; int num; /* Our offset for finding channel 1 */ int offset; char *variety; int intcount; int usecount; int clocktimeout; int sync; int dead; int blinktimer; int alarmtimer; int loopupcnt; int loopdowncnt; int miss; int misslast; int *chanmap;#ifdef FANCY_ALARM int alarmpos;#endif unsigned char ledtestreg; unsigned char outbyte; unsigned long ioaddr; unsigned short canary; /* T1 signalling */ 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 */ volatile unsigned char *readchunk; /* Double-word aligned read memory */ unsigned char ec_chunk1[31][ZT_CHUNKSIZE]; unsigned char ec_chunk2[31][ZT_CHUNKSIZE]; unsigned char tempo[32]; struct zt_span span; /* Span */ struct zt_chan chans[31]; /* Channels */};#define CANARY 0xca1eint debug = 0; /* doesnt do anything */static struct t1xxp *cards[WC_MAX_CARDS];static inline void start_alarm(struct t1xxp *wc){#ifdef FANCY_ALARM wc->alarmpos = 0;#endif wc->blinktimer = 0;}static inline void stop_alarm(struct t1xxp *wc){#ifdef FANCY_ALARM wc->alarmpos = 0;#endif wc->blinktimer = 0;}static inline void __select_framer(struct t1xxp *wc, int reg){ /* Top four bits of address from AUX 6-3 */ wc->outbyte &= ~BIT_CS; wc->outbyte &= ~BIT_ADDR; wc->outbyte |= (reg & 0xf0) >> 1; outb(wc->outbyte, wc->ioaddr + WC_AUXD);}static inline void __select_control(struct t1xxp *wc){ if (!(wc->outbyte & BIT_CS)) { wc->outbyte |= BIT_CS; outb(wc->outbyte, wc->ioaddr + WC_AUXD); }}static int t1xxp_open(struct zt_chan *chan){ struct t1xxp *wc = chan->pvt; if (wc->dead) return -ENODEV; wc->usecount++;#ifndef LINUX26 MOD_INC_USE_COUNT;#endif return 0;}static int __t1_get_reg(struct t1xxp *wc, int reg){ unsigned char res; __select_framer(wc, reg); /* Get value */ res = inb(wc->ioaddr + WC_USERREG + ((reg & 0xf) << 2)); return res;}static int __t1_set_reg(struct t1xxp *wc, int reg, unsigned char val){ __select_framer(wc, reg); /* Send address */ outb(val, wc->ioaddr + WC_USERREG + ((reg & 0xf) << 2)); return 0;}static int __control_set_reg(struct t1xxp *wc, int reg, unsigned char val){ __select_control(wc); outb(val, wc->ioaddr + WC_USERREG + ((reg & 0xf) << 2)); return 0;}static int control_set_reg(struct t1xxp *wc, int reg, unsigned char val){ unsigned long flags; int res; spin_lock_irqsave(&wc->lock, flags); res = __control_set_reg(wc, reg, val); spin_unlock_irqrestore(&wc->lock, flags); return res;}static int __control_get_reg(struct t1xxp *wc, int reg){ unsigned char res; /* The following makes UTTERLY no sense, but what was happening was that reads in some cases were not actually happening on the physical bus. Why, we dunno. But in debugging, we found that writing before reading (in this case to an unused position) seems to get rid of the problem */ __control_set_reg(wc,3,0x69); /* do magic here */ /* now get the read byte from the Xilinx part */ res = inb(wc->ioaddr + WC_USERREG + ((reg & 0xf) << 2)); return res;}static int control_get_reg(struct t1xxp *wc, int reg){ unsigned long flags; int res; spin_lock_irqsave(&wc->lock, flags); res = __control_get_reg(wc, reg); spin_unlock_irqrestore(&wc->lock, flags); return res;}static void t1xxp_release(struct t1xxp *wc){ zt_unregister(&wc->span); kfree(wc); printk("Freed a Wildcard\n");}static int t1xxp_close(struct zt_chan *chan){ struct t1xxp *wc = chan->pvt; wc->usecount--;#ifndef LINUX26 MOD_DEC_USE_COUNT;#endif /* If we're dead, release us now */ if (!wc->usecount && wc->dead) t1xxp_release(wc); 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(0x3c /* 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); if (debug) 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_set_clear(struct t1xxp *wc){ /* Setup registers */ int x,y; unsigned char b; /* No such thing under E1 */ if (wc->ise1) { printk("Can't set clear mode on an E1!\n"); return; } 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 void t1xxp_t1_framer_start(struct t1xxp *wc){ int i; char *coding, *framing; unsigned long endjiffies; int alreadyrunning = wc->span.flags & ZT_FLAG_RUNNING; unsigned long flags; spin_lock_irqsave(&wc->lock, flags); /* 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 (!alreadyrunning) { /* Setup the clear channels */ __t1xxp_set_clear(wc); /* Set LIRST bit to 1 */ __t1_set_reg(wc, 0x0a, 0x80); spin_unlock_irqrestore(&wc->lock, flags); /* Wait 100ms to give plenty of time for reset */ endjiffies = jiffies + 10; while(endjiffies < jiffies); spin_lock_irqsave(&wc->lock, flags); /* Reset LIRST bit and reset elastic stores */ __t1_set_reg(wc, 0xa, 0x30); wc->span.flags |= ZT_FLAG_RUNNING; } spin_unlock_irqrestore(&wc->lock, flags);}static void t1xxp_e1_framer_start(struct t1xxp *wc){ int i; char *coding, *framing; unsigned long endjiffies; int alreadyrunning = wc->span.flags & ZT_FLAG_RUNNING; unsigned long flags; char *crcing = ""; unsigned char ccr1, tcr1, tcr2; spin_lock_irqsave(&wc->lock, flags); /* Build up config */ ccr1 = 0; tcr1 = 8; tcr2 = 0; if (wc->span.lineconfig & ZT_CONFIG_CCS) { coding = "CCS"; /* Receive CCS */ ccr1 |= 8; } else { tcr1 |= 0x20; coding = "CAS"; } if (wc->span.lineconfig & ZT_CONFIG_HDB3) { ccr1 |= 0x44; /* TX/RX HDB3 */ framing = "HDB3"; } else { framing = "AMI"; } if (wc->span.lineconfig & ZT_CONFIG_CRC4) { ccr1 |= 0x11; tcr2 |= 0x02; crcing = " with CRC4"; } __t1_set_reg(wc, 0x12, tcr1); __t1_set_reg(wc, 0x13, tcr2); __t1_set_reg(wc, 0x14, ccr1); __t1_set_reg(wc, 0x18, 0x20); /* 120 Ohm */ #if 0 /* XXX Does LBO Matter? XXX */ /* Set outgoing LBO */ __t1_set_reg(wc, 0x7c, wc->span.txlevel << 5);#endif printk("Using %s/%s coding/framing%s 120 Ohms\n", coding, framing,crcing); if (!alreadyrunning) { __t1_set_reg(wc,0x1b,0x8a); /* CCR3: LIRST & TSCLKM */ __t1_set_reg(wc,0x20,0x1b); /* TAFR */ __t1_set_reg(wc,0x21,0x5f); /* TNAFR */ __t1_set_reg(wc,0x40,0xb); /* TSR1 */ for(i = 0x41; i <= 0x4f; i++) __t1_set_reg(wc,i,0x55); for(i = 0x22; i <= 0x25; i++) __t1_set_reg(wc,i,0xff); spin_unlock_irqrestore(&wc->lock, flags); /* Wait 100ms to give plenty of time for reset */ endjiffies = jiffies + 10; while(endjiffies < jiffies); spin_lock_irqsave(&wc->lock, flags);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -