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

📄 icn.c

📁 Linux内核源代码 为压缩文件 是<<Linux内核>>一书中的源代码
💻 C
📖 第 1 页 / 共 3 页
字号:
/* $Id: icn.c,v 1.65 2000/11/13 22:51:48 kai Exp $ * ISDN low-level module for the ICN active ISDN-Card. * * Copyright 1994,95,96 by Fritz Elfert (fritz@isdn4linux.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 "icn.h"/* * Verbose bootcode- and protocol-downloading. */#undef BOOT_DEBUG/* * Verbose Shmem-Mapping. */#undef MAP_DEBUGstatic char*revision = "$Revision: 1.65 $";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;	unsigned long flags;	while ((skb = skb_dequeue(queue)))		dev_kfree_skb(skb);	save_flags(flags);	cli();	card->xlen[channel] = 0;	card->sndcount[channel] = 0;	if ((skb = card->xskb[channel])) {		card->xskb[channel] = NULL;		restore_flags(flags);		dev_kfree_skb(skb);	} else		restore_flags(flags);}/* 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) */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. */static inline inticn_lock_channel(icn_card * card, int channel){	register int retval;	ulong flags;#ifdef MAP_DEBUG	printk(KERN_DEBUG "icn_lock_channel %d\n", channel);#endif	save_flags(flags);	cli();	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	}	restore_flags(flags);	return retval;}/* * Release current card/channel lock */static inline voidicn_release_channel(void){	ulong flags;#ifdef MAP_DEBUG	printk(KERN_DEBUG "icn_release_channel l=%d\n", dev.chanlock);#endif	save_flags(flags);	cli();	if (dev.chanlock > 0)		dev.chanlock--;	restore_flags(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	save_flags(flags);	cli();	if ((!dev.chanlock) ||	    ((dev.channel == channel) && (dev.mcard == card))) {		dev.chanlock++;		icn_map_channel(card, channel);		restore_flags(flags);#ifdef MAP_DEBUG		printk(KERN_DEBUG "trymaplock %d OK\n", channel);#endif		return 1;	}	restore_flags(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	save_flags(flags);	cli();	if (dev.chanlock > 0)		dev.chanlock--;	if (!dev.chanlock)		icn_map_channel(card, channel);	restore_flags(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_len(&card->spqueue[channel])))		return;	if (icn_trymaplock_channel(card, mch)) {		while (sbfree && 		       (card->sndcount[channel] ||			skb_queue_len(&card->spqueue[channel]) ||			card->xskb[channel])) {			save_flags(flags);			cli();			if (card->xmit_lock[channel]) {				restore_flags(flags);				break;			}			card->xmit_lock[channel]++;			restore_flags(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);			card->sndcount[channel] -= cnt;			sbnext; /* switch to next buffer        */			icn_maprelease_channel(card, mch & 2);			if (!skb->len) {				save_flags(flags);				cli();				if (card->xskb[channel])					card->xskb[channel] = NULL;				restore_flags(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 {				save_flags(flags);				cli();				card->xskb[channel] = skb;				restore_flags(flags);			}			card->xmit_lock[channel] = 0;			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 */		save_flags(flags);		cli();		mod_timer(&card->rb_timer, jiffies+ICN_TIMER_BCREAD);		card->flags |= ICN_FLAGS_RBTIMER;		restore_flags(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 */	{"NO D-CHAN",      ISDN_STAT_NODCH, 0},	/* No D-channel available     */	{"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:			save_flags(flags);			cli();			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;				restore_flags(flags);				card->interface.statcallb(&cmd);			} else				restore_flags(flags);						break;		case 1:			icn_free_queue(card,channel);			card->flags |= (channel) ?			    ICN_FLAGS_B2ACTIVE : ICN_FLAGS_B1ACTIVE;			break;		case 2:			card->flags &= ~((channel) ?				ICN_FLAGS_B2ACTIVE : ICN_FLAGS_B1ACTIVE);			icn_free_queue(card, channel);			save_flags(flags);			cli();			card->rcvidx[channel] = 0;			restore_flags(flags);			break;		case 3:			{				char *t = status + 6;				char *s = strpbrk(t, ",");				*s++ = '\0';				strncpy(cmd.parm.setup.phone, t,					sizeof(cmd.parm.setup.phone));				s = strpbrk(t = s, ",");				*s++ = '\0';				if (!strlen(t))					cmd.parm.setup.si1 = 0;				else					cmd.parm.setup.si1 =					    simple_strtoul(t, NULL, 10);				s = strpbrk(t = s, ",");				*s++ = '\0';				if (!strlen(t))					cmd.parm.setup.si2 = 0;				else					cmd.parm.setup.si2 =					    simple_strtoul(t, NULL, 10);				strncpy(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:			strncpy(cmd.parm.num, status + 3, sizeof(cmd.parm.num) - 1);			break;		case 6:			sprintf(cmd.parm.num, "%d",			     (int) simple_strtoul(status + 7, NULL, 16));			break;		case 7:			status += 3;			if (strlen(status) == 4)				sprintf(cmd.parm.num, "%s%c%c",				     status + 2, *status, *(status + 1));			else				strncpy(cmd.parm.num, status + 1, sizeof(cmd.parm.num) - 1);			break;		case 8:			card->flags &= ~ICN_FLAGS_B1ACTIVE;			icn_free_queue(card, 0);			save_flags(flags);			cli();			card->rcvidx[0] = 0;			restore_flags(flags);			cmd.arg = 0;			cmd.driver = card->myid;			card->interface.statcallb(&cmd);			cmd.command = ISDN_STAT_DHUP;			cmd.arg = 0;			cmd.driver = card->myid;			card->interface.statcallb(&cmd);			cmd.command = ISDN_STAT_BHUP;			card->flags &= ~ICN_FLAGS_B2ACTIVE;			icn_free_queue(card, 1);			save_flags(flags);			cli();			card->rcvidx[1] = 0;			restore_flags(flags);			cmd.arg = 1;			cmd.driver = card->myid;			card->interface.statcallb(&cmd);			cmd.command = ISDN_STAT_DHUP;			cmd.arg = 1;			cmd.driver = card->myid;			break;	}

⌨️ 快捷键说明

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