⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 isdn_net.c

📁 linux-2.6.15.6
💻 C
📖 第 1 页 / 共 5 页
字号:
/* $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 + -