asyncdata.c

来自「LINUX 2.6.17.4的源码」· C语言 代码 · 共 588 行

C
588
字号
/* * Common data handling layer for ser_gigaset and usb_gigaset * * Copyright (c) 2005 by Tilman Schmidt <tilman@imap.cc>, *                       Hansjoerg Lipp <hjlipp@web.de>, *                       Stefan Eilers. * * ===================================================================== *	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 of *	the License, or (at your option) any later version. * ===================================================================== */#include "gigaset.h"#include <linux/crc-ccitt.h>//#define GIG_M10x_STUFF_VOICE_DATA/* check if byte must be stuffed/escaped * I'm not sure which data should be encoded. * Therefore I will go the hard way and decode every value * less than 0x20, the flag sequence and the control escape char. */static inline int muststuff(unsigned char c){	if (c < PPP_TRANS) return 1;	if (c == PPP_FLAG) return 1;	if (c == PPP_ESCAPE) return 1;	/* other possible candidates: */	/* 0x91: XON with parity set */	/* 0x93: XOFF with parity set */	return 0;}/* == data input =========================================================== *//* process a block of received bytes in command mode (modem response) * Return value: *	number of processed bytes */static inline int cmd_loop(unsigned char c, unsigned char *src, int numbytes,			   struct inbuf_t *inbuf){	struct cardstate *cs = inbuf->cs;	unsigned cbytes      = cs->cbytes;	int inputstate = inbuf->inputstate;	int startbytes = numbytes;	for (;;) {		cs->respdata[cbytes] = c;		if (c == 10 || c == 13) {			gig_dbg(DEBUG_TRANSCMD, "%s: End of Command (%d Bytes)",				__func__, cbytes);			cs->cbytes = cbytes;			gigaset_handle_modem_response(cs); /* can change							      cs->dle */			cbytes = 0;			if (cs->dle &&			    !(inputstate & INS_DLE_command)) {				inputstate &= ~INS_command;				break;			}		} else {			/* advance in line buffer, checking for overflow */			if (cbytes < MAX_RESP_SIZE - 1)				cbytes++;			else				dev_warn(cs->dev, "response too large\n");		}		if (!numbytes)			break;		c = *src++;		--numbytes;		if (c == DLE_FLAG &&		    (cs->dle || inputstate & INS_DLE_command)) {			inputstate |= INS_DLE_char;			break;		}	}	cs->cbytes = cbytes;	inbuf->inputstate = inputstate;	return startbytes - numbytes;}/* process a block of received bytes in lock mode (tty i/f) * Return value: *	number of processed bytes */static inline int lock_loop(unsigned char *src, int numbytes,			    struct inbuf_t *inbuf){	struct cardstate *cs = inbuf->cs;	gigaset_dbg_buffer(DEBUG_LOCKCMD, "received response",			   numbytes, src);	gigaset_if_receive(cs, src, numbytes);	return numbytes;}/* process a block of received bytes in HDLC data mode * Collect HDLC frames, undoing byte stuffing and watching for DLE escapes. * When a frame is complete, check the FCS and pass valid frames to the LL. * If DLE is encountered, return immediately to let the caller handle it. * Return value: *	number of processed bytes *	numbytes (all bytes processed) on error --FIXME */static inline int hdlc_loop(unsigned char c, unsigned char *src, int numbytes,			    struct inbuf_t *inbuf){	struct cardstate *cs = inbuf->cs;	struct bc_state *bcs = inbuf->bcs;	int inputstate = bcs->inputstate;	__u16 fcs = bcs->fcs;	struct sk_buff *skb = bcs->skb;	unsigned char error;	struct sk_buff *compskb;	int startbytes = numbytes;	int l;	if (unlikely(inputstate & INS_byte_stuff)) {		inputstate &= ~INS_byte_stuff;		goto byte_stuff;	}	for (;;) {		if (unlikely(c == PPP_ESCAPE)) {			if (unlikely(!numbytes)) {				inputstate |= INS_byte_stuff;				break;			}			c = *src++;			--numbytes;			if (unlikely(c == DLE_FLAG &&				     (cs->dle ||				      inbuf->inputstate & INS_DLE_command))) {				inbuf->inputstate |= INS_DLE_char;				inputstate |= INS_byte_stuff;				break;			}byte_stuff:			c ^= PPP_TRANS;#ifdef CONFIG_GIGASET_DEBUG			if (unlikely(!muststuff(c)))				gig_dbg(DEBUG_HDLC, "byte stuffed: 0x%02x", c);#endif		} else if (unlikely(c == PPP_FLAG)) {			if (unlikely(inputstate & INS_skip_frame)) {				if (!(inputstate & INS_have_data)) { /* 7E 7E */#ifdef CONFIG_GIGASET_DEBUG					++bcs->emptycount;#endif				} else					gig_dbg(DEBUG_HDLC,					    "7e----------------------------");				/* end of frame */				error = 1;				gigaset_rcv_error(NULL, cs, bcs);			} else if (!(inputstate & INS_have_data)) { /* 7E 7E */#ifdef CONFIG_GIGASET_DEBUG				++bcs->emptycount;#endif				break;			} else {				gig_dbg(DEBUG_HDLC,					"7e----------------------------");				/* end of frame */				error = 0;				if (unlikely(fcs != PPP_GOODFCS)) {					dev_err(cs->dev,					    "Packet checksum at %lu failed, "					    "packet is corrupted (%u bytes)!\n",					    bcs->rcvbytes, skb->len);					compskb = NULL;					gigaset_rcv_error(compskb, cs, bcs);					error = 1;				} else {					if (likely((l = skb->len) > 2)) {						skb->tail -= 2;						skb->len -= 2;					} else {						dev_kfree_skb(skb);						skb = NULL;						inputstate |= INS_skip_frame;						if (l == 1) {							dev_err(cs->dev,						  "invalid packet size (1)!\n");							error = 1;							gigaset_rcv_error(NULL,								cs, bcs);						}					}					if (likely(!(error ||						     (inputstate &						      INS_skip_frame)))) {						gigaset_rcv_skb(skb, cs, bcs);					}				}			}			if (unlikely(error))				if (skb)					dev_kfree_skb(skb);			fcs = PPP_INITFCS;			inputstate &= ~(INS_have_data | INS_skip_frame);			if (unlikely(bcs->ignore)) {				inputstate |= INS_skip_frame;				skb = NULL;			} else if (likely((skb = dev_alloc_skb(SBUFSIZE + HW_HDR_LEN)) != NULL)) {				skb_reserve(skb, HW_HDR_LEN);			} else {				dev_warn(cs->dev,					 "could not allocate new skb\n");				inputstate |= INS_skip_frame;			}			break;#ifdef CONFIG_GIGASET_DEBUG		} else if (unlikely(muststuff(c))) {			/* Should not happen. Possible after ZDLE=1<CR><LF>. */			gig_dbg(DEBUG_HDLC, "not byte stuffed: 0x%02x", c);#endif		}		/* add character */#ifdef CONFIG_GIGASET_DEBUG		if (unlikely(!(inputstate & INS_have_data))) {			gig_dbg(DEBUG_HDLC, "7e (%d x) ================",				bcs->emptycount);			bcs->emptycount = 0;		}#endif		inputstate |= INS_have_data;		if (likely(!(inputstate & INS_skip_frame))) {			if (unlikely(skb->len == SBUFSIZE)) {				dev_warn(cs->dev, "received packet too long\n");				dev_kfree_skb_any(skb);				skb = NULL;				inputstate |= INS_skip_frame;				break;			}			*__skb_put(skb, 1) = c;			fcs = crc_ccitt_byte(fcs, c);		}		if (unlikely(!numbytes))			break;		c = *src++;		--numbytes;		if (unlikely(c == DLE_FLAG &&			     (cs->dle ||			      inbuf->inputstate & INS_DLE_command))) {			inbuf->inputstate |= INS_DLE_char;			break;		}	}	bcs->inputstate = inputstate;	bcs->fcs = fcs;	bcs->skb = skb;	return startbytes - numbytes;}/* process a block of received bytes in transparent data mode * Invert bytes, undoing byte stuffing and watching for DLE escapes. * If DLE is encountered, return immediately to let the caller handle it. * Return value: *	number of processed bytes *	numbytes (all bytes processed) on error --FIXME */static inline int iraw_loop(unsigned char c, unsigned char *src, int numbytes,			    struct inbuf_t *inbuf){	struct cardstate *cs = inbuf->cs;	struct bc_state *bcs = inbuf->bcs;	int inputstate = bcs->inputstate;	struct sk_buff *skb = bcs->skb;	int startbytes = numbytes;	for (;;) {		/* add character */		inputstate |= INS_have_data;		if (likely(!(inputstate & INS_skip_frame))) {			if (unlikely(skb->len == SBUFSIZE)) {				//FIXME just pass skb up and allocate a new one				dev_warn(cs->dev, "received packet too long\n");				dev_kfree_skb_any(skb);				skb = NULL;				inputstate |= INS_skip_frame;				break;			}			*__skb_put(skb, 1) = gigaset_invtab[c];		}		if (unlikely(!numbytes))			break;		c = *src++;		--numbytes;		if (unlikely(c == DLE_FLAG &&			     (cs->dle ||			      inbuf->inputstate & INS_DLE_command))) {			inbuf->inputstate |= INS_DLE_char;			break;		}	}	/* pass data up */	if (likely(inputstate & INS_have_data)) {		if (likely(!(inputstate & INS_skip_frame))) {			gigaset_rcv_skb(skb, cs, bcs);		}		inputstate &= ~(INS_have_data | INS_skip_frame);		if (unlikely(bcs->ignore)) {			inputstate |= INS_skip_frame;			skb = NULL;		} else if (likely((skb = dev_alloc_skb(SBUFSIZE + HW_HDR_LEN))				  != NULL)) {			skb_reserve(skb, HW_HDR_LEN);		} else {			dev_warn(cs->dev, "could not allocate new skb\n");			inputstate |= INS_skip_frame;		}	}	bcs->inputstate = inputstate;	bcs->skb = skb;	return startbytes - numbytes;}/* process a block of data received from the device */void gigaset_m10x_input(struct inbuf_t *inbuf){	struct cardstate *cs;	unsigned tail, head, numbytes;	unsigned char *src, c;	int procbytes;	head = atomic_read(&inbuf->head);	tail = atomic_read(&inbuf->tail);	gig_dbg(DEBUG_INTR, "buffer state: %u -> %u", head, tail);	if (head != tail) {		cs = inbuf->cs;		src = inbuf->data + head;		numbytes = (head > tail ? RBUFSIZE : tail) - head;		gig_dbg(DEBUG_INTR, "processing %u bytes", numbytes);		while (numbytes) {			if (atomic_read(&cs->mstate) == MS_LOCKED) {				procbytes = lock_loop(src, numbytes, inbuf);				src += procbytes;				numbytes -= procbytes;			} else {				c = *src++;				--numbytes;				if (c == DLE_FLAG && (cs->dle ||				    inbuf->inputstate & INS_DLE_command)) {					if (!(inbuf->inputstate & INS_DLE_char)) {						inbuf->inputstate |= INS_DLE_char;						goto nextbyte;					}					/* <DLE> <DLE> => <DLE> in data stream */					inbuf->inputstate &= ~INS_DLE_char;				}				if (!(inbuf->inputstate & INS_DLE_char)) {					/* FIXME use function pointers?  */					if (inbuf->inputstate & INS_command)						procbytes = cmd_loop(c, src, numbytes, inbuf);					else if (inbuf->bcs->proto2 == ISDN_PROTO_L2_HDLC)						procbytes = hdlc_loop(c, src, numbytes, inbuf);					else						procbytes = iraw_loop(c, src, numbytes, inbuf);					src += procbytes;					numbytes -= procbytes;				} else {  /* DLE char */					inbuf->inputstate &= ~INS_DLE_char;					switch (c) {					case 'X': /*begin of command*/#ifdef CONFIG_GIGASET_DEBUG						if (inbuf->inputstate & INS_command)							dev_err(cs->dev,					"received <DLE> 'X' in command mode\n");#endif						inbuf->inputstate |=							INS_command | INS_DLE_command;						break;					case '.': /*end of command*/#ifdef CONFIG_GIGASET_DEBUG						if (!(inbuf->inputstate & INS_command))							dev_err(cs->dev,					"received <DLE> '.' in hdlc mode\n");#endif						inbuf->inputstate &= cs->dle ?							~(INS_DLE_command|INS_command)							: ~INS_DLE_command;						break;					//case DLE_FLAG: /*DLE_FLAG in data stream*/ /* schon oben behandelt! */					default:						dev_err(cs->dev,						      "received 0x10 0x%02x!\n",							(int) c);						/* FIXME: reset driver?? */					}				}			}nextbyte:			if (!numbytes) {				/* end of buffer, check for wrap */				if (head > tail) {					head = 0;					src = inbuf->data;					numbytes = tail;				} else {					head = tail;					break;				}			}		}		gig_dbg(DEBUG_INTR, "setting head to %u", head);		atomic_set(&inbuf->head, head);	}}/* == data output ========================================================== *//* Encoding of a PPP packet into an octet stuffed HDLC frame * with FCS, opening and closing flags. * parameters: *	skb	skb containing original packet (freed upon return) *	head	number of headroom bytes to allocate in result skb *	tail	number of tailroom bytes to allocate in result skb * Return value: *	pointer to newly allocated skb containing the result frame */static struct sk_buff *HDLC_Encode(struct sk_buff *skb, int head, int tail){	struct sk_buff *hdlc_skb;	__u16 fcs;	unsigned char c;	unsigned char *cp;	int len;	unsigned int stuf_cnt;	stuf_cnt = 0;	fcs = PPP_INITFCS;	cp = skb->data;	len = skb->len;	while (len--) {		if (muststuff(*cp))			stuf_cnt++;		fcs = crc_ccitt_byte(fcs, *cp++);	}	fcs ^= 0xffff;			/* complement */	/* size of new buffer: original size + number of stuffing bytes	 * + 2 bytes FCS + 2 stuffing bytes for FCS (if needed) + 2 flag bytes	 */	hdlc_skb = dev_alloc_skb(skb->len + stuf_cnt + 6 + tail + head);	if (!hdlc_skb) {		dev_kfree_skb(skb);		return NULL;	}	skb_reserve(hdlc_skb, head);	/* Copy acknowledge request into new skb */	memcpy(hdlc_skb->head, skb->head, 2);	/* Add flag sequence in front of everything.. */	*(skb_put(hdlc_skb, 1)) = PPP_FLAG;	/* Perform byte stuffing while copying data. */	while (skb->len--) {		if (muststuff(*skb->data)) {			*(skb_put(hdlc_skb, 1)) = PPP_ESCAPE;			*(skb_put(hdlc_skb, 1)) = (*skb->data++) ^ PPP_TRANS;		} else			*(skb_put(hdlc_skb, 1)) = *skb->data++;	}	/* Finally add FCS (byte stuffed) and flag sequence */	c = (fcs & 0x00ff);	/* least significant byte first */	if (muststuff(c)) {		*(skb_put(hdlc_skb, 1)) = PPP_ESCAPE;		c ^= PPP_TRANS;	}	*(skb_put(hdlc_skb, 1)) = c;	c = ((fcs >> 8) & 0x00ff);	if (muststuff(c)) {		*(skb_put(hdlc_skb, 1)) = PPP_ESCAPE;		c ^= PPP_TRANS;	}	*(skb_put(hdlc_skb, 1)) = c;	*(skb_put(hdlc_skb, 1)) = PPP_FLAG;	dev_kfree_skb(skb);	return hdlc_skb;}/* Encoding of a raw packet into an octet stuffed bit inverted frame * parameters: *	skb	skb containing original packet (freed upon return) *	head	number of headroom bytes to allocate in result skb *	tail	number of tailroom bytes to allocate in result skb * Return value: *	pointer to newly allocated skb containing the result frame */static struct sk_buff *iraw_encode(struct sk_buff *skb, int head, int tail){	struct sk_buff *iraw_skb;	unsigned char c;	unsigned char *cp;	int len;	/* worst case: every byte must be stuffed */	iraw_skb = dev_alloc_skb(2*skb->len + tail + head);	if (!iraw_skb) {		dev_kfree_skb(skb);		return NULL;	}	skb_reserve(iraw_skb, head);	cp = skb->data;	len = skb->len;	while (len--) {		c = gigaset_invtab[*cp++];		if (c == DLE_FLAG)			*(skb_put(iraw_skb, 1)) = c;		*(skb_put(iraw_skb, 1)) = c;	}	dev_kfree_skb(skb);	return iraw_skb;}/* gigaset_send_skb * called by common.c to queue an skb for sending * and start transmission if necessary * parameters: *	B Channel control structure *	skb * Return value: *	number of bytes accepted for sending *	(skb->len if ok, 0 if out of buffer space) *	or error code (< 0, eg. -EINVAL) */int gigaset_m10x_send_skb(struct bc_state *bcs, struct sk_buff *skb){	unsigned len = skb->len;	unsigned long flags;	if (bcs->proto2 == ISDN_PROTO_L2_HDLC)		skb = HDLC_Encode(skb, HW_HDR_LEN, 0);	else		skb = iraw_encode(skb, HW_HDR_LEN, 0);	if (!skb) {		err("unable to allocate memory for encoding!\n");		return -ENOMEM;	}	skb_queue_tail(&bcs->squeue, skb);	spin_lock_irqsave(&bcs->cs->lock, flags);	if (bcs->cs->connected)		tasklet_schedule(&bcs->cs->write_tasklet);	spin_unlock_irqrestore(&bcs->cs->lock, flags);	return len;	/* ok so far */}

⌨️ 快捷键说明

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