📄 isdn_common.c
字号:
/* $Id: isdn_common.c,v 1.1.2.3 2004/02/10 01:07:13 keil 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 software may be used and distributed according to the terms * of the GNU General Public License, incorporated herein by reference. * */#include <linux/config.h>#include <linux/module.h>#include <linux/init.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"/* Debugflags */#undef ISDN_DEBUG_STATCALLBMODULE_DESCRIPTION("ISDN4Linux: link layer");MODULE_AUTHOR("Fritz Elfert");MODULE_LICENSE("GPL");isdn_dev *dev;static char *isdn_revision = "$Revision: 1.1.2.3 $";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_DIVERSION */static int isdn_writebuf_stub(int, int, const u_char __user *, int);static void set_global_features(void);static int isdn_wildmat(char *s, char *p);static int isdn_add_channels(isdn_driver_t *d, int drvidx, int n, int adding);static inline voidisdn_lock_driver(isdn_driver_t *drv){ try_module_get(drv->interface->owner); drv->locks++;}voidisdn_lock_drivers(void){ int i; for (i = 0; i < ISDN_MAX_DRIVERS; i++) { if (!dev->drv[i]) continue; isdn_lock_driver(dev->drv[i]); }}static inline voidisdn_unlock_driver(isdn_driver_t *drv){ if (drv->locks > 0) { drv->locks--; module_put(drv->interface->owner); }}voidisdn_unlock_drivers(void){ int i; for (i = 0; i < ISDN_MAX_DRIVERS; i++) { if (!dev->drv[i]) continue; isdn_unlock_driver(dev->drv[i]); }}#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 );}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 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 (tf & ISDN_TIMER_CARRIER) isdn_tty_carrier_timeout(); } } if (tf) mod_timer(&dev->timer, jiffies+ISDN_TIMER_RES);}voidisdn_timer_ctrl(int tf, int onoff){ unsigned long flags; int old_tflags; spin_lock_irqsave(&dev->timerlock, flags); 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; } old_tflags = dev->tflags; if (onoff) dev->tflags |= tf; else dev->tflags &= ~tf; if (dev->tflags && !old_tflags) mod_timer(&dev->timer, jiffies+ISDN_TIMER_RES); spin_unlock_irqrestore(&dev->timerlock, 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 <linux/isdn/capicmd.h>static 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; u_long 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: dev->drv[di]->stavail += c->arg; 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
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -