📄 isdn_net.c
字号:
/* $Id: isdn_net.c,v 1.1.2.2 2004/01/12 22:37:19 keil Exp $ * * Linux ISDN subsystem, network interfaces and related functions (linklevel). * * Copyright 1994-1998 by Fritz Elfert (fritz@isdn4linux.de) * Copyright 1995,96 by 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. * * Data Over Voice (DOV) support added - Guy Ellis 23-Mar-02 * guy@traverse.com.au * Outgoing calls - looks for a 'V' in first char of dialed number * Incoming calls - checks first character of eaz as follows: * Numeric - accept DATA only - original functionality * 'V' - accept VOICE (DOV) only * 'B' - accept BOTH DATA and DOV types * * Jan 2001: fix CISCO HDLC Bjoern A. Zeeb <i4l@zabbadoz.net> * for info on the protocol, see * http://i4l.zabbadoz.net/i4l/cisco-hdlc.txt */#include <linux/config.h>#include <linux/isdn.h>#include <net/arp.h>#include <net/dst.h>#include <net/pkt_sched.h>#include <linux/inetdevice.h>#include "isdn_common.h"#include "isdn_net.h"#ifdef CONFIG_ISDN_PPP#include "isdn_ppp.h"#endif#ifdef CONFIG_ISDN_X25#include <linux/concap.h>#include "isdn_concap.h"#endif/* * Outline of new tbusy handling: * * Old method, roughly spoken, consisted of setting tbusy when entering * isdn_net_start_xmit() and at several other locations and clearing * it from isdn_net_start_xmit() thread when sending was successful. * * With 2.3.x multithreaded network core, to prevent problems, tbusy should * only be set by the isdn_net_start_xmit() thread and only when a tx-busy * condition is detected. Other threads (in particular isdn_net_stat_callb()) * are only allowed to clear tbusy. * * -HE *//* * About SOFTNET: * Most of the changes were pretty obvious and basically done by HE already. * * One problem of the isdn net device code is that is uses struct net_device * for masters and slaves. However, only master interface are registered to * the network layer, and therefore, it only makes sense to call netif_* * functions on them. * * --KG *//* * Find out if the netdevice has been ifup-ed yet. * For slaves, look at the corresponding master. */static __inline__ int isdn_net_device_started(isdn_net_dev *n){ isdn_net_local *lp = n->local; struct net_device *dev; if (lp->master) dev = lp->master; else dev = &n->dev; return netif_running(dev);}/* * wake up the network -> net_device queue. * For slaves, wake the corresponding master interface. */static __inline__ void isdn_net_device_wake_queue(isdn_net_local *lp){ if (lp->master) netif_wake_queue(lp->master); else netif_wake_queue(&lp->netdev->dev);}/* * stop the network -> net_device queue. * For slaves, stop the corresponding master interface. */static __inline__ void isdn_net_device_stop_queue(isdn_net_local *lp){ if (lp->master) netif_stop_queue(lp->master); else netif_stop_queue(&lp->netdev->dev);}/* * find out if the net_device which this lp belongs to (lp can be * master or slave) is busy. It's busy iff all (master and slave) * queues are busy */static __inline__ int isdn_net_device_busy(isdn_net_local *lp){ isdn_net_local *nlp; isdn_net_dev *nd; unsigned long flags; if (!isdn_net_lp_busy(lp)) return 0; if (lp->master) nd = ((isdn_net_local *) lp->master->priv)->netdev; else nd = lp->netdev; spin_lock_irqsave(&nd->queue_lock, flags); nlp = lp->next; while (nlp != lp) { if (!isdn_net_lp_busy(nlp)) { spin_unlock_irqrestore(&nd->queue_lock, flags); return 0; } nlp = nlp->next; } spin_unlock_irqrestore(&nd->queue_lock, flags); return 1;}static __inline__ void isdn_net_inc_frame_cnt(isdn_net_local *lp){ atomic_inc(&lp->frame_cnt); if (isdn_net_device_busy(lp)) isdn_net_device_stop_queue(lp);}static __inline__ void isdn_net_dec_frame_cnt(isdn_net_local *lp){ atomic_dec(&lp->frame_cnt); if (!(isdn_net_device_busy(lp))) { if (!skb_queue_empty(&lp->super_tx_queue)) { schedule_work(&lp->tqueue); } else { isdn_net_device_wake_queue(lp); } } }static __inline__ void isdn_net_zero_frame_cnt(isdn_net_local *lp){ atomic_set(&lp->frame_cnt, 0);}/* For 2.2.x we leave the transmitter busy timeout at 2 secs, just * to be safe. * For 2.3.x we push it up to 20 secs, because call establishment * (in particular callback) may take such a long time, and we * don't want confusing messages in the log. However, there is a slight * possibility that this large timeout will break other things like MPPP, * which might rely on the tx timeout. If so, we'll find out this way... */#define ISDN_NET_TX_TIMEOUT (20*HZ) /* Prototypes */static int isdn_net_force_dial_lp(isdn_net_local *);static int isdn_net_start_xmit(struct sk_buff *, struct net_device *);static void isdn_net_ciscohdlck_connected(isdn_net_local *lp);static void isdn_net_ciscohdlck_disconnected(isdn_net_local *lp);char *isdn_net_revision = "$Revision: 1.1.2.2 $"; /* * Code for raw-networking over ISDN */static voidisdn_net_unreachable(struct net_device *dev, struct sk_buff *skb, char *reason){ if(skb) { u_short proto = ntohs(skb->protocol); printk(KERN_DEBUG "isdn_net: %s: %s, signalling dst_link_failure %s\n", dev->name, (reason != NULL) ? reason : "unknown", (proto != ETH_P_IP) ? "Protocol != ETH_P_IP" : ""); dst_link_failure(skb); } else { /* dial not triggered by rawIP packet */ printk(KERN_DEBUG "isdn_net: %s: %s\n", dev->name, (reason != NULL) ? reason : "reason unknown"); }}static voidisdn_net_reset(struct net_device *dev){#ifdef CONFIG_ISDN_X25 struct concap_device_ops * dops = ( (isdn_net_local *) dev->priv ) -> dops; struct concap_proto * cprot = ( (isdn_net_local *) dev->priv ) -> netdev -> cprot;#endif#ifdef CONFIG_ISDN_X25 if( cprot && cprot -> pops && dops ) cprot -> pops -> restart ( cprot, dev, dops );#endif}/* Open/initialize the board. */static intisdn_net_open(struct net_device *dev){ int i; struct net_device *p; struct in_device *in_dev; /* moved here from isdn_net_reset, because only the master has an interface associated which is supposed to be started. BTW: we need to call netif_start_queue, not netif_wake_queue here */ netif_start_queue(dev); isdn_net_reset(dev); /* Fill in the MAC-level header (not needed, but for compatibility... */ for (i = 0; i < ETH_ALEN - sizeof(u32); i++) dev->dev_addr[i] = 0xfc; if ((in_dev = dev->ip_ptr) != NULL) { /* * Any address will do - we take the first */ struct in_ifaddr *ifa = in_dev->ifa_list; if (ifa != NULL) memcpy(dev->dev_addr+2, &ifa->ifa_local, 4); } /* If this interface has slaves, start them also */ if ((p = (((isdn_net_local *) dev->priv)->slave))) { while (p) { isdn_net_reset(p); p = (((isdn_net_local *) p->priv)->slave); } } isdn_lock_drivers(); return 0;}/* * Assign an ISDN-channel to a net-interface */static voidisdn_net_bind_channel(isdn_net_local * lp, int idx){ lp->flags |= ISDN_NET_CONNECTED; lp->isdn_device = dev->drvmap[idx]; lp->isdn_channel = dev->chanmap[idx]; dev->rx_netdev[idx] = lp->netdev; dev->st_netdev[idx] = lp->netdev;}/* * unbind a net-interface (resets interface after an error) */static voidisdn_net_unbind_channel(isdn_net_local * lp){ skb_queue_purge(&lp->super_tx_queue); if (!lp->master) { /* reset only master device */ /* Moral equivalent of dev_purge_queues(): BEWARE! This chunk of code cannot be called from hardware interrupt handler. I hope it is true. --ANK */ qdisc_reset(lp->netdev->dev.qdisc); } lp->dialstate = 0; dev->rx_netdev[isdn_dc2minor(lp->isdn_device, lp->isdn_channel)] = NULL; dev->st_netdev[isdn_dc2minor(lp->isdn_device, lp->isdn_channel)] = NULL; isdn_free_channel(lp->isdn_device, lp->isdn_channel, ISDN_USAGE_NET); lp->flags &= ~ISDN_NET_CONNECTED; lp->isdn_device = -1; lp->isdn_channel = -1;}/* * Perform auto-hangup and cps-calculation for net-interfaces. * * auto-hangup: * Increment idle-counter (this counter is reset on any incoming or * outgoing packet), if counter exceeds configured limit either do a * hangup immediately or - if configured - wait until just before the next * charge-info. * * cps-calculation (needed for dynamic channel-bundling): * Since this function is called every second, simply reset the * byte-counter of the interface after copying it to the cps-variable. */static unsigned long last_jiffies = -HZ;voidisdn_net_autohup(void){ isdn_net_dev *p = dev->netdev; int anymore; anymore = 0; while (p) { isdn_net_local *l = p->local; if (jiffies == last_jiffies) l->cps = l->transcount; else l->cps = (l->transcount * HZ) / (jiffies - last_jiffies); l->transcount = 0; if (dev->net_verbose > 3) printk(KERN_DEBUG "%s: %d bogocps\n", l->name, l->cps); if ((l->flags & ISDN_NET_CONNECTED) && (!l->dialstate)) { anymore = 1; l->huptimer++; /* * if there is some dialmode where timeout-hangup * should _not_ be done, check for that here */ if ((l->onhtime) && (l->huptimer > l->onhtime)) { if (l->hupflags & ISDN_MANCHARGE && l->hupflags & ISDN_CHARGEHUP) { while (time_after(jiffies, l->chargetime + l->chargeint)) l->chargetime += l->chargeint; if (time_after(jiffies, l->chargetime + l->chargeint - 2 * HZ)) if (l->outgoing || l->hupflags & ISDN_INHUP) isdn_net_hangup(&p->dev); } else if (l->outgoing) { if (l->hupflags & ISDN_CHARGEHUP) { if (l->hupflags & ISDN_WAITCHARGE) { printk(KERN_DEBUG "isdn_net: Hupflags of %s are %X\n", l->name, l->hupflags); isdn_net_hangup(&p->dev); } else if (time_after(jiffies, l->chargetime + l->chargeint)) { printk(KERN_DEBUG "isdn_net: %s: chtime = %lu, chint = %d\n", l->name, l->chargetime, l->chargeint); isdn_net_hangup(&p->dev); } } else isdn_net_hangup(&p->dev); } else if (l->hupflags & ISDN_INHUP) isdn_net_hangup(&p->dev); } if(dev->global_flags & ISDN_GLOBAL_STOPPED || (ISDN_NET_DIALMODE(*l) == ISDN_NET_DM_OFF)) { isdn_net_hangup(&p->dev); break; } } p = (isdn_net_dev *) p->next; } last_jiffies = jiffies; isdn_timer_ctrl(ISDN_TIMER_NETHANGUP, anymore);}static void isdn_net_lp_disconnected(isdn_net_local *lp){ isdn_net_rm_from_bundle(lp);}/* * Handle status-messages from ISDN-interfacecard. * This function is called from within the main-status-dispatcher * isdn_status_callback, which itself is called from the low-level driver. * Return: 1 = Event handled, 0 = not for us or unknown Event. */intisdn_net_stat_callback(int idx, isdn_ctrl *c){ isdn_net_dev *p = dev->st_netdev[idx]; int cmd = c->command; if (p) { isdn_net_local *lp = p->local;#ifdef CONFIG_ISDN_X25 struct concap_proto *cprot = lp->netdev->cprot; struct concap_proto_ops *pops = cprot ? cprot->pops : NULL;#endif switch (cmd) { case ISDN_STAT_BSENT: /* A packet has successfully been sent out */ if ((lp->flags & ISDN_NET_CONNECTED) && (!lp->dialstate)) { isdn_net_dec_frame_cnt(lp); lp->stats.tx_packets++; lp->stats.tx_bytes += c->parm.length; } return 1; case ISDN_STAT_DCONN: /* D-Channel is up */ switch (lp->dialstate) { case 4: case 7: case 8: lp->dialstate++; return 1; case 12: lp->dialstate = 5; return 1; } break; case ISDN_STAT_DHUP: /* Either D-Channel-hangup or error during dialout */#ifdef CONFIG_ISDN_X25 /* If we are not connencted then dialing had failed. If there are generic encap protocol receiver routines signal the closure of the link*/ if( !(lp->flags & ISDN_NET_CONNECTED) && pops && pops -> disconn_ind ) pops -> disconn_ind(cprot);#endif /* CONFIG_ISDN_X25 */ if ((!lp->dialstate) && (lp->flags & ISDN_NET_CONNECTED)) { if (lp->p_encap == ISDN_NET_ENCAP_CISCOHDLCK) isdn_net_ciscohdlck_disconnected(lp);#ifdef CONFIG_ISDN_PPP if (lp->p_encap == ISDN_NET_ENCAP_SYNCPPP) isdn_ppp_free(lp);#endif isdn_net_lp_disconnected(lp); isdn_all_eaz(lp->isdn_device, lp->isdn_channel); printk(KERN_INFO "%s: remote hangup\n", lp->name); printk(KERN_INFO "%s: Chargesum is %d\n", lp->name, lp->charge); isdn_net_unbind_channel(lp); return 1; } break;#ifdef CONFIG_ISDN_X25 case ISDN_STAT_BHUP: /* B-Channel-hangup */ /* try if there are generic encap protocol receiver routines and signal the closure of the link */ if( pops && pops -> disconn_ind ){ pops -> disconn_ind(cprot); return 1; } break;#endif /* CONFIG_ISDN_X25 */ case ISDN_STAT_BCONN: /* B-Channel is up */ isdn_net_zero_frame_cnt(lp); switch (lp->dialstate) { case 5: case 6: case 7: case 8:
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -