📄 isdn_net.c
字号:
/* $Id: isdn_net.c,v 1.140.6.1 2000/12/10 22:01:04 kai 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 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>#define __NO_VERSION__#include <linux/module.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)) { queue_task(&lp->tqueue, &tq_immediate); } 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 */int isdn_net_force_dial_lp(isdn_net_local *);static int isdn_net_start_xmit(struct sk_buff *, struct net_device *);char *isdn_net_revision = "$Revision: 1.140.6.1 $"; /* * 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 ulong flags; /* not sure if the cli() is needed at all --KG */ save_flags(flags); cli(); /* Avoid glitch on writes to CMD regs */#ifdef CONFIG_ISDN_X25 if( cprot && cprot -> pops && dops ) cprot -> pops -> restart ( cprot, dev, dops );#endif restore_flags(flags);}/* 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_MOD_INC_USE_COUNT(); return 0;}/* * Assign an ISDN-channel to a net-interface */static voidisdn_net_bind_channel(isdn_net_local * lp, int idx){ ulong flags; save_flags(flags); cli(); 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; restore_flags(flags);}/* * unbind a net-interface (resets interface after an error) */static voidisdn_net_unbind_channel(isdn_net_local * lp){ ulong flags; struct sk_buff *skb; save_flags(flags); cli(); while ((skb = skb_dequeue(&lp->super_tx_queue))) { kfree_skb(skb); } 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; restore_flags(flags);}/* * 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. */unsigned long last_jiffies = -HZ;voidisdn_net_autohup(){ isdn_net_dev *p = dev->netdev; int anymore; anymore = 0; while (p) { isdn_net_local *l = p->local; if ((jiffies - last_jiffies) == 0) 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 (jiffies - l->chargetime > l->chargeint) l->chargetime += l->chargeint; if (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 (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 : 0;#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)) {#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;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -