ppp_async.c

来自「讲述linux的初始化过程」· C语言 代码 · 共 976 行 · 第 1/2 页

C
976
字号
	0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a,	0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1,	0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9,	0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330,	0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78};EXPORT_SYMBOL(ppp_crc16_table);#define fcstab	ppp_crc16_table		/* for PPP_FCS macro *//* * Procedure to encode the data for async serial transmission. * Does octet stuffing (escaping), puts the address/control bytes * on if A/C compression is disabled, and does protocol compression. * Assumes ap->tpkt != 0 on entry. * Returns 1 if we finished the current frame, 0 otherwise. */#define PUT_BYTE(ap, buf, c, islcp)	do {		\	if ((islcp && c < 0x20) || (ap->xaccm[c >> 5] & (1 << (c & 0x1f)))) {\		*buf++ = PPP_ESCAPE;			\		*buf++ = c ^ 0x20;			\	} else						\		*buf++ = c;				\} while (0)static intppp_async_encode(struct asyncppp *ap){	int fcs, i, count, c, proto;	unsigned char *buf, *buflim;	unsigned char *data;	int islcp;	buf = ap->obuf;	ap->olim = buf;	ap->optr = buf;	i = ap->tpkt_pos;	data = ap->tpkt->data;	count = ap->tpkt->len;	fcs = ap->tfcs;	proto = (data[0] << 8) + data[1];	/*	 * LCP packets with code values between 1 (configure-reqest)	 * and 7 (code-reject) must be sent as though no options	 * had been negotiated.	 */	islcp = proto == PPP_LCP && 1 <= data[2] && data[2] <= 7;	if (i == 0) {		if (islcp)			async_lcp_peek(ap, data, count, 0);		/*		 * Start of a new packet - insert the leading FLAG		 * character if necessary.		 */		if (islcp || flag_time == 0		    || jiffies - ap->last_xmit >= flag_time)			*buf++ = PPP_FLAG;		ap->last_xmit = jiffies;		fcs = PPP_INITFCS;		/*		 * Put in the address/control bytes if necessary		 */		if ((ap->flags & SC_COMP_AC) == 0 || islcp) {			PUT_BYTE(ap, buf, 0xff, islcp);			fcs = PPP_FCS(fcs, 0xff);			PUT_BYTE(ap, buf, 0x03, islcp);			fcs = PPP_FCS(fcs, 0x03);		}	}	/*	 * Once we put in the last byte, we need to put in the FCS	 * and closing flag, so make sure there is at least 7 bytes	 * of free space in the output buffer.	 */	buflim = ap->obuf + OBUFSIZE - 6;	while (i < count && buf < buflim) {		c = data[i++];		if (i == 1 && c == 0 && (ap->flags & SC_COMP_PROT))			continue;	/* compress protocol field */		fcs = PPP_FCS(fcs, c);		PUT_BYTE(ap, buf, c, islcp);	}	if (i < count) {		/*		 * Remember where we are up to in this packet.		 */		ap->olim = buf;		ap->tpkt_pos = i;		ap->tfcs = fcs;		return 0;	}	/*	 * We have finished the packet.  Add the FCS and flag.	 */	fcs = ~fcs;	c = fcs & 0xff;	PUT_BYTE(ap, buf, c, islcp);	c = (fcs >> 8) & 0xff;	PUT_BYTE(ap, buf, c, islcp);	*buf++ = PPP_FLAG;	ap->olim = buf;	kfree_skb(ap->tpkt);	ap->tpkt = 0;	return 1;}/* * Transmit-side routines. *//* * Send a packet to the peer over an async tty line. * Returns 1 iff the packet was accepted. * If the packet was not accepted, we will call ppp_output_wakeup * at some later time. */static intppp_async_send(struct ppp_channel *chan, struct sk_buff *skb){	struct asyncppp *ap = chan->private;	ppp_async_push(ap);	if (test_and_set_bit(XMIT_FULL, &ap->xmit_flags))		return 0;	/* already full */	ap->tpkt = skb;	ap->tpkt_pos = 0;	ppp_async_push(ap);	return 1;}/* * Push as much data as possible out to the tty. */static intppp_async_push(struct asyncppp *ap){	int avail, sent, done = 0;	struct tty_struct *tty = ap->tty;	int tty_stuffed = 0;	set_bit(XMIT_WAKEUP, &ap->xmit_flags);	if (!spin_trylock_bh(&ap->xmit_lock))		return 0;	for (;;) {		if (test_and_clear_bit(XMIT_WAKEUP, &ap->xmit_flags))			tty_stuffed = 0;		if (!tty_stuffed && ap->optr < ap->olim) {			avail = ap->olim - ap->optr;			set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);			sent = tty->driver.write(tty, 0, ap->optr, avail);			if (sent < 0)				goto flush;	/* error, e.g. loss of CD */			ap->optr += sent;			if (sent < avail)				tty_stuffed = 1;			continue;		}		if (ap->optr == ap->olim && ap->tpkt != 0) {			if (ppp_async_encode(ap)) {				/* finished processing ap->tpkt */				clear_bit(XMIT_FULL, &ap->xmit_flags);				done = 1;			}			continue;		}		/* haven't made any progress */		spin_unlock_bh(&ap->xmit_lock);		if (!(test_bit(XMIT_WAKEUP, &ap->xmit_flags)		      || (!tty_stuffed && ap->tpkt != 0)))			break;		if (!spin_trylock_bh(&ap->xmit_lock))			break;	}	return done;flush:	if (ap->tpkt != 0) {		kfree_skb(ap->tpkt);		ap->tpkt = 0;		clear_bit(XMIT_FULL, &ap->xmit_flags);		done = 1;	}	ap->optr = ap->olim;	spin_unlock_bh(&ap->xmit_lock);	return done;}/* * Flush output from our internal buffers. * Called for the TCFLSH ioctl. */static voidppp_async_flush_output(struct asyncppp *ap){	int done = 0;	spin_lock_bh(&ap->xmit_lock);	ap->optr = ap->olim;	if (ap->tpkt != NULL) {		kfree_skb(ap->tpkt);		ap->tpkt = 0;		clear_bit(XMIT_FULL, &ap->xmit_flags);		done = 1;	}	spin_unlock_bh(&ap->xmit_lock);	if (done)		ppp_output_wakeup(&ap->chan);}/* * Receive-side routines. *//* see how many ordinary chars there are at the start of buf */static inline intscan_ordinary(struct asyncppp *ap, const unsigned char *buf, int count){	int i, c;	for (i = 0; i < count; ++i) {		c = buf[i];		if (c == PPP_ESCAPE || c == PPP_FLAG		    || (c < 0x20 && (ap->raccm & (1 << c)) != 0))			break;	}	return i;}/* called when a flag is seen - do end-of-packet processing */static inline voidprocess_input_packet(struct asyncppp *ap){	struct sk_buff *skb;	unsigned char *p;	unsigned int len, fcs, proto;	int code = 0;	skb = ap->rpkt;	ap->rpkt = 0;	if ((ap->state & (SC_TOSS | SC_ESCAPE)) || skb == 0) {		ap->state &= ~(SC_TOSS | SC_ESCAPE);		if (skb != 0)			kfree_skb(skb);		return;	}	/* check the FCS */	p = skb->data;	len = skb->len;	if (len < 3)		goto err;	/* too short */	fcs = PPP_INITFCS;	for (; len > 0; --len)		fcs = PPP_FCS(fcs, *p++);	if (fcs != PPP_GOODFCS)		goto err;	/* bad FCS */	skb_trim(skb, skb->len - 2);	/* check for address/control and protocol compression */	p = skb->data;	if (p[0] == PPP_ALLSTATIONS && p[1] == PPP_UI) {		/* chop off address/control */		if (skb->len < 3)			goto err;		p = skb_pull(skb, 2);	}	proto = p[0];	if (proto & 1) {		/* protocol is compressed */		skb_push(skb, 1)[0] = 0;	} else {		if (skb->len < 2)			goto err;		proto = (proto << 8) + p[1];		if (proto == PPP_LCP)			async_lcp_peek(ap, p, skb->len, 1);	}	/* all OK, give it to the generic layer */	ppp_input(&ap->chan, skb);	return; err:	kfree_skb(skb);	ppp_input_error(&ap->chan, code);}static inline voidinput_error(struct asyncppp *ap, int code){	ap->state |= SC_TOSS;	ppp_input_error(&ap->chan, code);}/* called when the tty driver has data for us. */static voidppp_async_input(struct asyncppp *ap, const unsigned char *buf,		char *flags, int count){	struct sk_buff *skb;	int c, i, j, n, s, f;	unsigned char *sp;	/* update bits used for 8-bit cleanness detection */	if (~ap->rbits & SC_RCV_BITS) {		s = 0;		for (i = 0; i < count; ++i) {			c = buf[i];			if (flags != 0 && flags[i] != 0)				continue;			s |= (c & 0x80)? SC_RCV_B7_1: SC_RCV_B7_0;			c = ((c >> 4) ^ c) & 0xf;			s |= (0x6996 & (1 << c))? SC_RCV_ODDP: SC_RCV_EVNP;		}		ap->rbits |= s;	}	while (count > 0) {		/* scan through and see how many chars we can do in bulk */		if ((ap->state & SC_ESCAPE) && buf[0] == PPP_ESCAPE)			n = 1;		else			n = scan_ordinary(ap, buf, count);		f = 0;		if (flags != 0 && (ap->state & SC_TOSS) == 0) {			/* check the flags to see if any char had an error */			for (j = 0; j < n; ++j)				if ((f = flags[j]) != 0)					break;		}		if (f != 0) {			/* start tossing */			input_error(ap, f);		} else if (n > 0 && (ap->state & SC_TOSS) == 0) {			/* stuff the chars in the skb */			skb = ap->rpkt;			if (skb == 0) {				skb = dev_alloc_skb(ap->mru + PPP_HDRLEN + 2);				if (skb == 0)					goto nomem;				/* Try to get the payload 4-byte aligned */				if (buf[0] != PPP_ALLSTATIONS)					skb_reserve(skb, 2 + (buf[0] & 1));				ap->rpkt = skb;			}			if (n > skb_tailroom(skb)) {				/* packet overflowed MRU */				input_error(ap, 1);			} else {				sp = skb_put(skb, n);				memcpy(sp, buf, n);				if (ap->state & SC_ESCAPE) {					sp[0] ^= 0x20;					ap->state &= ~SC_ESCAPE;				}			}		}		if (n >= count)			break;		c = buf[n];		if (c == PPP_FLAG) {			process_input_packet(ap);		} else if (c == PPP_ESCAPE) {			ap->state |= SC_ESCAPE;		}		/* otherwise it's a char in the recv ACCM */		++n;		buf += n;		if (flags != 0)			flags += n;		count -= n;	}	return; nomem:	printk(KERN_ERR "PPPasync: no memory (input pkt)\n");	input_error(ap, 0);}/* * We look at LCP frames going past so that we can notice * and react to the LCP configure-ack from the peer. * In the situation where the peer has been sent a configure-ack * already, LCP is up once it has sent its configure-ack * so the immediately following packet can be sent with the * configured LCP options.  This allows us to process the following * packet correctly without pppd needing to respond quickly. * * We only respond to the received configure-ack if we have just * sent a configure-request, and the configure-ack contains the * same data (this is checked using a 16-bit crc of the data). */#define CONFREQ		1	/* LCP code field values */#define CONFACK		2#define LCP_MRU		1	/* LCP option numbers */#define LCP_ASYNCMAP	2static void async_lcp_peek(struct asyncppp *ap, unsigned char *data,			   int len, int inbound){	int dlen, fcs, i, code;	u32 val;	data += 2;		/* skip protocol bytes */	len -= 2;	if (len < 4)		/* 4 = code, ID, length */		return;	code = data[0];	if (code != CONFACK && code != CONFREQ)		return;	dlen = (data[2] << 8) + data[3];	if (len < dlen)		return;		/* packet got truncated or length is bogus */	if (code == (inbound? CONFACK: CONFREQ)) {		/*		 * sent confreq or received confack:		 * calculate the crc of the data from the ID field on.		 */		fcs = PPP_INITFCS;		for (i = 1; i < dlen; ++i)			fcs = PPP_FCS(fcs, data[i]);		if (!inbound) {			/* outbound confreq - remember the crc for later */			ap->lcp_fcs = fcs;			return;		}		/* received confack, check the crc */		fcs ^= ap->lcp_fcs;		ap->lcp_fcs = -1;		if (fcs != 0)			return;	} else if (inbound)		return;	/* not interested in received confreq */	/* process the options in the confack */	data += 4;	dlen -= 4;	/* data[0] is code, data[1] is length */	while (dlen >= 2 && dlen >= data[1]) {		switch (data[0]) {		case LCP_MRU:			val = (data[2] << 8) + data[3];			if (inbound)				ap->mru = val;			else				ap->chan.mtu = val;			break;		case LCP_ASYNCMAP:			val = (data[2] << 24) + (data[3] << 16)				+ (data[4] << 8) + data[5];			if (inbound)				ap->raccm = val;			else				ap->xaccm[0] = val;			break;		}		dlen -= data[1];		data += data[1];	}}void __exit ppp_async_cleanup(void){	if (tty_register_ldisc(N_PPP, NULL) != 0)		printk(KERN_ERR "failed to unregister PPP line discipline\n");}module_init(ppp_async_init);module_exit(ppp_async_cleanup);

⌨️ 快捷键说明

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