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

📄 isdnloop.c

📁 linux-2.6.15.6
💻 C
📖 第 1 页 / 共 3 页
字号:
/* $Id: isdnloop.c,v 1.11.6.7 2001/11/11 19:54:31 kai Exp $ * * ISDN low-level module implementing a dummy loop driver. * * Copyright 1997 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 <linux/config.h>#include <linux/module.h>#include <linux/interrupt.h>#include <linux/init.h>#include <linux/sched.h>#include "isdnloop.h"static char *revision = "$Revision: 1.11.6.7 $";static char *isdnloop_id = "loop0";MODULE_DESCRIPTION("ISDN4Linux: Pseudo Driver that simulates an ISDN card");MODULE_AUTHOR("Fritz Elfert");MODULE_LICENSE("GPL");MODULE_PARM(isdnloop_id, "s");MODULE_PARM_DESC(isdnloop_id, "ID-String of first card");static int isdnloop_addcard(char *);/* * Free queue completely. * * Parameter: *   card    = pointer to card struct *   channel = channel number */static voidisdnloop_free_queue(isdnloop_card * card, int channel){	struct sk_buff_head *queue = &card->bqueue[channel];	skb_queue_purge(queue);	card->sndcount[channel] = 0;}/* * Send B-Channel data to another virtual card. * This routine is called via timer-callback from isdnloop_pollbchan(). * * Parameter: *   card = pointer to card struct. *   ch   = channel number (0-based) */static voidisdnloop_bchan_send(isdnloop_card * card, int ch){	isdnloop_card *rcard = card->rcard[ch];	int rch = card->rch[ch], len, ack;	struct sk_buff *skb;	isdn_ctrl cmd;	while (card->sndcount[ch]) {		if ((skb = skb_dequeue(&card->bqueue[ch]))) {			len = skb->len;			card->sndcount[ch] -= len;			ack = *(skb->head); /* used as scratch area */			cmd.driver = card->myid;			cmd.arg = ch;			if (rcard){				rcard->interface.rcvcallb_skb(rcard->myid, rch, skb);			} else {				printk(KERN_WARNING "isdnloop: no rcard, skb dropped\n");				dev_kfree_skb(skb);			};			cmd.command = ISDN_STAT_BSENT;			cmd.parm.length = len;			card->interface.statcallb(&cmd);		} else			card->sndcount[ch] = 0;	}}/* * Send/Receive Data to/from the B-Channel. * This routine is called via timer-callback. * It schedules itself while any B-Channel is open. * * Parameter: *   data = pointer to card struct, set by kernel timer.data */static voidisdnloop_pollbchan(unsigned long data){	isdnloop_card *card = (isdnloop_card *) data;	unsigned long flags;	if (card->flags & ISDNLOOP_FLAGS_B1ACTIVE)		isdnloop_bchan_send(card, 0);	if (card->flags & ISDNLOOP_FLAGS_B2ACTIVE)		isdnloop_bchan_send(card, 1);	if (card->flags & (ISDNLOOP_FLAGS_B1ACTIVE | ISDNLOOP_FLAGS_B2ACTIVE)) {		/* schedule b-channel polling again */		save_flags(flags);		cli();		card->rb_timer.expires = jiffies + ISDNLOOP_TIMER_BCREAD;		add_timer(&card->rb_timer);		card->flags |= ISDNLOOP_FLAGS_RBTIMER;		restore_flags(flags);	} else		card->flags &= ~ISDNLOOP_FLAGS_RBTIMER;}/* * Parse ICN-type setup string and fill fields of setup-struct * with parsed data. * * Parameter: *   setup = setup string, format: [caller-id],si1,si2,[called-id] *   cmd   = pointer to struct to be filled. */static voidisdnloop_parse_setup(char *setup, isdn_ctrl * cmd){	char *t = setup;	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;}typedef struct isdnloop_stat {	char *statstr;	int command;	int action;} isdnloop_stat;/* *INDENT-OFF* */static isdnloop_stat isdnloop_stat_table[] ={	{"BCON_",          ISDN_STAT_BCONN, 1}, /* B-Channel connected        */	{"BDIS_",          ISDN_STAT_BHUP,  2}, /* B-Channel disconnected     */	{"DCON_",          ISDN_STAT_DCONN, 0}, /* D-Channel connected        */	{"DDIS_",          ISDN_STAT_DHUP,  0}, /* 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* *//* * Parse Status message-strings from virtual card. * Depending on status, call statcallb for sending messages to upper * levels. Also set/reset B-Channel active-flags. * * Parameter: *   status  = status string to parse. *   channel = channel where message comes from. *   card    = card where message comes from. */static voidisdnloop_parse_status(u_char * status, int channel, isdnloop_card * card){	isdnloop_stat *s = isdnloop_stat_table;	int action = -1;	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 1:			/* BCON_x */			card->flags |= (channel) ?			    ISDNLOOP_FLAGS_B2ACTIVE : ISDNLOOP_FLAGS_B1ACTIVE;			break;		case 2:			/* BDIS_x */			card->flags &= ~((channel) ?					 ISDNLOOP_FLAGS_B2ACTIVE : ISDNLOOP_FLAGS_B1ACTIVE);			isdnloop_free_queue(card, channel);			break;		case 3:			/* DCAL_I and DSCA_I */			isdnloop_parse_setup(status + 6, &cmd);			break;		case 4:			/* FCALL */			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:			/* CIF */			strlcpy(cmd.parm.num, status + 3, sizeof(cmd.parm.num));			break;		case 6:			/* AOC */			snprintf(cmd.parm.num, sizeof(cmd.parm.num), "%d",			     (int) simple_strtoul(status + 7, NULL, 16));			break;		case 7:			/* CAU */			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:			/* Misc Errors on L1 and L2 */			card->flags &= ~ISDNLOOP_FLAGS_B1ACTIVE;			isdnloop_free_queue(card, 0);			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 &= ~ISDNLOOP_FLAGS_B2ACTIVE;			isdnloop_free_queue(card, 1);			cmd.arg = 1;			cmd.driver = card->myid;			card->interface.statcallb(&cmd);			cmd.command = ISDN_STAT_DHUP;			cmd.arg = 1;			cmd.driver = card->myid;			break;	}	card->interface.statcallb(&cmd);}/* * Store a cwcharacter into ringbuffer for reading from /dev/isdnctrl * * Parameter: *   card = pointer to card struct. *   c    = char to store. */static voidisdnloop_putmsg(isdnloop_card * card, unsigned char c){	ulong flags;	save_flags(flags);	cli();	*card->msg_buf_write++ = (c == 0xff) ? '\n' : c;	if (card->msg_buf_write == card->msg_buf_read) {		if (++card->msg_buf_read > card->msg_buf_end)			card->msg_buf_read = card->msg_buf;	}	if (card->msg_buf_write > card->msg_buf_end)		card->msg_buf_write = card->msg_buf;	restore_flags(flags);}/* * Poll a virtual cards message queue. * If there are new status-replies from the card, copy them to * ringbuffer for reading on /dev/isdnctrl and call * isdnloop_parse_status() for processing them. Watch for special * Firmware bootmessage and parse it, to get the D-Channel protocol. * If there are B-Channels open, initiate a timer-callback to * isdnloop_pollbchan(). * This routine is called periodically via timer interrupt. * * Parameter: *   data = pointer to card struct */static voidisdnloop_polldchan(unsigned long data){	isdnloop_card *card = (isdnloop_card *) data;	struct sk_buff *skb;	int avail;	int left;	u_char c;	int ch;	unsigned long flags;	u_char *p;	isdn_ctrl cmd;	if ((skb = skb_dequeue(&card->dqueue)))		avail = skb->len;	else		avail = 0;	for (left = avail; left > 0; left--) {		c = *skb->data;		skb_pull(skb, 1);		isdnloop_putmsg(card, c);		card->imsg[card->iptr] = c;		if (card->iptr < 59)			card->iptr++;		if (!skb->len) {			avail++;			isdnloop_putmsg(card, '\n');			card->imsg[card->iptr] = 0;			card->iptr = 0;			if (card->imsg[0] == '0' && card->imsg[1] >= '0' &&			  card->imsg[1] <= '2' && card->imsg[2] == ';') {				ch = (card->imsg[1] - '0') - 1;				p = &card->imsg[3];				isdnloop_parse_status(p, ch, card);			} else {				p = card->imsg;				if (!strncmp(p, "DRV1.", 5)) {					printk(KERN_INFO "isdnloop: (%s) %s\n", CID, p);					if (!strncmp(p + 7, "TC", 2)) {						card->ptype = ISDN_PTYPE_1TR6;						card->interface.features |= ISDN_FEATURE_P_1TR6;						printk(KERN_INFO						       "isdnloop: (%s) 1TR6-Protocol loaded and running\n", CID);					}					if (!strncmp(p + 7, "EC", 2)) {						card->ptype = ISDN_PTYPE_EURO;						card->interface.features |= ISDN_FEATURE_P_EURO;						printk(KERN_INFO						       "isdnloop: (%s) Euro-Protocol loaded and running\n", CID);					}					continue;				}			}		}	}	if (avail) {		cmd.command = ISDN_STAT_STAVAIL;		cmd.driver = card->myid;		cmd.arg = avail;		card->interface.statcallb(&cmd);	}	if (card->flags & (ISDNLOOP_FLAGS_B1ACTIVE | ISDNLOOP_FLAGS_B2ACTIVE))		if (!(card->flags & ISDNLOOP_FLAGS_RBTIMER)) {			/* schedule b-channel polling */			card->flags |= ISDNLOOP_FLAGS_RBTIMER;			save_flags(flags);			cli();			del_timer(&card->rb_timer);			card->rb_timer.function = isdnloop_pollbchan;			card->rb_timer.data = (unsigned long) card;			card->rb_timer.expires = jiffies + ISDNLOOP_TIMER_BCREAD;			add_timer(&card->rb_timer);			restore_flags(flags);		}	/* schedule again */	save_flags(flags);	cli();	card->st_timer.expires = jiffies + ISDNLOOP_TIMER_DCREAD;	add_timer(&card->st_timer);	restore_flags(flags);}/* * Append a packet to the transmit buffer-queue. * * Parameter: *   channel = Number of B-channel *   skb     = packet to send. *   card    = pointer to card-struct * Return: *   Number of bytes transferred, -E??? on error */static intisdnloop_sendbuf(int channel, struct sk_buff *skb, isdnloop_card * card){	int len = skb->len;	unsigned long flags;	struct sk_buff *nskb;	if (len > 4000) {		printk(KERN_WARNING		       "isdnloop: Send packet too large\n");		return -EINVAL;	}	if (len) {		if (!(card->flags & (channel) ? ISDNLOOP_FLAGS_B2ACTIVE : ISDNLOOP_FLAGS_B1ACTIVE))			return 0;		if (card->sndcount[channel] > ISDNLOOP_MAX_SQUEUE)			return 0;		save_flags(flags);		cli();		nskb = dev_alloc_skb(skb->len);		if (nskb) {			memcpy(skb_put(nskb, len), skb->data, len);			skb_queue_tail(&card->bqueue[channel], nskb);			dev_kfree_skb(skb);		} else			len = 0;		card->sndcount[channel] += len;		restore_flags(flags);	}	return len;}/* * Read the messages from the card's ringbuffer * * Parameter: *   buf  = pointer to buffer. *   len  = number of bytes to read. *   user = flag, 1: called from userlevel 0: called from kernel. *   card = pointer to card struct. * Return: *   number of bytes actually transferred. */static intisdnloop_readstatus(u_char __user *buf, int len, isdnloop_card * card){	int count;	u_char __user *p;	for (p = buf, count = 0; count < len; p++, count++) {		if (card->msg_buf_read == card->msg_buf_write)			return count;		put_user(*card->msg_buf_read++, p);		if (card->msg_buf_read > card->msg_buf_end)			card->msg_buf_read = card->msg_buf;	}	return count;}/* * Simulate a card's response by appending it to the cards * message queue. * * Parameter: *   card = pointer to card struct. *   s    = pointer to message-string. *   ch   = channel: 0 = generic messages, 1 and 2 = D-channel messages. * Return: *   0 on success, 1 on memory squeeze. */static intisdnloop_fake(isdnloop_card * card, char *s, int ch){	struct sk_buff *skb;	int len = strlen(s) + ((ch >= 0) ? 3 : 0);	if (!(skb = dev_alloc_skb(len))) {		printk(KERN_WARNING "isdnloop: Out of memory in isdnloop_fake\n");		return 1;	}	if (ch >= 0)		sprintf(skb_put(skb, 3), "%02d;", ch);	memcpy(skb_put(skb, strlen(s)), s, strlen(s));	skb_queue_tail(&card->dqueue, skb);	return 0;}/* *INDENT-OFF* */static isdnloop_stat isdnloop_cmd_table[] ={	{"BCON_R",         0,  1},	/* B-Channel connect        */	{"BCON_I",         0, 17},	/* B-Channel connect ind    */	{"BDIS_R",         0,  2},	/* B-Channel disconnect     */	{"DDIS_R",         0,  3},	/* D-Channel disconnect     */	{"DCON_R",         0, 16},	/* D-Channel connect        */	{"DSCA_R",         0,  4},	/* Dial 1TR6-SPV     */	{"DCAL_R",         0,  5},	/* Dial */	{"EAZC",           0,  6},	/* Clear EAZ listener */	{"EAZ",            0,  7},	/* Set EAZ listener */	{"SEEAZ",          0,  8},	/* Get EAZ listener */	{"MSN",            0,  9},	/* Set/Clear MSN listener */	{"MSALL",          0, 10},	/* Set multi MSN listeners */	{"SETSIL",         0, 11},	/* Set SI list     */	{"SEESIL",         0, 12},	/* Get SI list     */	{"SILC",           0, 13},	/* Clear SI list     */	{"LOCK",           0, -1},	/* LOCK channel     */	{"UNLOCK",         0, -1},	/* UNLOCK channel     */	{"FV2ON",          1, 14},	/* Leased mode on               */	{"FV2OFF",         1, 15},	/* Leased mode off              */	{NULL, 0, -1}};/* *INDENT-ON* *//* * Simulate an error-response from a card. *

⌨️ 快捷键说明

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