📄 wct4xxp.c
字号:
/* * TE410P Quad-T1/E1 PCI Driver version 0.1, 12/16/02 * * Written by Mark Spencer <markster@linux-support.net> * Based on previous works, designs, and archetectures conceived and * written by Jim Dixon <jim@lambdatel.com>. * * Copyright (C) 2001 Jim Dixon / Zapata Telephony. * 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 <linux/kernel.h>#include <linux/errno.h>#include <linux/module.h>#include <linux/pci.h>#include <linux/init.h>#include <linux/sched.h>#include <linux/interrupt.h>#ifdef STANDALONE_ZAPATA#include "zaptel.h"#else#include <linux/zaptel.h>#endif#ifdef LINUX26#include <linux/moduleparam.h>#endif#include "wct4xxp.h"/* * Tasklets provide better system interactive response at the cost of the * possibility of losing a frame of data at very infrequent intervals. If * you are more concerned with the performance of your machine, enable the * tasklets. If you are strict about absolutely no drops, then do not enable * tasklets. *//* #define ENABLE_TASKLETS *//* Define to get more attention-grabbing but slightly more I/O using alarm status */#define FANCY_ALARMstatic int debug;static int timingcable;static int highestorder;static int t1e1override = -1;static int loopback = 0;static int alarmdebounce = 0;static int noburst = 0;#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, };#endif#define MAX_SPANS 16#define FLAG_STARTED (1 << 0)#define FLAG_NMF (1 << 1)#define FLAG_SENDINGYELLOW (1 << 2)#define TYPE_T1 1 /* is a T1 card */#define TYPE_E1 2 /* is an E1 card */#define CANARY 0xc0destatic int inirq = 0;struct t4 { /* This structure exists one per card */ struct pci_dev *dev; /* Pointer to PCI device */ int intcount; int num; /* Which card we are */ int t1e1; /* T1/E1 select pins */ int globalconfig; /* Whether global setup has been done */ int syncsrc; /* active sync source */ int syncs[4]; /* sync sources */ int psyncs[4]; /* span-relative sync sources */ int alarmtimer[4]; /* Alarm timer */ int redalarms[4]; int blinktimer; int alarmcount[4]; /* How much red alarm we've seen */#ifdef FANCY_ALARM int alarmpos;#endif int irq; /* IRQ used by device */ int order; /* Order */ int flags; /* Device flags */ int spanflags[4]; /* Span flags */ int syncpos[4]; /* span-relative sync sources */ int master; /* Are we master */ int ledreg; /* LED Register */ int e1check[4]; /* E1 check */ unsigned int dmactrl; int e1recover; /* E1 recovery timer */ dma_addr_t readdma; dma_addr_t writedma; unsigned long memaddr; /* Base address of card */ unsigned long memlen; volatile unsigned int *membase; /* Base address of card */ struct zt_span spans[4]; /* Spans */ struct zt_chan *chans[4]; /* Pointers to blocks of 24(30/31) contiguous zt_chans for each span */ unsigned char txsigs[4][16]; /* Copy of tx sig registers */ int loopupcnt[4]; /* loop up code counter */ int loopdowncnt[4]; /* loop down code counter */ int spansstarted; /* number of spans started */ /* spinlock_t lock; */ /* lock context */ spinlock_t reglock; /* lock register access */ unsigned char ec_chunk1[4][31][ZT_CHUNKSIZE]; /* first EC chunk buffer */ unsigned char ec_chunk2[4][31][ZT_CHUNKSIZE]; /* second EC chunk buffer */ volatile unsigned int *writechunk; /* Double-word aligned write memory */ volatile unsigned int *readchunk; /* Double-word aligned read memory */ unsigned short canary;#ifdef ENABLE_TASKLETS int taskletrun; int taskletsched; int taskletpending; int taskletexec; int txerrors; struct tasklet_struct t4xxp_tlet;#endif int spantype[4]; /* card type, T1 or E1 */ unsigned int passno; /* number of interrupt passes */ char *variety; int last0; /* for detecting double-missed IRQ */ int checktiming; /* Set >0 to cause the timing source to be checked */};static void __set_clear(struct t4 *wc, int span);static int t4_startup(struct zt_span *span);static int t4_shutdown(struct zt_span *span);static int t4_rbsbits(struct zt_chan *chan, int bits);static int t4_maint(struct zt_span *span, int cmd);static int t4_reset_dma(struct t4 *wc);static int t4_ioctl(struct zt_chan *chan, unsigned int cmd, unsigned long data);static void __t4_set_timing_source(struct t4 *wc, int unit);#define WC_RDADDR 0#define WC_WRADDR 1#define WC_COUNT 2#define WC_DMACTRL 3 #define WC_INTR 4/* #define WC_GPIO 5 */#define WC_VERSION 6#define WC_LEDS 7#define WC_ACTIVATE (1 << 12)#define WC_GPIOCTL 8#define WC_GPIO 9#define WC_LADDR 10#define WC_LDATA 11#define WC_LREAD (1 << 15)#define WC_LWRITE (1 << 16)#define WC_OFF (0)#define WC_RED (1)#define WC_GREEN (2)#define WC_YELLOW (3)#define MAX_T4_CARDS 64#ifdef ENABLE_TASKLETSstatic void t4_tasklet(unsigned long data);#endifstatic struct t4 *cards[MAX_T4_CARDS];static inline void __t4_pci_out(struct t4 *wc, const unsigned int addr, const unsigned int value){ unsigned int tmp; wc->membase[addr] = value;#if 1 tmp = wc->membase[addr]; if ((value != tmp) && (addr != WC_LEDS) && (addr != WC_LDATA) && (addr != WC_GPIO) && (addr != WC_INTR)) printk("Tried to load %08x into %08x, but got %08x instead\n", value, addr, tmp);#endif }static inline unsigned int __t4_pci_in(struct t4 *wc, const unsigned int addr){ return wc->membase[addr];}static inline void t4_pci_out(struct t4 *wc, const unsigned int addr, const unsigned int value){ unsigned long flags; spin_lock_irqsave(&wc->reglock, flags); __t4_pci_out(wc, addr, value); spin_unlock_irqrestore(&wc->reglock, flags);}static inline void __t4_set_led(struct t4 *wc, int span, int color){ int oldreg = wc->ledreg; wc->ledreg &= ~(0x3 << (span << 1)); wc->ledreg |= (color << (span << 1)); if (oldreg != wc->ledreg) __t4_pci_out(wc, WC_LEDS, wc->ledreg);}static inline void t4_activate(struct t4 *wc){ wc->ledreg |= WC_ACTIVATE; t4_pci_out(wc, WC_LEDS, wc->ledreg);}static inline unsigned int t4_pci_in(struct t4 *wc, const unsigned int addr){ unsigned int ret; unsigned long flags; spin_lock_irqsave(&wc->reglock, flags); ret = __t4_pci_in(wc, addr); spin_unlock_irqrestore(&wc->reglock, flags); return ret;}static inline unsigned int __t4_framer_in(struct t4 *wc, int unit, const unsigned int addr){ unsigned int ret; unit &= 0x3; __t4_pci_out(wc, WC_LADDR, (unit << 8) | (addr & 0xff)); __t4_pci_out(wc, WC_LADDR, (unit << 8) | (addr & 0xff) | ( 1 << 10) | WC_LREAD); ret = __t4_pci_in(wc, WC_LDATA); __t4_pci_out(wc, WC_LADDR, 0); return ret & 0xff;}static inline unsigned int t4_framer_in(struct t4 *wc, int unit, const unsigned int addr){ unsigned long flags; unsigned int ret; spin_lock_irqsave(&wc->reglock, flags); ret = __t4_framer_in(wc, unit, addr); spin_unlock_irqrestore(&wc->reglock, flags); return ret;}static inline void __t4_framer_out(struct t4 *wc, int unit, const unsigned int addr, const unsigned int value){ unit &= 0x3; if (debug) printk("Writing %02x to address %02x of unit %d\n", value, addr, unit); __t4_pci_out(wc, WC_LADDR, (unit << 8) | (addr & 0xff)); __t4_pci_out(wc, WC_LDATA, value); __t4_pci_out(wc, WC_LADDR, (unit << 8) | (addr & 0xff) | (1 << 10)); __t4_pci_out(wc, WC_LADDR, (unit << 8) | (addr & 0xff) | (1 << 10) | WC_LWRITE); __t4_pci_out(wc, WC_LADDR, (unit << 8) | (addr & 0xff) | (1 << 10)); __t4_pci_out(wc, WC_LADDR, (unit << 8) | (addr & 0xff)); __t4_pci_out(wc, WC_LADDR, 0); if (debug) printk("Write complete\n");#if 0 { unsigned int tmp; tmp = t4_framer_in(wc, unit, addr); if (tmp != value) { printk("Expected %d from unit %d register %d but got %d instead\n", value, unit, addr, tmp); } }#endif }static inline void t4_framer_out(struct t4 *wc, int unit, const unsigned int addr, const unsigned int value){ unsigned long flags; spin_lock_irqsave(&wc->reglock, flags); __t4_framer_out(wc, unit, addr, value); spin_unlock_irqrestore(&wc->reglock, flags);}static void __set_clear(struct t4 *wc, int span){ int i,j; unsigned short val=0; for (i=0;i<24;i++) { j = (i/8); if (wc->spans[span].chans[i].flags & ZT_FLAG_CLEAR) val |= 1 << (7 - (i % 8)); if ((i % 8)==7) { if (debug) printk("Putting %d in register %02x on span %d\n", val, 0x2f + j, span + 1); __t4_framer_out(wc,span, 0x2f + j, val); val = 0; } }}#if 0static void set_clear(struct t4 *wc, int span){ unsigned long flags; spin_lock_irqsave(&wc->reglock, flags); __set_clear(wc, span); spin_unlock_irqrestore(&wc->reglock, flags);}#endifstatic int t4_ioctl(struct zt_chan *chan, unsigned int cmd, unsigned long data){ struct t4_regs regs; int x; switch(cmd) { case WCT4_GET_REGS: for (x=0;x<NUM_PCI;x++) regs.pci[x] = t4_pci_in(chan->pvt, x); for (x=0;x<NUM_REGS;x++) regs.regs[x] = t4_framer_in(chan->pvt, chan->span->offset, x); if (copy_to_user((struct t4_regs *)data, ®s, sizeof(regs))) return -EFAULT; break; default: return -ENOTTY; } return 0;}static int t4_maint(struct zt_span *span, int cmd){ struct t4 *wc = span->pvt; if (wc->spantype[span->offset] == TYPE_E1) { switch(cmd) { case ZT_MAINT_NONE: printk("XXX Turn off local and remote loops E1 XXX\n"); break; case ZT_MAINT_LOCALLOOP: printk("XXX Turn on local loopback E1 XXX\n"); break; case ZT_MAINT_REMOTELOOP: printk("XXX Turn on remote loopback E1 XXX\n"); break; case ZT_MAINT_LOOPUP: printk("XXX Send loopup code E1 XXX\n"); break; case ZT_MAINT_LOOPDOWN: printk("XXX Send loopdown code E1 XXX\n"); break; case ZT_MAINT_LOOPSTOP: printk("XXX Stop sending loop codes E1 XXX\n"); break; default: printk("TE410P: Unknown E1 maint command: %d\n", cmd); break; } } else { switch(cmd) { case ZT_MAINT_NONE: printk("XXX Turn off local and remote loops T1 XXX\n"); break; case ZT_MAINT_LOCALLOOP: printk("XXX Turn on local loop and no remote loop XXX\n"); break; case ZT_MAINT_REMOTELOOP: printk("XXX Turn on remote loopup XXX\n"); break; case ZT_MAINT_LOOPUP: t4_framer_out(wc, span->offset, 0x21, 0x50); /* FMR5: Nothing but RBS mode */ break; case ZT_MAINT_LOOPDOWN: t4_framer_out(wc, span->offset, 0x21, 0x60); /* FMR5: Nothing but RBS mode */ break; case ZT_MAINT_LOOPSTOP: t4_framer_out(wc, span->offset, 0x21, 0x40); /* FMR5: Nothing but RBS mode */ break; default: printk("TE410P: Unknown T1 maint command: %d\n", cmd); break; } } return 0;}static int t4_rbsbits(struct zt_chan *chan, int bits){ u_char m,c; int k,n,b; struct t4 *wc = chan->pvt; unsigned long flags; if(debug) printk("Setting bits to %d on channel %s\n", bits, chan->name); spin_lock_irqsave(&wc->reglock, flags); k = chan->span->offset; if (wc->spantype[k] == TYPE_E1) { /* do it E1 way */ if (chan->chanpos == 16) { spin_unlock_irqrestore(&wc->reglock, flags); return 0; } n = chan->chanpos - 1; if (chan->chanpos > 15) n--; b = (n % 15); c = wc->txsigs[k][b]; m = (n / 15) << 2; /* nibble selector */ c &= (0xf << m); /* keep the other nibble */ c |= (bits & 0xf) << (4 - m); /* put our new nibble here */ wc->txsigs[k][b] = c; /* output them to the chip */ __t4_framer_out(wc,k,0x71 + b,c); } else if (wc->spans[k].lineconfig & ZT_CONFIG_D4) { n = chan->chanpos - 1; b = (n/4); c = wc->txsigs[k][b]; m = ((3 - (n % 4)) << 1); /* nibble selector */ c &= ~(0x3 << m); /* keep the other nibble */ c |= ((bits >> 2) & 0x3) << m; /* put our new nibble here */ wc->txsigs[k][b] = c; /* output them to the chip */ __t4_framer_out(wc,k,0x70 + b,c); __t4_framer_out(wc,k,0x70 + b + 6,c); } else if (wc->spans[k].lineconfig & ZT_CONFIG_ESF) { n = chan->chanpos - 1; b = (n/2); c = wc->txsigs[k][b]; m = ((n % 2) << 2); /* nibble selector */ c &= (0xf << m); /* keep the other nibble */ c |= (bits & 0xf) << (4 - m); /* put our new nibble here */ wc->txsigs[k][b] = c; /* output them to the chip */ __t4_framer_out(wc,k,0x70 + b,c); } spin_unlock_irqrestore(&wc->reglock, flags);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -