📄 zaptel.c
字号:
/* * Zapata Telephony Interface Driver * * Written by Mark Spencer <markster@linux-support.net> * Based on previous works, designs, and architectures conceived and * written by Jim Dixon <jim@lambdatel.com>. * * Special thanks to Steve Underwood <steve@coppice.org> * for substantial contributions to signal processing functions * in zaptel and the zapata library. * * Yury Bokhoncovich <byg@cf1.ru> * Adaptation for 2.4.20+ kernels (HDLC API was changed) * The work has been performed as a part of our move * from Cisco 3620 to IBM x305 here in F1 Group * * 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. * * $Id: zaptel.c,v 1.95.2.5 2005/01/17 01:58:09 russell Exp $ */#include "zconfig.h"#include <linux/kernel.h>#include <linux/errno.h>#include <linux/module.h>#include <linux/proc_fs.h>#include <linux/pci.h>#include <linux/init.h>#include <linux/version.h>#include <linux/kmod.h>#ifdef CONFIG_DEVFS_FS#include <linux/devfs_fs_kernel.h>#endif /* CONFIG_DEVFS_FS */#ifdef CONFIG_ZAPATA_NET#include <linux/netdevice.h>#endif /* CONFIG_ZAPATA_NET */#include <linux/ppp_defs.h>#ifdef CONFIG_ZAPATA_PPP#include <linux/netdevice.h>#include <linux/if.h>#include <linux/if_ppp.h>#endif#ifndef CONFIG_OLD_HDLC_API#define NEW_HDLC_INTERFACE#endif#define __ECHO_STATE_MUTE (1 << 8)#define ECHO_STATE_IDLE (0)#define ECHO_STATE_PRETRAINING (1 | (__ECHO_STATE_MUTE))#define ECHO_STATE_STARTTRAINING (2 | (__ECHO_STATE_MUTE))#define ECHO_STATE_AWAITINGECHO (3 | (__ECHO_STATE_MUTE))#define ECHO_STATE_TRAINING (4 | (__ECHO_STATE_MUTE))#define ECHO_STATE_ACTIVE (5)/* #define BUF_MUNGE *//* Grab fasthdlc with tables */#define FAST_HDLC_NEED_TABLES#include "fasthdlc.h"#ifdef STANDALONE_ZAPATA#include "zaptel.h"#else#include <linux/zaptel.h>#endif#ifdef LINUX26#include <linux/moduleparam.h>#endif/* Get helper arithmetic */#include "arith.h"#if defined(CONFIG_ZAPTEL_MMX) || defined(ECHO_CAN_FP)#include <asm/i387.h>#endif#define hdlc_to_ztchan(h) (((struct zt_hdlc *)(h))->chan)#define dev_to_ztchan(h) (((struct zt_hdlc *)(dev_to_hdlc(h)->priv))->chan)#ifdef LINUX26#define ztchan_to_dev(h) ((h)->hdlcnetdev->netdev)#else#define ztchan_to_dev(h) (&((h)->hdlcnetdev->netdev.netdev))#endif/* macro-oni for determining a unit (channel) number */#define UNIT(file) MINOR(file->f_dentry->d_inode->i_rdev)/* names of tx level settings */static char *zt_txlevelnames[] = {"0 db (CSU)/0-133 feet (DSX-1)","133-266 feet (DSX-1)","266-399 feet (DSX-1)","399-533 feet (DSX-1)","533-655 feet (DSX-1)","-7.5db (CSU)","-15db (CSU)","-22.5db (CSU)"} ;EXPORT_SYMBOL(zt_init_tone_state);EXPORT_SYMBOL(zt_dtmf_tone);EXPORT_SYMBOL(zt_register);EXPORT_SYMBOL(zt_unregister);EXPORT_SYMBOL(__zt_mulaw);EXPORT_SYMBOL(__zt_alaw);#ifdef CONFIG_CALC_XLAWEXPORT_SYMBOL(__zt_lineartoulaw);EXPORT_SYMBOL(__zt_lineartoalaw);#elseEXPORT_SYMBOL(__zt_lin2mu);EXPORT_SYMBOL(__zt_lin2a);#endifEXPORT_SYMBOL(zt_lboname);EXPORT_SYMBOL(zt_transmit);EXPORT_SYMBOL(zt_receive);EXPORT_SYMBOL(zt_rbsbits);EXPORT_SYMBOL(zt_qevent_nolock);EXPORT_SYMBOL(zt_qevent_lock);EXPORT_SYMBOL(zt_hooksig);EXPORT_SYMBOL(zt_alarm_notify);EXPORT_SYMBOL(zt_set_dynamic_ioctl);EXPORT_SYMBOL(zt_ec_chunk);#ifdef CONFIG_PROC_FSstatic struct proc_dir_entry *proc_entries[ZT_MAX_SPANS]; #endif/* Here are a couple important little additions for devfs */#ifdef CONFIG_DEVFS_FSstatic devfs_handle_t zaptel_devfs_dir;static devfs_handle_t channel;static devfs_handle_t pseudo;static devfs_handle_t ctl;static devfs_handle_t timer;#endif/* udev necessary data structures. Yeah! */#ifdef CONFIG_ZAP_UDEVstatic struct class_simple *zap_class = NULL;#endif/* There is a table like this in the PPP driver, too */static int deftaps = 64;static __u16 fcstab[256] ={ 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf, 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7, 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e, 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876, 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd, 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5, 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c, 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974, 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb, 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3, 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a, 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72, 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9, 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1, 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738, 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70, 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7, 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff, 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036, 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e, 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5, 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd, 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134, 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c, 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3, 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb, 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232, 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a, 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1, 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9, 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330, 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78};static int debug;/* states for transmit signalling */typedef enum {ZT_TXSTATE_ONHOOK,ZT_TXSTATE_OFFHOOK,ZT_TXSTATE_START, ZT_TXSTATE_PREWINK,ZT_TXSTATE_WINK,ZT_TXSTATE_PREFLASH, ZT_TXSTATE_FLASH,ZT_TXSTATE_DEBOUNCE,ZT_TXSTATE_AFTERSTART, ZT_TXSTATE_RINGON,ZT_TXSTATE_RINGOFF,ZT_TXSTATE_KEWL, ZT_TXSTATE_AFTERKEWL,ZT_TXSTATE_PULSEBREAK,ZT_TXSTATE_PULSEMAKE, ZT_TXSTATE_PULSEAFTER } ZT_TXSTATE_t;typedef short sumtype[ZT_MAX_CHUNKSIZE];static sumtype sums[(ZT_MAX_CONF + 1) * 3];/* Translate conference aliases into actual conferences and vice-versa */static short confalias[ZT_MAX_CONF + 1];static short confrev[ZT_MAX_CONF + 1];static sumtype *conf_sums_next;static sumtype *conf_sums;static sumtype *conf_sums_prev;static struct zt_span *master;static struct file_operations zt_fops;static struct{ int src; /* source conf number */ int dst; /* dst conf number */} conf_links[ZT_MAX_CONF + 1];/* There are three sets of conference sum accumulators. One for the currentsample chunk (conf_sums), one for the next sample chunk (conf_sums_next), andone for the previous sample chunk (conf_sums_prev). The following routine (rotate_sums) "rotates" the pointers to these accululator arrays as partof the events of sample chink processing as follows:The following sequence is designed to be looked at from the reference pointof the receive routine of the master span.1. All (real span) receive chunks are processed (with putbuf). The last oneto be processed is the master span. The data received is loaded into theaccumulators for the next chunk (conf_sums_next), to be in alignment withcurrent data after rotate_sums() is called (which immediately follows).Keep in mind that putbuf is *also* a transmit routine for the pseudo partsof channels that are in the REALANDPSEUDO conference mode. These channelsare processed from data in the current sample chunk (conf_sums), beingthat this is a "transmit" function (for the pseudo part).2. rotate_sums() is called.3. All pseudo channel receive chunks are processed. This data is loaded intothe current sample chunk accumulators (conf_sums).4. All conference links are processed (being that all receive data for thischunk has already been processed by now).5. All pseudo channel transmit chunks are processed. This data is loaded fromthe current sample chunk accumulators (conf_sums).6. All (real span) transmit chunks are processed (with getbuf). This data isloaded from the current sample chunk accumulators (conf_sums). Keep in mindthat getbuf is *also* a receive routine for the pseudo part of channels thatare in the REALANDPSEUDO conference mode. These samples are loaded intothe next sample chunk accumulators (conf_sums_next) to be processed as partof the next sample chunk's data (next time around the world).*/#define DIGIT_MODE_DTMF 0#define DIGIT_MODE_MFV1 1#define DIGIT_MODE_PULSE 2#include "digits.h"#if defined(CONFIG_ZAPTEL_MMX) || defined(ECHO_CAN_FP)/* XXX kernel_fpu_begin() is NOT exported properly (in 2.4), so we have to make a local version. Somebody fix this! XXX */#ifndef LINUX26static inline void __save_init_fpu( struct task_struct *tsk ){ if ( cpu_has_fxsr ) { asm volatile( "fxsave %0 ; fnclex" : "=m" (tsk->thread.i387.fxsave) ); } else { asm volatile( "fnsave %0 ; fwait" : "=m" (tsk->thread.i387.fsave) ); } tsk->flags &= ~PF_USEDFPU;}static inline void zt_kernel_fpu_begin(void){ struct task_struct *tsk = current; if (tsk->flags & PF_USEDFPU) { __save_init_fpu(tsk); return; } clts();}#else#define zt_kernel_fpu_begin kernel_fpu_begin#endif /* LINUX26 */#endif static struct zt_timer { int ms; /* Countdown */ int pos; /* Position */ int ping; /* Whether we've been ping'd */ int tripped; /* Whether we're tripped */ struct zt_timer *next; /* Linked list */ wait_queue_head_t sel;} *zaptimers = NULL;static spinlock_t zaptimerlock = SPIN_LOCK_UNLOCKED;static spinlock_t bigzaplock = SPIN_LOCK_UNLOCKED;struct zt_zone { char name[40]; /* Informational, only */ int ringcadence[ZT_MAX_CADENCE]; struct zt_tone *tones[ZT_TONE_MAX]; /* Each of these is a circular list of zt_tones to generate what we want. Use NULL if the tone is unavailable */};static struct zt_span *spans[ZT_MAX_SPANS];static struct zt_chan *chans[ZT_MAX_CHANNELS]; static int maxspans = 0;static int maxchans = 0;static int maxconfs = 0;static int maxlinks = 0;static int default_zone = DEFAULT_TONE_ZONE;short __zt_mulaw[256];short __zt_alaw[256];#ifndef CONFIG_CALC_XLAWu_char __zt_lin2mu[16384];u_char __zt_lin2a[16384];#endifstatic u_char defgain[256];static rwlock_t zone_lock = RW_LOCK_UNLOCKED;static rwlock_t chan_lock = RW_LOCK_UNLOCKED;static struct zt_zone *tone_zones[ZT_TONE_ZONE_MAX];#define NUM_SIGS 10 static inline void rotate_sums(void){ /* Rotate where we sum and so forth */ static int pos = 0; conf_sums_prev = sums + (ZT_MAX_CONF + 1) * pos; conf_sums = sums + (ZT_MAX_CONF + 1) * ((pos + 1) % 3); conf_sums_next = sums + (ZT_MAX_CONF + 1) * ((pos + 2) % 3); pos = (pos + 1) % 3; memset(conf_sums_next, 0, maxconfs * sizeof(sumtype));} /* return quiescent (idle) signalling states, for the various signalling types */static int zt_q_sig(struct zt_chan *chan){int x;static unsigned int in_sig[NUM_SIGS][2] = { { ZT_SIG_NONE, 0}, { ZT_SIG_EM, 0 | (ZT_ABIT << 8)}, { ZT_SIG_FXSLS,ZT_BBIT | (ZT_BBIT << 8)}, { ZT_SIG_FXSGS,ZT_ABIT | ZT_BBIT | ((ZT_ABIT | ZT_BBIT) << 8)}, { ZT_SIG_FXSKS,ZT_BBIT | ZT_BBIT | ((ZT_ABIT | ZT_BBIT) << 8)}, { ZT_SIG_FXOLS,0 | (ZT_ABIT << 8)}, { ZT_SIG_FXOGS,ZT_BBIT | ((ZT_ABIT | ZT_BBIT) << 8)}, { ZT_SIG_FXOKS,0 | (ZT_ABIT << 8)}, { ZT_SIG_SF, 0}, { ZT_SIG_EM_E1, ZT_DBIT | ((ZT_ABIT | ZT_DBIT) << 8) }, } ; /* must have span to begin with */ if (!chan->span) return(-1); /* if RBS does not apply, return error */ if (!(chan->span->flags & ZT_FLAG_RBS) || !chan->span->rbsbits) return(-1); if (chan->sig == ZT_SIG_CAS) { static int printed = 0; if (printed < 10) { printed++; } return chan->idlebits; } for (x=0;x<NUM_SIGS;x++) { if (in_sig[x][0] == chan->sig) return(in_sig[x][1]); } return(-1); /* not found -- error */}#ifdef CONFIG_PROC_FSstatic char *sigstr(int sig){ switch (sig) { case ZT_SIG_FXSLS: return "FXSLS"; case ZT_SIG_FXSKS: return "FXSKS"; case ZT_SIG_FXSGS: return "FXSGS"; case ZT_SIG_FXOLS: return "FXOLS"; case ZT_SIG_FXOKS: return "FXOKS"; case ZT_SIG_FXOGS: return "FXOGS"; case ZT_SIG_EM: return "E&M"; case ZT_SIG_EM_E1: return "E&M-E1"; case ZT_SIG_CLEAR: return "Clear"; case ZT_SIG_HDLCRAW: return "HDLCRAW"; case ZT_SIG_HDLCFCS: return "HDLCFCS"; case ZT_SIG_HDLCNET: return "HDLCNET"; case ZT_SIG_SLAVE: return "Slave"; case ZT_SIG_CAS: return "CAS"; case ZT_SIG_DACS: return "DACS"; case ZT_SIG_DACS_RBS: return "DACS+RBS"; case ZT_SIG_SF: return "SF (ToneOnly)"; case ZT_SIG_NONE: default: return "Unconfigured"; }}static int zaptel_proc_read(char *page, char **start, off_t off, int count, int *eof, void *data){ int x, len = 0; long span; /* In Linux 2.6, this MUST NOT EXECEED 1024 bytes in one read! */ if (off > 0) return 0; span = (long)data; if (!span) return 0; if (spans[span]->name) len += sprintf(page + len, "Span %ld: %s ", span, spans[span]->name); if (spans[span]->desc) len += sprintf(page + len, "\"%s\"", spans[span]->desc); else len += sprintf(page + len, "\"\""); if (spans[span]->lineconfig) { /* framing first */ if (spans[span]->lineconfig & ZT_CONFIG_B8ZS) len += sprintf(page + len, " B8ZS/"); else if (spans[span]->lineconfig & ZT_CONFIG_AMI) len += sprintf(page + len, " AMI/"); else if (spans[span]->lineconfig & ZT_CONFIG_HDB3) len += sprintf(page + len, " HDB3/"); /* then coding */ if (spans[span]->lineconfig & ZT_CONFIG_ESF) len += sprintf(page + len, "ESF"); else if (spans[span]->lineconfig & ZT_CONFIG_D4) len += sprintf(page + len, "D4"); else if (spans[span]->lineconfig & ZT_CONFIG_CCS)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -