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

📄 icn.c

📁 linux-2.6.15.6
💻 C
📖 第 1 页 / 共 3 页
字号:
/* $Id: icn.c,v 1.65.6.8 2001/09/23 22:24:55 kai Exp $ * * ISDN low-level module for the ICN active ISDN-Card. * * Copyright 1994,95,96 by Fritz Elfert (fritz@isdn4linux.de) * * This software may be used and distributed according to the terms * of the GNU General Public License, incorporated herein by reference. * */#include "icn.h"#include <linux/module.h>#include <linux/init.h>static int portbase = ICN_BASEADDR;static unsigned long membase = ICN_MEMADDR;static char *icn_id = "\0";static char *icn_id2 = "\0";MODULE_DESCRIPTION("ISDN4Linux: Driver for ICN active ISDN card");MODULE_AUTHOR("Fritz Elfert");MODULE_LICENSE("GPL");module_param(portbase, int, 0);MODULE_PARM_DESC(portbase, "Port address of first card");module_param(membase, ulong, 0);MODULE_PARM_DESC(membase, "Shared memory address of all cards");module_param(icn_id, charp, 0);MODULE_PARM_DESC(icn_id, "ID-String of first card");module_param(icn_id2, charp, 0);MODULE_PARM_DESC(icn_id2, "ID-String of first card, second S0 (4B only)");/* * Verbose bootcode- and protocol-downloading. */#undef BOOT_DEBUG/* * Verbose Shmem-Mapping. */#undef MAP_DEBUGstatic char*revision = "$Revision: 1.65.6.8 $";static int icn_addcard(int, char *, char *);/* * Free send-queue completely. * Parameter: *   card   = pointer to card struct *   channel = channel number */static voidicn_free_queue(icn_card * card, int channel){	struct sk_buff_head *queue = &card->spqueue[channel];	struct sk_buff *skb;	skb_queue_purge(queue);	card->xlen[channel] = 0;	card->sndcount[channel] = 0;	if ((skb = card->xskb[channel])) {		card->xskb[channel] = NULL;		dev_kfree_skb(skb);	}}/* Put a value into a shift-register, highest bit first. * Parameters: *            port     = port for output (bit 0 is significant) *            val      = value to be output *            firstbit = Bit-Number of highest bit *            bitcount = Number of bits to output */static inline voidicn_shiftout(unsigned short port,	     unsigned long val,	     int firstbit,	     int bitcount){	register u_char s;	register u_char c;	for (s = firstbit, c = bitcount; c > 0; s--, c--)		OUTB_P((u_char) ((val >> s) & 1) ? 0xff : 0, port);}/* * disable a cards shared memory */static inline voidicn_disable_ram(icn_card * card){	OUTB_P(0, ICN_MAPRAM);}/* * enable a cards shared memory */static inline voidicn_enable_ram(icn_card * card){	OUTB_P(0xff, ICN_MAPRAM);}/* * Map a cards channel0 (Bank0/Bank8) or channel1 (Bank4/Bank12) * * must called with holding the devlock */static inline voidicn_map_channel(icn_card * card, int channel){#ifdef MAP_DEBUG	printk(KERN_DEBUG "icn_map_channel %d %d\n", dev.channel, channel);#endif	if ((channel == dev.channel) && (card == dev.mcard))		return;	if (dev.mcard)		icn_disable_ram(dev.mcard);	icn_shiftout(ICN_BANK, chan2bank[channel], 3, 4);	/* Select Bank          */	icn_enable_ram(card);	dev.mcard = card;	dev.channel = channel;#ifdef MAP_DEBUG	printk(KERN_DEBUG "icn_map_channel done\n");#endif}/* * Lock a cards channel. * Return 0 if requested card/channel is unmapped (failure). * Return 1 on success. * * must called with holding the devlock */static inline inticn_lock_channel(icn_card * card, int channel){	register int retval;#ifdef MAP_DEBUG	printk(KERN_DEBUG "icn_lock_channel %d\n", channel);#endif	if ((dev.channel == channel) && (card == dev.mcard)) {		dev.chanlock++;		retval = 1;#ifdef MAP_DEBUG		printk(KERN_DEBUG "icn_lock_channel %d OK\n", channel);#endif	} else {		retval = 0;#ifdef MAP_DEBUG		printk(KERN_DEBUG "icn_lock_channel %d FAILED, dc=%d\n", channel, dev.channel);#endif	}	return retval;}/* * Release current card/channel lock * * must called with holding the devlock */static inline void__icn_release_channel(void){#ifdef MAP_DEBUG	printk(KERN_DEBUG "icn_release_channel l=%d\n", dev.chanlock);#endif	if (dev.chanlock > 0)		dev.chanlock--;}/* * Release current card/channel lock */static inline voidicn_release_channel(void){	ulong flags;	spin_lock_irqsave(&dev.devlock, flags);	__icn_release_channel();	spin_unlock_irqrestore(&dev.devlock, flags);}/* * Try to map and lock a cards channel. * Return 1 on success, 0 on failure. */static inline inticn_trymaplock_channel(icn_card * card, int channel){	ulong flags;#ifdef MAP_DEBUG	printk(KERN_DEBUG "trymaplock c=%d dc=%d l=%d\n", channel, dev.channel,	       dev.chanlock);#endif	spin_lock_irqsave(&dev.devlock, flags);	if ((!dev.chanlock) ||	    ((dev.channel == channel) && (dev.mcard == card))) {		dev.chanlock++;		icn_map_channel(card, channel);		spin_unlock_irqrestore(&dev.devlock, flags);#ifdef MAP_DEBUG		printk(KERN_DEBUG "trymaplock %d OK\n", channel);#endif		return 1;	}	spin_unlock_irqrestore(&dev.devlock, flags);#ifdef MAP_DEBUG	printk(KERN_DEBUG "trymaplock %d FAILED\n", channel);#endif	return 0;}/* * Release current card/channel lock, * then map same or other channel without locking. */static inline voidicn_maprelease_channel(icn_card * card, int channel){	ulong flags;#ifdef MAP_DEBUG	printk(KERN_DEBUG "map_release c=%d l=%d\n", channel, dev.chanlock);#endif	spin_lock_irqsave(&dev.devlock, flags);	if (dev.chanlock > 0)		dev.chanlock--;	if (!dev.chanlock)		icn_map_channel(card, channel);	spin_unlock_irqrestore(&dev.devlock, flags);}/* Get Data from the B-Channel, assemble fragmented packets and put them * into receive-queue. Wake up any B-Channel-reading processes. * This routine is called via timer-callback from icn_pollbchan(). */static voidicn_pollbchan_receive(int channel, icn_card * card){	int mch = channel + ((card->secondhalf) ? 2 : 0);	int eflag;	int cnt;	struct sk_buff *skb;	if (icn_trymaplock_channel(card, mch)) {		while (rbavl) {			cnt = readb(&rbuf_l);			if ((card->rcvidx[channel] + cnt) > 4000) {				printk(KERN_WARNING				       "icn: (%s) bogus packet on ch%d, dropping.\n",				       CID,				       channel + 1);				card->rcvidx[channel] = 0;				eflag = 0;			} else {				memcpy_fromio(&card->rcvbuf[channel][card->rcvidx[channel]],					      &rbuf_d, cnt);				card->rcvidx[channel] += cnt;				eflag = readb(&rbuf_f);			}			rbnext;			icn_maprelease_channel(card, mch & 2);			if (!eflag) {				if ((cnt = card->rcvidx[channel])) {					if (!(skb = dev_alloc_skb(cnt))) {						printk(KERN_WARNING "icn: receive out of memory\n");						break;					}					memcpy(skb_put(skb, cnt), card->rcvbuf[channel], cnt);					card->rcvidx[channel] = 0;					card->interface.rcvcallb_skb(card->myid, channel, skb);				}			}			if (!icn_trymaplock_channel(card, mch))				break;		}		icn_maprelease_channel(card, mch & 2);	}}/* Send data-packet to B-Channel, split it up into fragments of * ICN_FRAGSIZE length. If last fragment is sent out, signal * success to upper layers via statcallb with ISDN_STAT_BSENT argument. * This routine is called via timer-callback from icn_pollbchan() or * directly from icn_sendbuf(). */static voidicn_pollbchan_send(int channel, icn_card * card){	int mch = channel + ((card->secondhalf) ? 2 : 0);	int cnt;	unsigned long flags;	struct sk_buff *skb;	isdn_ctrl cmd;	if (!(card->sndcount[channel] || card->xskb[channel] ||	      !skb_queue_empty(&card->spqueue[channel])))		return;	if (icn_trymaplock_channel(card, mch)) {		while (sbfree && 		       (card->sndcount[channel] ||			!skb_queue_empty(&card->spqueue[channel]) ||			card->xskb[channel])) {			spin_lock_irqsave(&card->lock, flags);			if (card->xmit_lock[channel]) {				spin_unlock_irqrestore(&card->lock, flags);				break;			}			card->xmit_lock[channel]++;			spin_unlock_irqrestore(&card->lock, flags);			skb = card->xskb[channel];			if (!skb) {				skb = skb_dequeue(&card->spqueue[channel]);				if (skb) {					/* Pop ACK-flag off skb.					 * Store length to xlen.					 */					if (*(skb_pull(skb,1)))						card->xlen[channel] = skb->len;					else						card->xlen[channel] = 0;				}			}			if (!skb)				break;			if (skb->len > ICN_FRAGSIZE) {				writeb(0xff, &sbuf_f);				cnt = ICN_FRAGSIZE;			} else {				writeb(0x0, &sbuf_f);				cnt = skb->len;			}			writeb(cnt, &sbuf_l);			memcpy_toio(&sbuf_d, skb->data, cnt);			skb_pull(skb, cnt);			sbnext; /* switch to next buffer        */			icn_maprelease_channel(card, mch & 2);			spin_lock_irqsave(&card->lock, flags);			card->sndcount[channel] -= cnt;			if (!skb->len) {				if (card->xskb[channel])					card->xskb[channel] = NULL;				card->xmit_lock[channel] = 0;				spin_unlock_irqrestore(&card->lock, flags);				dev_kfree_skb(skb);				if (card->xlen[channel]) {					cmd.command = ISDN_STAT_BSENT;					cmd.driver = card->myid;					cmd.arg = channel;					cmd.parm.length = card->xlen[channel];					card->interface.statcallb(&cmd);				}			} else {				card->xskb[channel] = skb;				card->xmit_lock[channel] = 0;				spin_unlock_irqrestore(&card->lock, flags);			}			if (!icn_trymaplock_channel(card, mch))				break;		}		icn_maprelease_channel(card, mch & 2);	}}/* Send/Receive Data to/from the B-Channel. * This routine is called via timer-callback. * It schedules itself while any B-Channel is open. */static voidicn_pollbchan(unsigned long data){	icn_card *card = (icn_card *) data;	unsigned long flags;	if (card->flags & ICN_FLAGS_B1ACTIVE) {		icn_pollbchan_receive(0, card);		icn_pollbchan_send(0, card);	}	if (card->flags & ICN_FLAGS_B2ACTIVE) {		icn_pollbchan_receive(1, card);		icn_pollbchan_send(1, card);	}	if (card->flags & (ICN_FLAGS_B1ACTIVE | ICN_FLAGS_B2ACTIVE)) {		/* schedule b-channel polling again */		spin_lock_irqsave(&card->lock, flags);		mod_timer(&card->rb_timer, jiffies+ICN_TIMER_BCREAD);		card->flags |= ICN_FLAGS_RBTIMER;		spin_unlock_irqrestore(&card->lock, flags);	} else		card->flags &= ~ICN_FLAGS_RBTIMER;}typedef struct icn_stat {	char *statstr;	int command;	int action;} icn_stat;/* *INDENT-OFF* */static icn_stat icn_stat_table[] ={	{"BCON_",          ISDN_STAT_BCONN, 1},	/* B-Channel connected        */	{"BDIS_",          ISDN_STAT_BHUP,  2},	/* B-Channel disconnected     */	/*	** add d-channel connect and disconnect support to link-level	*/	{"DCON_",          ISDN_STAT_DCONN, 10},	/* D-Channel connected        */	{"DDIS_",          ISDN_STAT_DHUP,  11},	/* D-Channel disconnected     */	{"DCAL_I",         ISDN_STAT_ICALL, 3},	/* Incoming call dialup-line  */	{"DSCA_I",         ISDN_STAT_ICALL, 3},	/* Incoming call 1TR6-SPV     */	{"FCALL",          ISDN_STAT_ICALL, 4},	/* Leased line connection up  */	{"CIF",            ISDN_STAT_CINF,  5},	/* Charge-info, 1TR6-type     */	{"AOC",            ISDN_STAT_CINF,  6},	/* Charge-info, DSS1-type     */	{"CAU",            ISDN_STAT_CAUSE, 7},	/* Cause code                 */	{"TEI OK",         ISDN_STAT_RUN,   0},	/* Card connected to wallplug */	{"E_L1: ACT FAIL", ISDN_STAT_BHUP,  8},	/* Layer-1 activation failed  */	{"E_L2: DATA LIN", ISDN_STAT_BHUP,  8},	/* Layer-2 data link lost     */	{"E_L1: ACTIVATION FAILED",					   ISDN_STAT_BHUP,  8},	/* Layer-1 activation failed  */	{NULL, 0, -1}};/* *INDENT-ON* *//* * Check Statusqueue-Pointer from isdn-cards. * If there are new status-replies from the interface, check * them against B-Channel-connects/disconnects and set flags accordingly. * Wake-Up any processes, who are reading the status-device. * If there are B-Channels open, initiate a timer-callback to * icn_pollbchan(). * This routine is called periodically via timer. */static voidicn_parse_status(u_char * status, int channel, icn_card * card){	icn_stat *s = icn_stat_table;	int action = -1;	unsigned long flags;	isdn_ctrl cmd;	while (s->statstr) {		if (!strncmp(status, s->statstr, strlen(s->statstr))) {			cmd.command = s->command;			action = s->action;			break;		}		s++;	}	if (action == -1)		return;	cmd.driver = card->myid;	cmd.arg = channel;	switch (action) {		case 11:			spin_lock_irqsave(&card->lock, flags);			icn_free_queue(card,channel);			card->rcvidx[channel] = 0;			if (card->flags & 			    ((channel)?ICN_FLAGS_B2ACTIVE:ICN_FLAGS_B1ACTIVE)) {								isdn_ctrl ncmd;								card->flags &= ~((channel)?						 ICN_FLAGS_B2ACTIVE:ICN_FLAGS_B1ACTIVE);								memset(&ncmd, 0, sizeof(ncmd));								ncmd.driver = card->myid;				ncmd.arg = channel;				ncmd.command = ISDN_STAT_BHUP;				spin_unlock_irqrestore(&card->lock, flags);				card->interface.statcallb(&cmd);			} else				spin_unlock_irqrestore(&card->lock, flags);			break;		case 1:			spin_lock_irqsave(&card->lock, flags);			icn_free_queue(card,channel);			card->flags |= (channel) ?			    ICN_FLAGS_B2ACTIVE : ICN_FLAGS_B1ACTIVE;			spin_unlock_irqrestore(&card->lock, flags);			break;		case 2:			spin_lock_irqsave(&card->lock, flags);			card->flags &= ~((channel) ?				ICN_FLAGS_B2ACTIVE : ICN_FLAGS_B1ACTIVE);			icn_free_queue(card, channel);			card->rcvidx[channel] = 0;			spin_unlock_irqrestore(&card->lock, flags);			break;		case 3:			{				char *t = status + 6;				char *s = strchr(t, ',');				*s++ = '\0';				strlcpy(cmd.parm.setup.phone, t,					sizeof(cmd.parm.setup.phone));				s = strchr(t = s, ',');				*s++ = '\0';				if (!strlen(t))					cmd.parm.setup.si1 = 0;				else					cmd.parm.setup.si1 =					    simple_strtoul(t, NULL, 10);				s = strchr(t = s, ',');				*s++ = '\0';				if (!strlen(t))					cmd.parm.setup.si2 = 0;				else					cmd.parm.setup.si2 =					    simple_strtoul(t, NULL, 10);				strlcpy(cmd.parm.setup.eazmsn, s,					sizeof(cmd.parm.setup.eazmsn));			}			cmd.parm.setup.plan = 0;			cmd.parm.setup.screen = 0;			break;		case 4:			sprintf(cmd.parm.setup.phone, "LEASED%d", card->myid);			sprintf(cmd.parm.setup.eazmsn, "%d", channel + 1);			cmd.parm.setup.si1 = 7;			cmd.parm.setup.si2 = 0;			cmd.parm.setup.plan = 0;			cmd.parm.setup.screen = 0;			break;		case 5:			strlcpy(cmd.parm.num, status + 3, sizeof(cmd.parm.num));			break;		case 6:			snprintf(cmd.parm.num, sizeof(cmd.parm.num), "%d",			     (int) simple_strtoul(status + 7, NULL, 16));			break;		case 7:			status += 3;			if (strlen(status) == 4)				snprintf(cmd.parm.num, sizeof(cmd.parm.num), "%s%c%c",				     status + 2, *status, *(status + 1));			else				strlcpy(cmd.parm.num, status + 1, sizeof(cmd.parm.num));			break;		case 8:			spin_lock_irqsave(&card->lock, flags);			card->flags &= ~ICN_FLAGS_B1ACTIVE;			icn_free_queue(card, 0);			card->rcvidx[0] = 0;			spin_unlock_irqrestore(&card->lock, flags);			cmd.arg = 0;			cmd.driver = card->myid;			card->interface.statcallb(&cmd);			cmd.command = ISDN_STAT_DHUP;

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -