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

📄 isdn_tty.c

📁 讲述linux的初始化过程
💻 C
📖 第 1 页 / 共 5 页
字号:
/* $Id: isdn_tty.c,v 1.94 2000/11/25 17:00:59 kai Exp $ * Linux ISDN subsystem, tty functions and AT-command emulator (linklevel). * * Copyright 1994-1999  by Fritz Elfert (fritz@isdn4linux.de) * Copyright 1995,96    by Thinking Objects Software GmbH Wuerzburg * * 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. * */#undef ISDN_TTY_STAT_DEBUG#define __NO_VERSION__#include <linux/config.h>#include <linux/module.h>#include <linux/isdn.h>#include "isdn_common.h"#include "isdn_tty.h"#ifdef CONFIG_ISDN_AUDIO#include "isdn_audio.h"#define VBUF 0x3e0#define VBUFX (VBUF/16)#endif#define FIX_FILE_TRANSFER#define	DUMMY_HAYES_AT/* Prototypes */static int isdn_tty_edit_at(const char *, int, modem_info *, int);static void isdn_tty_check_esc(const u_char *, u_char, int, int *, int *, int);static void isdn_tty_modem_reset_regs(modem_info *, int);static void isdn_tty_cmd_ATA(modem_info *);static void isdn_tty_flush_buffer(struct tty_struct *);static void isdn_tty_modem_result(int, modem_info *);#ifdef CONFIG_ISDN_AUDIOstatic int isdn_tty_countDLE(unsigned char *, int);#endif/* Leave this unchanged unless you know what you do! */#define MODEM_PARANOIA_CHECK#define MODEM_DO_RESTART#ifdef CONFIG_DEVFS_FSstatic char *isdn_ttyname_ttyI = "isdn/ttyI%d";static char *isdn_ttyname_cui = "isdn/cui%d";#elsestatic char *isdn_ttyname_ttyI = "ttyI";static char *isdn_ttyname_cui = "cui";#endifstatic int bit2si[8] ={1, 5, 7, 7, 7, 7, 7, 7};static int si2bit[8] ={4, 1, 4, 4, 4, 4, 4, 4};char *isdn_tty_revision = "$Revision: 1.94 $";/* isdn_tty_try_read() is called from within isdn_tty_rcv_skb() * to stuff incoming data directly into a tty's flip-buffer. This * is done to speed up tty-receiving if the receive-queue is empty. * This routine MUST be called with interrupts off. * Return: *  1 = Success *  0 = Failure, data has to be buffered and later processed by *      isdn_tty_readmodem(). */static intisdn_tty_try_read(modem_info * info, struct sk_buff *skb){	int c;	int len;	struct tty_struct *tty;	if (info->online) {		if ((tty = info->tty)) {			if (info->mcr & UART_MCR_RTS) {				c = TTY_FLIPBUF_SIZE - tty->flip.count;				len = skb->len#ifdef CONFIG_ISDN_AUDIO					+ ISDN_AUDIO_SKB_DLECOUNT(skb)#endif					;				if (c >= len) {#ifdef CONFIG_ISDN_AUDIO					if (ISDN_AUDIO_SKB_DLECOUNT(skb))						while (skb->len--) {							if (*skb->data == DLE)								tty_insert_flip_char(tty, DLE, 0);							tty_insert_flip_char(tty, *skb->data++, 0);					} else {#endif						memcpy(tty->flip.char_buf_ptr,						       skb->data, len);						tty->flip.count += len;						tty->flip.char_buf_ptr += len;						memset(tty->flip.flag_buf_ptr, 0, len);						tty->flip.flag_buf_ptr += len;#ifdef CONFIG_ISDN_AUDIO					}#endif					if (info->emu.mdmreg[REG_CPPP] & BIT_CPPP)						tty->flip.flag_buf_ptr[len - 1] = 0xff;					queue_task(&tty->flip.tqueue, &tq_timer);					kfree_skb(skb);					return 1;				}			}		}	}	return 0;}/* isdn_tty_readmodem() is called periodically from within timer-interrupt. * It tries getting received data from the receive queue an stuff it into * the tty's flip-buffer. */voidisdn_tty_readmodem(void){	int resched = 0;	int midx;	int i;	int c;	int r;	ulong flags;	struct tty_struct *tty;	modem_info *info;	for (i = 0; i < ISDN_MAX_CHANNELS; i++) {		if ((midx = dev->m_idx[i]) >= 0) {			info = &dev->mdm.info[midx];			if (info->online) {				r = 0;#ifdef CONFIG_ISDN_AUDIO				isdn_audio_eval_dtmf(info);				if ((info->vonline & 1) && (info->emu.vpar[1]))					isdn_audio_eval_silence(info);#endif				if ((tty = info->tty)) {					if (info->mcr & UART_MCR_RTS) {						c = TTY_FLIPBUF_SIZE - tty->flip.count;						if (c > 0) {							save_flags(flags);							cli();							r = isdn_readbchan(info->isdn_driver, info->isdn_channel,									   tty->flip.char_buf_ptr,									   tty->flip.flag_buf_ptr, c, 0);							/* CISCO AsyncPPP Hack */							if (!(info->emu.mdmreg[REG_CPPP] & BIT_CPPP))								memset(tty->flip.flag_buf_ptr, 0, r);							tty->flip.count += r;							tty->flip.flag_buf_ptr += r;							tty->flip.char_buf_ptr += r;							if (r)								queue_task(&tty->flip.tqueue, &tq_timer);							restore_flags(flags);						}					} else						r = 1;				} else					r = 1;				if (r) {					info->rcvsched = 0;					resched = 1;				} else					info->rcvsched = 1;			}		}	}	if (!resched)		isdn_timer_ctrl(ISDN_TIMER_MODEMREAD, 0);}intisdn_tty_rcv_skb(int i, int di, int channel, struct sk_buff *skb){	ulong flags;	int midx;#ifdef CONFIG_ISDN_AUDIO	int ifmt;#endif	modem_info *info;	if ((midx = dev->m_idx[i]) < 0) {		/* if midx is invalid, packet is not for tty */		return 0;	}	info = &dev->mdm.info[midx];#ifdef CONFIG_ISDN_AUDIO	ifmt = 1;		if ((info->vonline) && (!info->emu.vpar[4]))		isdn_audio_calc_dtmf(info, skb->data, skb->len, ifmt);	if ((info->vonline & 1) && (info->emu.vpar[1]))		isdn_audio_calc_silence(info, skb->data, skb->len, ifmt);#endif	if ((info->online < 2)#ifdef CONFIG_ISDN_AUDIO	    && (!(info->vonline & 1))#endif		) {		/* If Modem not listening, drop data */		kfree_skb(skb);		return 1;	}	if (info->emu.mdmreg[REG_T70] & BIT_T70) {		if (info->emu.mdmreg[REG_T70] & BIT_T70_EXT) {			/* T.70 decoding: throw away the T.70 header (2 or 4 bytes)   */			if (skb->data[0] == 3) /* pure data packet -> 4 byte headers  */				skb_pull(skb, 4);			else				if (skb->data[0] == 1) /* keepalive packet -> 2 byte hdr  */					skb_pull(skb, 2);		} else			/* T.70 decoding: Simply throw away the T.70 header (4 bytes) */			if ((skb->data[0] == 1) && ((skb->data[1] == 0) || (skb->data[1] == 1)))				skb_pull(skb, 4);	}#ifdef CONFIG_ISDN_AUDIO	if (skb_headroom(skb) < sizeof(isdn_audio_skb)) {		printk(KERN_WARNING		       "isdn_audio: insufficient skb_headroom, dropping\n");		kfree_skb(skb);		return 1;	}	ISDN_AUDIO_SKB_DLECOUNT(skb) = 0;	ISDN_AUDIO_SKB_LOCK(skb) = 0;	if (info->vonline & 1) {		/* voice conversion/compression */		switch (info->emu.vpar[3]) {			case 2:			case 3:			case 4:				/* adpcm				 * Since compressed data takes less				 * space, we can overwrite the buffer.				 */				skb_trim(skb, isdn_audio_xlaw2adpcm(info->adpcmr,								    ifmt,								    skb->data,								    skb->data,								    skb->len));				break;			case 5:				/* a-law */				if (!ifmt)					isdn_audio_ulaw2alaw(skb->data, skb->len);				break;			case 6:				/* u-law */				if (ifmt)					isdn_audio_alaw2ulaw(skb->data, skb->len);				break;		}		ISDN_AUDIO_SKB_DLECOUNT(skb) =			isdn_tty_countDLE(skb->data, skb->len);	}#ifdef CONFIG_ISDN_TTY_FAX	else {		if (info->faxonline & 2) {			isdn_tty_fax_bitorder(info, skb);			ISDN_AUDIO_SKB_DLECOUNT(skb) =				isdn_tty_countDLE(skb->data, skb->len);		}	}#endif#endif	/* Try to deliver directly via tty-flip-buf if queue is empty */	save_flags(flags);	cli();	if (skb_queue_empty(&dev->drv[di]->rpqueue[channel]))		if (isdn_tty_try_read(info, skb)) {			restore_flags(flags);			return 1;		}	/* Direct deliver failed or queue wasn't empty.	 * Queue up for later dequeueing via timer-irq.	 */	__skb_queue_tail(&dev->drv[di]->rpqueue[channel], skb);	dev->drv[di]->rcvcount[channel] +=		(skb->len#ifdef CONFIG_ISDN_AUDIO		 + ISDN_AUDIO_SKB_DLECOUNT(skb)#endif			);	restore_flags(flags);	/* Schedule dequeuing */	if ((dev->modempoll) && (info->rcvsched))		isdn_timer_ctrl(ISDN_TIMER_MODEMREAD, 1);	return 1;}voidisdn_tty_cleanup_xmit(modem_info * info){	struct sk_buff *skb;	unsigned long flags;	save_flags(flags);	cli();	if (skb_queue_len(&info->xmit_queue))		while ((skb = skb_dequeue(&info->xmit_queue)))			kfree_skb(skb);#ifdef CONFIG_ISDN_AUDIO	if (skb_queue_len(&info->dtmf_queue))		while ((skb = skb_dequeue(&info->dtmf_queue)))			kfree_skb(skb);#endif	restore_flags(flags);}static voidisdn_tty_tint(modem_info * info){	struct sk_buff *skb = skb_dequeue(&info->xmit_queue);	int len,	 slen;	if (!skb)		return;	len = skb->len;	if ((slen = isdn_writebuf_skb_stub(info->isdn_driver,					   info->isdn_channel, 1, skb)) == len) {		struct tty_struct *tty = info->tty;		info->send_outstanding++;		info->msr &= ~UART_MSR_CTS;		info->lsr &= ~UART_LSR_TEMT;		if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&		    tty->ldisc.write_wakeup)			(tty->ldisc.write_wakeup) (tty);		wake_up_interruptible(&tty->write_wait);		return;	}	if (slen < 0) {		/* Error: no channel, already shutdown, or wrong parameter */		dev_kfree_skb(skb);		return;	}	skb_queue_head(&info->xmit_queue, skb);}#ifdef CONFIG_ISDN_AUDIOstatic intisdn_tty_countDLE(unsigned char *buf, int len){	int count = 0;	while (len--)		if (*buf++ == DLE)			count++;	return count;}/* This routine is called from within isdn_tty_write() to perform * DLE-decoding when sending audio-data. */static intisdn_tty_handleDLEdown(modem_info * info, atemu * m, int len){	unsigned char *p = &info->xmit_buf[info->xmit_count];	int count = 0;	while (len > 0) {		if (m->lastDLE) {			m->lastDLE = 0;			switch (*p) {				case DLE:					/* Escape code */					if (len > 1)						memmove(p, p + 1, len - 1);					p--;					count++;					break;				case ETX:					/* End of data */					info->vonline |= 4;					return count;				case DC4:					/* Abort RX */					info->vonline &= ~1;#ifdef ISDN_DEBUG_MODEM_VOICE					printk(KERN_DEBUG					       "DLEdown: got DLE-DC4, send DLE-ETX on ttyI%d\n",					       info->line);#endif					isdn_tty_at_cout("\020\003", info);					if (!info->vonline) {#ifdef ISDN_DEBUG_MODEM_VOICE						printk(KERN_DEBUG						       "DLEdown: send VCON on ttyI%d\n",						       info->line);#endif						isdn_tty_at_cout("\r\nVCON\r\n", info);					}					/* Fall through */				case 'q':				case 's':					/* Silence */					if (len > 1)						memmove(p, p + 1, len - 1);					p--;					break;			}		} else {			if (*p == DLE)				m->lastDLE = 1;			else				count++;		}		p++;		len--;	}	if (len < 0) {		printk(KERN_WARNING "isdn_tty: len<0 in DLEdown\n");		return 0;	}	return count;}/* This routine is called from within isdn_tty_write() when receiving * audio-data. It interrupts receiving, if an character other than * ^S or ^Q is sent. */static intisdn_tty_end_vrx(const char *buf, int c, int from_user){	char ch;	while (c--) {		if (from_user)			get_user(ch, buf);		else			ch = *buf;		if ((ch != 0x11) && (ch != 0x13))			return 1;		buf++;	}	return 0;}static int voice_cf[7] ={0, 0, 4, 3, 2, 0, 0};#endif                          /* CONFIG_ISDN_AUDIO *//* isdn_tty_senddown() is called either directly from within isdn_tty_write() * or via timer-interrupt from within isdn_tty_modem_xmit(). It pulls * outgoing data from the tty's xmit-buffer, handles voice-decompression or * T.70 if necessary, and finally queues it up for sending via isdn_tty_tint. */static voidisdn_tty_senddown(modem_info * info){	int buflen;	int skb_res;#ifdef CONFIG_ISDN_AUDIO	int audio_len;#endif	struct sk_buff *skb;#ifdef CONFIG_ISDN_AUDIO	if (info->vonline & 4) {		info->vonline &= ~6;		if (!info->vonline) {#ifdef ISDN_DEBUG_MODEM_VOICE			printk(KERN_DEBUG			       "senddown: send VCON on ttyI%d\n",			       info->line);#endif			isdn_tty_at_cout("\r\nVCON\r\n", info);		}	}#endif	if (!(buflen = info->xmit_count))		return; 	if ((info->emu.mdmreg[REG_CTS] & BIT_CTS) != 0)		info->msr &= ~UART_MSR_CTS;	info->lsr &= ~UART_LSR_TEMT;		/* info->xmit_count is modified here and in isdn_tty_write().	 * So we return here if isdn_tty_write() is in the	 * critical section.	 */	atomic_inc(&info->xmit_lock);	if (!(atomic_dec_and_test(&info->xmit_lock)))

⌨️ 快捷键说明

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