📄 isdn_common.c
字号:
/* $Id: isdn_common.c,v 1.114 2000/11/25 17:00:59 kai Exp $ * Linux ISDN subsystem, common used functions (linklevel). * * Copyright 1994-1999 by Fritz Elfert (fritz@isdn4linux.de) * Copyright 1995,96 Thinking Objects Software GmbH Wuerzburg * Copyright 1995,96 by Michael Hipp (Michael.Hipp@student.uni-tuebingen.de) * * 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, 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/config.h>#include <linux/module.h>#include <linux/version.h>#include <linux/poll.h>#include <linux/vmalloc.h>#include <linux/isdn.h>#include <linux/smp_lock.h>#include "isdn_common.h"#include "isdn_tty.h"#include "isdn_net.h"#include "isdn_ppp.h"#ifdef CONFIG_ISDN_AUDIO#include "isdn_audio.h"#endif#ifdef CONFIG_ISDN_DIVERSION_MODULE#define CONFIG_ISDN_DIVERSION#endif#ifdef CONFIG_ISDN_DIVERSION#include <linux/isdn_divertif.h>#endif CONFIG_ISDN_DIVERSION#include "isdn_v110.h"#include "isdn_cards.h"#include <linux/devfs_fs_kernel.h>/* Debugflags */#undef ISDN_DEBUG_STATCALLBisdn_dev *dev;static char *isdn_revision = "$Revision: 1.114 $";extern char *isdn_net_revision;extern char *isdn_tty_revision;#ifdef CONFIG_ISDN_PPPextern char *isdn_ppp_revision;#elsestatic char *isdn_ppp_revision = ": none $";#endif#ifdef CONFIG_ISDN_AUDIOextern char *isdn_audio_revision;#elsestatic char *isdn_audio_revision = ": none $";#endifextern char *isdn_v110_revision;#ifdef CONFIG_ISDN_DIVERSIONstatic isdn_divert_if *divert_if; /* = NULL */#endif CONFIG_ISDN_DIVERSIONstatic int isdn_writebuf_stub(int, int, const u_char *, int, int);static void set_global_features(void);static void isdn_register_devfs(int);static void isdn_unregister_devfs(int);static int isdn_wildmat(char *s, char *p);voidisdn_lock_drivers(void){ int i; for (i = 0; i < dev->drivers; i++) { isdn_ctrl cmd; cmd.driver = i; cmd.arg = 0; cmd.command = ISDN_CMD_LOCK; isdn_command(&cmd); dev->drv[i]->locks++; }}voidisdn_MOD_INC_USE_COUNT(void){ MOD_INC_USE_COUNT; isdn_lock_drivers();}voidisdn_unlock_drivers(void){ int i; for (i = 0; i < dev->drivers; i++) if (dev->drv[i]->locks > 0) { isdn_ctrl cmd; cmd.driver = i; cmd.arg = 0; cmd.command = ISDN_CMD_UNLOCK; isdn_command(&cmd); dev->drv[i]->locks--; }}voidisdn_MOD_DEC_USE_COUNT(void){ MOD_DEC_USE_COUNT; isdn_unlock_drivers();}#if defined(ISDN_DEBUG_NET_DUMP) || defined(ISDN_DEBUG_MODEM_DUMP)voidisdn_dumppkt(char *s, u_char * p, int len, int dumplen){ int dumpc; printk(KERN_DEBUG "%s(%d) ", s, len); for (dumpc = 0; (dumpc < dumplen) && (len); len--, dumpc++) printk(" %02x", *p++); printk("\n");}#endif/* * I picked the pattern-matching-functions from an old GNU-tar version (1.10) * It was originally written and put to PD by rs@mirror.TMC.COM (Rich Salz) */static intisdn_star(char *s, char *p){ while (isdn_wildmat(s, p)) { if (*++s == '\0') return (2); } return (0);}/* * Shell-type Pattern-matching for incoming caller-Ids * This function gets a string in s and checks, if it matches the pattern * given in p. * * Return: * 0 = match. * 1 = no match. * 2 = no match. Would eventually match, if s would be longer. * * Possible Patterns: * * '?' matches one character * '*' matches zero or more characters * [xyz] matches the set of characters in brackets. * [^xyz] matches any single character not in the set of characters */static intisdn_wildmat(char *s, char *p){ register int last; register int matched; register int reverse; register int nostar = 1; if (!(*s) && !(*p)) return(1); for (; *p; s++, p++) switch (*p) { case '\\': /* * Literal match with following character, * fall through. */ p++; default: if (*s != *p) return (*s == '\0')?2:1; continue; case '?': /* Match anything. */ if (*s == '\0') return (2); continue; case '*': nostar = 0; /* Trailing star matches everything. */ return (*++p ? isdn_star(s, p) : 0); case '[': /* [^....] means inverse character class. */ if ((reverse = (p[1] == '^'))) p++; for (last = 0, matched = 0; *++p && (*p != ']'); last = *p) /* This next line requires a good C compiler. */ if (*p == '-' ? *s <= *++p && *s >= last : *s == *p) matched = 1; if (matched == reverse) return (1); continue; } return (*s == '\0')?0:nostar;}int isdn_msncmp( const char * msn1, const char * msn2 ){ char TmpMsn1[ ISDN_MSNLEN ]; char TmpMsn2[ ISDN_MSNLEN ]; char *p; for ( p = TmpMsn1; *msn1 && *msn1 != ':'; ) // Strip off a SPID *p++ = *msn1++; *p = '\0'; for ( p = TmpMsn2; *msn2 && *msn2 != ':'; ) // Strip off a SPID *p++ = *msn2++; *p = '\0'; return isdn_wildmat( TmpMsn1, TmpMsn2 );}static voidisdn_free_queue(struct sk_buff_head *queue){ struct sk_buff *skb; unsigned long flags; save_flags(flags); cli(); if (skb_queue_len(queue)) while ((skb = skb_dequeue(queue))) dev_kfree_skb(skb); restore_flags(flags);}intisdn_dc2minor(int di, int ch){ int i; for (i = 0; i < ISDN_MAX_CHANNELS; i++) if (dev->chanmap[i] == ch && dev->drvmap[i] == di) return i; return -1;}static int isdn_timer_cnt1 = 0;static int isdn_timer_cnt2 = 0;static int isdn_timer_cnt3 = 0;static int isdn_timer_cnt4 = 0;static voidisdn_timer_funct(ulong dummy){ int tf = dev->tflags; if (tf & ISDN_TIMER_FAST) { if (tf & ISDN_TIMER_MODEMREAD) isdn_tty_readmodem(); if (tf & ISDN_TIMER_MODEMPLUS) isdn_tty_modem_escape(); if (tf & ISDN_TIMER_MODEMXMIT) isdn_tty_modem_xmit(); } if (tf & ISDN_TIMER_SLOW) { if (++isdn_timer_cnt1 >= ISDN_TIMER_02SEC) { isdn_timer_cnt1 = 0; if (tf & ISDN_TIMER_NETDIAL) isdn_net_dial(); } if (++isdn_timer_cnt2 >= ISDN_TIMER_1SEC) { isdn_timer_cnt2 = 0; if (tf & ISDN_TIMER_NETHANGUP) isdn_net_autohup(); if (++isdn_timer_cnt3 > ISDN_TIMER_RINGING) { isdn_timer_cnt3 = 0; if (tf & ISDN_TIMER_MODEMRING) isdn_tty_modem_ring(); } if (++isdn_timer_cnt4 > ISDN_TIMER_KEEPINT) { isdn_timer_cnt4 = 0; if (tf & ISDN_TIMER_KEEPALIVE) isdn_net_slarp_out(); } if (tf & ISDN_TIMER_CARRIER) isdn_tty_carrier_timeout(); } } if (tf) { int flags; save_flags(flags); cli(); mod_timer(&dev->timer, jiffies+ISDN_TIMER_RES); restore_flags(flags); }}voidisdn_timer_ctrl(int tf, int onoff){ int flags; save_flags(flags); cli(); if ((tf & ISDN_TIMER_SLOW) && (!(dev->tflags & ISDN_TIMER_SLOW))) { /* If the slow-timer wasn't activated until now */ isdn_timer_cnt1 = 0; isdn_timer_cnt2 = 0; } if (onoff) dev->tflags |= tf; else dev->tflags &= ~tf; if (dev->tflags) mod_timer(&dev->timer, jiffies+ISDN_TIMER_RES); restore_flags(flags);}/* * Receive a packet from B-Channel. (Called from low-level-module) */static voidisdn_receive_skb_callback(int di, int channel, struct sk_buff *skb){ int i; if ((i = isdn_dc2minor(di, channel)) == -1) { dev_kfree_skb(skb); return; } /* Update statistics */ dev->ibytes[i] += skb->len; /* First, try to deliver data to network-device */ if (isdn_net_rcv_skb(i, skb)) return; /* V.110 handling * makes sense for async streams only, so it is * called after possible net-device delivery. */ if (dev->v110[i]) { atomic_inc(&dev->v110use[i]); skb = isdn_v110_decode(dev->v110[i], skb); atomic_dec(&dev->v110use[i]); if (!skb) return; } /* No network-device found, deliver to tty or raw-channel */ if (skb->len) { if (isdn_tty_rcv_skb(i, di, channel, skb)) return; wake_up_interruptible(&dev->drv[di]->rcv_waitq[channel]); } else dev_kfree_skb(skb);}/* * Intercept command from Linklevel to Lowlevel. * If layer 2 protocol is V.110 and this is not supported by current * lowlevel-driver, use driver's transparent mode and handle V.110 in * linklevel instead. */intisdn_command(isdn_ctrl *cmd){ if (cmd->driver == -1) { printk(KERN_WARNING "isdn_command command(%x) driver -1\n", cmd->command); return(1); } if (cmd->command == ISDN_CMD_SETL2) { int idx = isdn_dc2minor(cmd->driver, cmd->arg & 255); unsigned long l2prot = (cmd->arg >> 8) & 255; unsigned long features = (dev->drv[cmd->driver]->interface->features >> ISDN_FEATURE_L2_SHIFT) & ISDN_FEATURE_L2_MASK; unsigned long l2_feature = (1 << l2prot); switch (l2prot) { case ISDN_PROTO_L2_V11096: case ISDN_PROTO_L2_V11019: case ISDN_PROTO_L2_V11038: /* If V.110 requested, but not supported by * HL-driver, set emulator-flag and change * Layer-2 to transparent */ if (!(features & l2_feature)) { dev->v110emu[idx] = l2prot; cmd->arg = (cmd->arg & 255) | (ISDN_PROTO_L2_TRANS << 8); } else dev->v110emu[idx] = 0; } } return dev->drv[cmd->driver]->interface->command(cmd);}voidisdn_all_eaz(int di, int ch){ isdn_ctrl cmd; if (di < 0) return; cmd.driver = di; cmd.arg = ch; cmd.command = ISDN_CMD_SETEAZ; cmd.parm.num[0] = '\0'; isdn_command(&cmd);}/* * Begin of a CAPI like LL<->HL interface, currently used only for * supplementary service (CAPI 2.0 part III) */#include "avmb1/capicmd.h" /* this should be moved in a common place */intisdn_capi_rec_hl_msg(capi_msg *cm) { int di; int ch; di = (cm->adr.Controller & 0x7f) -1; ch = isdn_dc2minor(di, (cm->adr.Controller>>8)& 0x7f); switch(cm->Command) { case CAPI_FACILITY: /* in the moment only handled in tty */ return(isdn_tty_capi_facility(cm)); default: return(-1); }}static intisdn_status_callback(isdn_ctrl * c){ int di; ulong flags; int i; int r; int retval = 0; isdn_ctrl cmd; isdn_net_dev *p; di = c->driver; i = isdn_dc2minor(di, c->arg); switch (c->command) { case ISDN_STAT_BSENT: if (i < 0) return -1; if (dev->global_flags & ISDN_GLOBAL_STOPPED) return 0; if (isdn_net_stat_callback(i, c)) return 0; if (isdn_v110_stat_callback(i, c)) return 0; if (isdn_tty_stat_callback(i, c)) return 0; wake_up_interruptible(&dev->drv[di]->snd_waitq[c->arg]); break; case ISDN_STAT_STAVAIL: save_flags(flags); cli(); dev->drv[di]->stavail += c->arg; restore_flags(flags); wake_up_interruptible(&dev->drv[di]->st_waitq); break; case ISDN_STAT_RUN: dev->drv[di]->flags |= DRV_FLAG_RUNNING; for (i = 0; i < ISDN_MAX_CHANNELS; i++) if (dev->drvmap[i] == di) isdn_all_eaz(di, dev->chanmap[i]); set_global_features(); break; case ISDN_STAT_STOP: dev->drv[di]->flags &= ~DRV_FLAG_RUNNING; break; case ISDN_STAT_ICALL: if (i < 0) return -1;#ifdef ISDN_DEBUG_STATCALLB printk(KERN_DEBUG "ICALL (net): %d %ld %s\n", di, c->arg, c->parm.num);#endif if (dev->global_flags & ISDN_GLOBAL_STOPPED) { cmd.driver = di; cmd.arg = c->arg; cmd.command = ISDN_CMD_HANGUP; isdn_command(&cmd); return 0; } /* Try to find a network-interface which will accept incoming call */ r = ((c->command == ISDN_STAT_ICALLW) ? 0 : isdn_net_find_icall(di, c->arg, i, &c->parm.setup)); switch (r) { case 0: /* No network-device replies. * Try ttyI's. * These return 0 on no match, 1 on match and * 3 on eventually match, if CID is longer. */ if (c->command == ISDN_STAT_ICALL) if ((retval = isdn_tty_find_icall(di, c->arg, &c->parm.setup))) return(retval);#ifdef CONFIG_ISDN_DIVERSION if (divert_if) if ((retval = divert_if->stat_callback(c))) return(retval); /* processed */#endif CONFIG_ISDN_DIVERSION if ((!retval) && (dev->drv[di]->flags & DRV_FLAG_REJBUS)) { /* No tty responding */ cmd.driver = di; cmd.arg = c->arg; cmd.command = ISDN_CMD_HANGUP; isdn_command(&cmd); retval = 2; } break; case 1: /* Schedule connection-setup */ isdn_net_dial(); cmd.driver = di; cmd.arg = c->arg; cmd.command = ISDN_CMD_ACCEPTD; for ( p = dev->netdev; p; p = p->next ) if ( p->local->isdn_channel == cmd.arg ) { strcpy( cmd.parm.setup.eazmsn, p->local->msn ); isdn_command(&cmd); retval = 1; break; } break; case 2: /* For calling back, first reject incoming call ... */ case 3: /* Interface found, but down, reject call actively */ retval = 2; printk(KERN_INFO "isdn: Rejecting Call\n"); cmd.driver = di; cmd.arg = c->arg; cmd.command = ISDN_CMD_HANGUP; isdn_command(&cmd); if (r == 3) break; /* Fall through */ case 4: /* ... then start callback. */ isdn_net_dial(); break; case 5: /* Number would eventually match, if longer */ retval = 3; break; }#ifdef ISDN_DEBUG_STATCALLB printk(KERN_DEBUG "ICALL: ret=%d\n", retval);#endif return retval; break; case ISDN_STAT_CINF: if (i < 0) return -1;#ifdef ISDN_DEBUG_STATCALLB printk(KERN_DEBUG "CINF: %ld %s\n", c->arg, c->parm.num);#endif if (dev->global_flags & ISDN_GLOBAL_STOPPED) return 0; if (strcmp(c->parm.num, "0")) isdn_net_stat_callback(i, c); isdn_tty_stat_callback(i, c); break; case ISDN_STAT_CAUSE:#ifdef ISDN_DEBUG_STATCALLB printk(KERN_DEBUG "CAUSE: %ld %s\n", c->arg, c->parm.num);#endif printk(KERN_INFO "isdn: %s,ch%ld cause: %s\n", dev->drvid[di], c->arg, c->parm.num); isdn_tty_stat_callback(i, c);#ifdef CONFIG_ISDN_DIVERSION if (divert_if) divert_if->stat_callback(c); #endif CONFIG_ISDN_DIVERSION break; case ISDN_STAT_DISPLAY:#ifdef ISDN_DEBUG_STATCALLB printk(KERN_DEBUG "DISPLAY: %ld %s\n", c->arg, c->parm.display);#endif isdn_tty_stat_callback(i, c);#ifdef CONFIG_ISDN_DIVERSION if (divert_if) divert_if->stat_callback(c); #endif CONFIG_ISDN_DIVERSION break; case ISDN_STAT_DCONN: if (i < 0) return -1;#ifdef ISDN_DEBUG_STATCALLB printk(KERN_DEBUG "DCONN: %ld\n", c->arg);#endif if (dev->global_flags & ISDN_GLOBAL_STOPPED) return 0; /* Find any net-device, waiting for D-channel setup */ if (isdn_net_stat_callback(i, c)) break; isdn_v110_stat_callback(i, c); /* Find any ttyI, waiting for D-channel setup */ if (isdn_tty_stat_callback(i, c)) { cmd.driver = di;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -