| Hansjoerg Lipp | 07dc1f9 | 2006-03-26 01:38:37 -0800 | [diff] [blame] | 1 | /* | 
 | 2 |  * Common data handling layer for ser_gigaset and usb_gigaset | 
 | 3 |  * | 
 | 4 |  * Copyright (c) 2005 by Tilman Schmidt <tilman@imap.cc>, | 
 | 5 |  *                       Hansjoerg Lipp <hjlipp@web.de>, | 
| Tilman Schmidt | 70440cf | 2006-04-10 22:55:14 -0700 | [diff] [blame] | 6 |  *                       Stefan Eilers. | 
| Hansjoerg Lipp | 07dc1f9 | 2006-03-26 01:38:37 -0800 | [diff] [blame] | 7 |  * | 
 | 8 |  * ===================================================================== | 
 | 9 |  *	This program is free software; you can redistribute it and/or | 
 | 10 |  *	modify it under the terms of the GNU General Public License as | 
 | 11 |  *	published by the Free Software Foundation; either version 2 of | 
 | 12 |  *	the License, or (at your option) any later version. | 
 | 13 |  * ===================================================================== | 
| Hansjoerg Lipp | 07dc1f9 | 2006-03-26 01:38:37 -0800 | [diff] [blame] | 14 |  */ | 
 | 15 |  | 
 | 16 | #include "gigaset.h" | 
 | 17 | #include <linux/crc-ccitt.h> | 
 | 18 |  | 
 | 19 | //#define GIG_M10x_STUFF_VOICE_DATA | 
 | 20 |  | 
 | 21 | /* check if byte must be stuffed/escaped | 
 | 22 |  * I'm not sure which data should be encoded. | 
 | 23 |  * Therefore I will go the hard way and decode every value | 
 | 24 |  * less than 0x20, the flag sequence and the control escape char. | 
 | 25 |  */ | 
 | 26 | static inline int muststuff(unsigned char c) | 
 | 27 | { | 
 | 28 | 	if (c < PPP_TRANS) return 1; | 
 | 29 | 	if (c == PPP_FLAG) return 1; | 
 | 30 | 	if (c == PPP_ESCAPE) return 1; | 
 | 31 | 	/* other possible candidates: */ | 
 | 32 | 	/* 0x91: XON with parity set */ | 
 | 33 | 	/* 0x93: XOFF with parity set */ | 
 | 34 | 	return 0; | 
 | 35 | } | 
 | 36 |  | 
 | 37 | /* == data input =========================================================== */ | 
 | 38 |  | 
 | 39 | /* process a block of received bytes in command mode (modem response) | 
 | 40 |  * Return value: | 
 | 41 |  *	number of processed bytes | 
 | 42 |  */ | 
 | 43 | static inline int cmd_loop(unsigned char c, unsigned char *src, int numbytes, | 
| Tilman Schmidt | 784d585 | 2006-04-10 22:55:04 -0700 | [diff] [blame] | 44 | 			   struct inbuf_t *inbuf) | 
| Hansjoerg Lipp | 07dc1f9 | 2006-03-26 01:38:37 -0800 | [diff] [blame] | 45 | { | 
 | 46 | 	struct cardstate *cs = inbuf->cs; | 
 | 47 | 	unsigned cbytes      = cs->cbytes; | 
 | 48 | 	int inputstate = inbuf->inputstate; | 
 | 49 | 	int startbytes = numbytes; | 
 | 50 |  | 
 | 51 | 	for (;;) { | 
 | 52 | 		cs->respdata[cbytes] = c; | 
 | 53 | 		if (c == 10 || c == 13) { | 
| Tilman Schmidt | 784d585 | 2006-04-10 22:55:04 -0700 | [diff] [blame] | 54 | 			gig_dbg(DEBUG_TRANSCMD, "%s: End of Command (%d Bytes)", | 
 | 55 | 				__func__, cbytes); | 
| Hansjoerg Lipp | 07dc1f9 | 2006-03-26 01:38:37 -0800 | [diff] [blame] | 56 | 			cs->cbytes = cbytes; | 
| Tilman Schmidt | 917f508 | 2006-04-10 22:55:00 -0700 | [diff] [blame] | 57 | 			gigaset_handle_modem_response(cs); /* can change | 
 | 58 | 							      cs->dle */ | 
| Hansjoerg Lipp | 07dc1f9 | 2006-03-26 01:38:37 -0800 | [diff] [blame] | 59 | 			cbytes = 0; | 
 | 60 |  | 
 | 61 | 			if (cs->dle && | 
 | 62 | 			    !(inputstate & INS_DLE_command)) { | 
 | 63 | 				inputstate &= ~INS_command; | 
 | 64 | 				break; | 
 | 65 | 			} | 
 | 66 | 		} else { | 
 | 67 | 			/* advance in line buffer, checking for overflow */ | 
 | 68 | 			if (cbytes < MAX_RESP_SIZE - 1) | 
 | 69 | 				cbytes++; | 
 | 70 | 			else | 
| Tilman Schmidt | 784d585 | 2006-04-10 22:55:04 -0700 | [diff] [blame] | 71 | 				dev_warn(cs->dev, "response too large\n"); | 
| Hansjoerg Lipp | 07dc1f9 | 2006-03-26 01:38:37 -0800 | [diff] [blame] | 72 | 		} | 
 | 73 |  | 
 | 74 | 		if (!numbytes) | 
 | 75 | 			break; | 
 | 76 | 		c = *src++; | 
 | 77 | 		--numbytes; | 
 | 78 | 		if (c == DLE_FLAG && | 
 | 79 | 		    (cs->dle || inputstate & INS_DLE_command)) { | 
 | 80 | 			inputstate |= INS_DLE_char; | 
 | 81 | 			break; | 
 | 82 | 		} | 
 | 83 | 	} | 
 | 84 |  | 
 | 85 | 	cs->cbytes = cbytes; | 
 | 86 | 	inbuf->inputstate = inputstate; | 
 | 87 |  | 
 | 88 | 	return startbytes - numbytes; | 
 | 89 | } | 
 | 90 |  | 
 | 91 | /* process a block of received bytes in lock mode (tty i/f) | 
 | 92 |  * Return value: | 
 | 93 |  *	number of processed bytes | 
 | 94 |  */ | 
 | 95 | static inline int lock_loop(unsigned char *src, int numbytes, | 
| Tilman Schmidt | 784d585 | 2006-04-10 22:55:04 -0700 | [diff] [blame] | 96 | 			    struct inbuf_t *inbuf) | 
| Hansjoerg Lipp | 07dc1f9 | 2006-03-26 01:38:37 -0800 | [diff] [blame] | 97 | { | 
 | 98 | 	struct cardstate *cs = inbuf->cs; | 
 | 99 |  | 
| Tilman Schmidt | 917f508 | 2006-04-10 22:55:00 -0700 | [diff] [blame] | 100 | 	gigaset_dbg_buffer(DEBUG_LOCKCMD, "received response", | 
| Tilman Schmidt | 0137150 | 2006-04-10 22:55:11 -0700 | [diff] [blame] | 101 | 			   numbytes, src); | 
| Hansjoerg Lipp | 07dc1f9 | 2006-03-26 01:38:37 -0800 | [diff] [blame] | 102 | 	gigaset_if_receive(cs, src, numbytes); | 
 | 103 |  | 
 | 104 | 	return numbytes; | 
 | 105 | } | 
 | 106 |  | 
 | 107 | /* process a block of received bytes in HDLC data mode | 
 | 108 |  * Collect HDLC frames, undoing byte stuffing and watching for DLE escapes. | 
 | 109 |  * When a frame is complete, check the FCS and pass valid frames to the LL. | 
 | 110 |  * If DLE is encountered, return immediately to let the caller handle it. | 
 | 111 |  * Return value: | 
 | 112 |  *	number of processed bytes | 
 | 113 |  *	numbytes (all bytes processed) on error --FIXME | 
 | 114 |  */ | 
 | 115 | static inline int hdlc_loop(unsigned char c, unsigned char *src, int numbytes, | 
| Tilman Schmidt | 784d585 | 2006-04-10 22:55:04 -0700 | [diff] [blame] | 116 | 			    struct inbuf_t *inbuf) | 
| Hansjoerg Lipp | 07dc1f9 | 2006-03-26 01:38:37 -0800 | [diff] [blame] | 117 | { | 
 | 118 | 	struct cardstate *cs = inbuf->cs; | 
 | 119 | 	struct bc_state *bcs = inbuf->bcs; | 
| Tilman Schmidt | d48c778 | 2006-04-10 22:55:08 -0700 | [diff] [blame] | 120 | 	int inputstate = bcs->inputstate; | 
 | 121 | 	__u16 fcs = bcs->fcs; | 
 | 122 | 	struct sk_buff *skb = bcs->skb; | 
| Hansjoerg Lipp | 07dc1f9 | 2006-03-26 01:38:37 -0800 | [diff] [blame] | 123 | 	unsigned char error; | 
 | 124 | 	struct sk_buff *compskb; | 
 | 125 | 	int startbytes = numbytes; | 
 | 126 | 	int l; | 
 | 127 |  | 
| Hansjoerg Lipp | 07dc1f9 | 2006-03-26 01:38:37 -0800 | [diff] [blame] | 128 | 	if (unlikely(inputstate & INS_byte_stuff)) { | 
 | 129 | 		inputstate &= ~INS_byte_stuff; | 
 | 130 | 		goto byte_stuff; | 
 | 131 | 	} | 
 | 132 | 	for (;;) { | 
 | 133 | 		if (unlikely(c == PPP_ESCAPE)) { | 
 | 134 | 			if (unlikely(!numbytes)) { | 
 | 135 | 				inputstate |= INS_byte_stuff; | 
 | 136 | 				break; | 
 | 137 | 			} | 
 | 138 | 			c = *src++; | 
 | 139 | 			--numbytes; | 
 | 140 | 			if (unlikely(c == DLE_FLAG && | 
 | 141 | 				     (cs->dle || | 
 | 142 | 				      inbuf->inputstate & INS_DLE_command))) { | 
 | 143 | 				inbuf->inputstate |= INS_DLE_char; | 
 | 144 | 				inputstate |= INS_byte_stuff; | 
 | 145 | 				break; | 
 | 146 | 			} | 
 | 147 | byte_stuff: | 
 | 148 | 			c ^= PPP_TRANS; | 
 | 149 | #ifdef CONFIG_GIGASET_DEBUG | 
 | 150 | 			if (unlikely(!muststuff(c))) | 
| Tilman Schmidt | 784d585 | 2006-04-10 22:55:04 -0700 | [diff] [blame] | 151 | 				gig_dbg(DEBUG_HDLC, "byte stuffed: 0x%02x", c); | 
| Hansjoerg Lipp | 07dc1f9 | 2006-03-26 01:38:37 -0800 | [diff] [blame] | 152 | #endif | 
 | 153 | 		} else if (unlikely(c == PPP_FLAG)) { | 
 | 154 | 			if (unlikely(inputstate & INS_skip_frame)) { | 
 | 155 | 				if (!(inputstate & INS_have_data)) { /* 7E 7E */ | 
| Hansjoerg Lipp | 07dc1f9 | 2006-03-26 01:38:37 -0800 | [diff] [blame] | 156 | #ifdef CONFIG_GIGASET_DEBUG | 
 | 157 | 					++bcs->emptycount; | 
 | 158 | #endif | 
 | 159 | 				} else | 
| Tilman Schmidt | 784d585 | 2006-04-10 22:55:04 -0700 | [diff] [blame] | 160 | 					gig_dbg(DEBUG_HDLC, | 
| Hansjoerg Lipp | 07dc1f9 | 2006-03-26 01:38:37 -0800 | [diff] [blame] | 161 | 					    "7e----------------------------"); | 
 | 162 |  | 
 | 163 | 				/* end of frame */ | 
 | 164 | 				error = 1; | 
 | 165 | 				gigaset_rcv_error(NULL, cs, bcs); | 
 | 166 | 			} else if (!(inputstate & INS_have_data)) { /* 7E 7E */ | 
| Hansjoerg Lipp | 07dc1f9 | 2006-03-26 01:38:37 -0800 | [diff] [blame] | 167 | #ifdef CONFIG_GIGASET_DEBUG | 
 | 168 | 				++bcs->emptycount; | 
 | 169 | #endif | 
 | 170 | 				break; | 
 | 171 | 			} else { | 
| Tilman Schmidt | 784d585 | 2006-04-10 22:55:04 -0700 | [diff] [blame] | 172 | 				gig_dbg(DEBUG_HDLC, | 
 | 173 | 					"7e----------------------------"); | 
| Hansjoerg Lipp | 07dc1f9 | 2006-03-26 01:38:37 -0800 | [diff] [blame] | 174 |  | 
 | 175 | 				/* end of frame */ | 
 | 176 | 				error = 0; | 
 | 177 |  | 
 | 178 | 				if (unlikely(fcs != PPP_GOODFCS)) { | 
| Tilman Schmidt | 784d585 | 2006-04-10 22:55:04 -0700 | [diff] [blame] | 179 | 					dev_err(cs->dev, | 
 | 180 | 					    "Packet checksum at %lu failed, " | 
 | 181 | 					    "packet is corrupted (%u bytes)!\n", | 
| Hansjoerg Lipp | 07dc1f9 | 2006-03-26 01:38:37 -0800 | [diff] [blame] | 182 | 					    bcs->rcvbytes, skb->len); | 
 | 183 | 					compskb = NULL; | 
 | 184 | 					gigaset_rcv_error(compskb, cs, bcs); | 
 | 185 | 					error = 1; | 
 | 186 | 				} else { | 
 | 187 | 					if (likely((l = skb->len) > 2)) { | 
 | 188 | 						skb->tail -= 2; | 
 | 189 | 						skb->len -= 2; | 
 | 190 | 					} else { | 
 | 191 | 						dev_kfree_skb(skb); | 
 | 192 | 						skb = NULL; | 
 | 193 | 						inputstate |= INS_skip_frame; | 
 | 194 | 						if (l == 1) { | 
| Tilman Schmidt | 784d585 | 2006-04-10 22:55:04 -0700 | [diff] [blame] | 195 | 							dev_err(cs->dev, | 
 | 196 | 						  "invalid packet size (1)!\n"); | 
| Hansjoerg Lipp | 07dc1f9 | 2006-03-26 01:38:37 -0800 | [diff] [blame] | 197 | 							error = 1; | 
| Tilman Schmidt | 784d585 | 2006-04-10 22:55:04 -0700 | [diff] [blame] | 198 | 							gigaset_rcv_error(NULL, | 
 | 199 | 								cs, bcs); | 
| Hansjoerg Lipp | 07dc1f9 | 2006-03-26 01:38:37 -0800 | [diff] [blame] | 200 | 						} | 
 | 201 | 					} | 
 | 202 | 					if (likely(!(error || | 
 | 203 | 						     (inputstate & | 
 | 204 | 						      INS_skip_frame)))) { | 
 | 205 | 						gigaset_rcv_skb(skb, cs, bcs); | 
 | 206 | 					} | 
 | 207 | 				} | 
 | 208 | 			} | 
 | 209 |  | 
 | 210 | 			if (unlikely(error)) | 
 | 211 | 				if (skb) | 
 | 212 | 					dev_kfree_skb(skb); | 
 | 213 |  | 
 | 214 | 			fcs = PPP_INITFCS; | 
 | 215 | 			inputstate &= ~(INS_have_data | INS_skip_frame); | 
 | 216 | 			if (unlikely(bcs->ignore)) { | 
 | 217 | 				inputstate |= INS_skip_frame; | 
 | 218 | 				skb = NULL; | 
 | 219 | 			} else if (likely((skb = dev_alloc_skb(SBUFSIZE + HW_HDR_LEN)) != NULL)) { | 
 | 220 | 				skb_reserve(skb, HW_HDR_LEN); | 
 | 221 | 			} else { | 
| Tilman Schmidt | 784d585 | 2006-04-10 22:55:04 -0700 | [diff] [blame] | 222 | 				dev_warn(cs->dev, | 
 | 223 | 					 "could not allocate new skb\n"); | 
| Hansjoerg Lipp | 07dc1f9 | 2006-03-26 01:38:37 -0800 | [diff] [blame] | 224 | 				inputstate |= INS_skip_frame; | 
 | 225 | 			} | 
 | 226 |  | 
 | 227 | 			break; | 
 | 228 | #ifdef CONFIG_GIGASET_DEBUG | 
 | 229 | 		} else if (unlikely(muststuff(c))) { | 
 | 230 | 			/* Should not happen. Possible after ZDLE=1<CR><LF>. */ | 
| Tilman Schmidt | 784d585 | 2006-04-10 22:55:04 -0700 | [diff] [blame] | 231 | 			gig_dbg(DEBUG_HDLC, "not byte stuffed: 0x%02x", c); | 
| Hansjoerg Lipp | 07dc1f9 | 2006-03-26 01:38:37 -0800 | [diff] [blame] | 232 | #endif | 
 | 233 | 		} | 
 | 234 |  | 
 | 235 | 		/* add character */ | 
 | 236 |  | 
 | 237 | #ifdef CONFIG_GIGASET_DEBUG | 
 | 238 | 		if (unlikely(!(inputstate & INS_have_data))) { | 
| Tilman Schmidt | 784d585 | 2006-04-10 22:55:04 -0700 | [diff] [blame] | 239 | 			gig_dbg(DEBUG_HDLC, "7e (%d x) ================", | 
 | 240 | 				bcs->emptycount); | 
| Hansjoerg Lipp | 07dc1f9 | 2006-03-26 01:38:37 -0800 | [diff] [blame] | 241 | 			bcs->emptycount = 0; | 
 | 242 | 		} | 
 | 243 | #endif | 
 | 244 |  | 
 | 245 | 		inputstate |= INS_have_data; | 
 | 246 |  | 
 | 247 | 		if (likely(!(inputstate & INS_skip_frame))) { | 
 | 248 | 			if (unlikely(skb->len == SBUFSIZE)) { | 
| Tilman Schmidt | 784d585 | 2006-04-10 22:55:04 -0700 | [diff] [blame] | 249 | 				dev_warn(cs->dev, "received packet too long\n"); | 
| Hansjoerg Lipp | 07dc1f9 | 2006-03-26 01:38:37 -0800 | [diff] [blame] | 250 | 				dev_kfree_skb_any(skb); | 
 | 251 | 				skb = NULL; | 
 | 252 | 				inputstate |= INS_skip_frame; | 
 | 253 | 				break; | 
 | 254 | 			} | 
| Tilman Schmidt | 443e1f4 | 2006-04-10 22:55:13 -0700 | [diff] [blame] | 255 | 			*__skb_put(skb, 1) = c; | 
| Hansjoerg Lipp | 07dc1f9 | 2006-03-26 01:38:37 -0800 | [diff] [blame] | 256 | 			fcs = crc_ccitt_byte(fcs, c); | 
 | 257 | 		} | 
 | 258 |  | 
 | 259 | 		if (unlikely(!numbytes)) | 
 | 260 | 			break; | 
 | 261 | 		c = *src++; | 
 | 262 | 		--numbytes; | 
 | 263 | 		if (unlikely(c == DLE_FLAG && | 
 | 264 | 			     (cs->dle || | 
 | 265 | 			      inbuf->inputstate & INS_DLE_command))) { | 
 | 266 | 			inbuf->inputstate |= INS_DLE_char; | 
 | 267 | 			break; | 
 | 268 | 		} | 
 | 269 | 	} | 
 | 270 | 	bcs->inputstate = inputstate; | 
 | 271 | 	bcs->fcs = fcs; | 
 | 272 | 	bcs->skb = skb; | 
 | 273 | 	return startbytes - numbytes; | 
 | 274 | } | 
 | 275 |  | 
 | 276 | /* process a block of received bytes in transparent data mode | 
 | 277 |  * Invert bytes, undoing byte stuffing and watching for DLE escapes. | 
 | 278 |  * If DLE is encountered, return immediately to let the caller handle it. | 
 | 279 |  * Return value: | 
 | 280 |  *	number of processed bytes | 
 | 281 |  *	numbytes (all bytes processed) on error --FIXME | 
 | 282 |  */ | 
 | 283 | static inline int iraw_loop(unsigned char c, unsigned char *src, int numbytes, | 
| Tilman Schmidt | 784d585 | 2006-04-10 22:55:04 -0700 | [diff] [blame] | 284 | 			    struct inbuf_t *inbuf) | 
| Hansjoerg Lipp | 07dc1f9 | 2006-03-26 01:38:37 -0800 | [diff] [blame] | 285 | { | 
 | 286 | 	struct cardstate *cs = inbuf->cs; | 
 | 287 | 	struct bc_state *bcs = inbuf->bcs; | 
| Tilman Schmidt | d48c778 | 2006-04-10 22:55:08 -0700 | [diff] [blame] | 288 | 	int inputstate = bcs->inputstate; | 
 | 289 | 	struct sk_buff *skb = bcs->skb; | 
| Hansjoerg Lipp | 07dc1f9 | 2006-03-26 01:38:37 -0800 | [diff] [blame] | 290 | 	int startbytes = numbytes; | 
 | 291 |  | 
| Hansjoerg Lipp | 07dc1f9 | 2006-03-26 01:38:37 -0800 | [diff] [blame] | 292 | 	for (;;) { | 
 | 293 | 		/* add character */ | 
 | 294 | 		inputstate |= INS_have_data; | 
 | 295 |  | 
 | 296 | 		if (likely(!(inputstate & INS_skip_frame))) { | 
 | 297 | 			if (unlikely(skb->len == SBUFSIZE)) { | 
 | 298 | 				//FIXME just pass skb up and allocate a new one | 
| Tilman Schmidt | 784d585 | 2006-04-10 22:55:04 -0700 | [diff] [blame] | 299 | 				dev_warn(cs->dev, "received packet too long\n"); | 
| Hansjoerg Lipp | 07dc1f9 | 2006-03-26 01:38:37 -0800 | [diff] [blame] | 300 | 				dev_kfree_skb_any(skb); | 
 | 301 | 				skb = NULL; | 
 | 302 | 				inputstate |= INS_skip_frame; | 
 | 303 | 				break; | 
 | 304 | 			} | 
| Tilman Schmidt | 443e1f4 | 2006-04-10 22:55:13 -0700 | [diff] [blame] | 305 | 			*__skb_put(skb, 1) = gigaset_invtab[c]; | 
| Hansjoerg Lipp | 07dc1f9 | 2006-03-26 01:38:37 -0800 | [diff] [blame] | 306 | 		} | 
 | 307 |  | 
 | 308 | 		if (unlikely(!numbytes)) | 
 | 309 | 			break; | 
 | 310 | 		c = *src++; | 
 | 311 | 		--numbytes; | 
 | 312 | 		if (unlikely(c == DLE_FLAG && | 
 | 313 | 			     (cs->dle || | 
 | 314 | 			      inbuf->inputstate & INS_DLE_command))) { | 
 | 315 | 			inbuf->inputstate |= INS_DLE_char; | 
 | 316 | 			break; | 
 | 317 | 		} | 
 | 318 | 	} | 
 | 319 |  | 
 | 320 | 	/* pass data up */ | 
 | 321 | 	if (likely(inputstate & INS_have_data)) { | 
 | 322 | 		if (likely(!(inputstate & INS_skip_frame))) { | 
 | 323 | 			gigaset_rcv_skb(skb, cs, bcs); | 
 | 324 | 		} | 
 | 325 | 		inputstate &= ~(INS_have_data | INS_skip_frame); | 
 | 326 | 		if (unlikely(bcs->ignore)) { | 
 | 327 | 			inputstate |= INS_skip_frame; | 
 | 328 | 			skb = NULL; | 
 | 329 | 		} else if (likely((skb = dev_alloc_skb(SBUFSIZE + HW_HDR_LEN)) | 
 | 330 | 				  != NULL)) { | 
 | 331 | 			skb_reserve(skb, HW_HDR_LEN); | 
 | 332 | 		} else { | 
| Tilman Schmidt | 784d585 | 2006-04-10 22:55:04 -0700 | [diff] [blame] | 333 | 			dev_warn(cs->dev, "could not allocate new skb\n"); | 
| Hansjoerg Lipp | 07dc1f9 | 2006-03-26 01:38:37 -0800 | [diff] [blame] | 334 | 			inputstate |= INS_skip_frame; | 
 | 335 | 		} | 
 | 336 | 	} | 
 | 337 |  | 
 | 338 | 	bcs->inputstate = inputstate; | 
 | 339 | 	bcs->skb = skb; | 
 | 340 | 	return startbytes - numbytes; | 
 | 341 | } | 
 | 342 |  | 
 | 343 | /* process a block of data received from the device | 
 | 344 |  */ | 
 | 345 | void gigaset_m10x_input(struct inbuf_t *inbuf) | 
 | 346 | { | 
 | 347 | 	struct cardstate *cs; | 
 | 348 | 	unsigned tail, head, numbytes; | 
 | 349 | 	unsigned char *src, c; | 
 | 350 | 	int procbytes; | 
 | 351 |  | 
 | 352 | 	head = atomic_read(&inbuf->head); | 
 | 353 | 	tail = atomic_read(&inbuf->tail); | 
| Tilman Schmidt | 784d585 | 2006-04-10 22:55:04 -0700 | [diff] [blame] | 354 | 	gig_dbg(DEBUG_INTR, "buffer state: %u -> %u", head, tail); | 
| Hansjoerg Lipp | 07dc1f9 | 2006-03-26 01:38:37 -0800 | [diff] [blame] | 355 |  | 
 | 356 | 	if (head != tail) { | 
 | 357 | 		cs = inbuf->cs; | 
 | 358 | 		src = inbuf->data + head; | 
 | 359 | 		numbytes = (head > tail ? RBUFSIZE : tail) - head; | 
| Tilman Schmidt | 784d585 | 2006-04-10 22:55:04 -0700 | [diff] [blame] | 360 | 		gig_dbg(DEBUG_INTR, "processing %u bytes", numbytes); | 
| Hansjoerg Lipp | 07dc1f9 | 2006-03-26 01:38:37 -0800 | [diff] [blame] | 361 |  | 
 | 362 | 		while (numbytes) { | 
 | 363 | 			if (atomic_read(&cs->mstate) == MS_LOCKED) { | 
 | 364 | 				procbytes = lock_loop(src, numbytes, inbuf); | 
 | 365 | 				src += procbytes; | 
 | 366 | 				numbytes -= procbytes; | 
 | 367 | 			} else { | 
 | 368 | 				c = *src++; | 
 | 369 | 				--numbytes; | 
 | 370 | 				if (c == DLE_FLAG && (cs->dle || | 
 | 371 | 				    inbuf->inputstate & INS_DLE_command)) { | 
 | 372 | 					if (!(inbuf->inputstate & INS_DLE_char)) { | 
 | 373 | 						inbuf->inputstate |= INS_DLE_char; | 
 | 374 | 						goto nextbyte; | 
 | 375 | 					} | 
 | 376 | 					/* <DLE> <DLE> => <DLE> in data stream */ | 
 | 377 | 					inbuf->inputstate &= ~INS_DLE_char; | 
 | 378 | 				} | 
 | 379 |  | 
 | 380 | 				if (!(inbuf->inputstate & INS_DLE_char)) { | 
 | 381 |  | 
| Tilman Schmidt | 917f508 | 2006-04-10 22:55:00 -0700 | [diff] [blame] | 382 | 					/* FIXME use function pointers?  */ | 
| Hansjoerg Lipp | 07dc1f9 | 2006-03-26 01:38:37 -0800 | [diff] [blame] | 383 | 					if (inbuf->inputstate & INS_command) | 
 | 384 | 						procbytes = cmd_loop(c, src, numbytes, inbuf); | 
 | 385 | 					else if (inbuf->bcs->proto2 == ISDN_PROTO_L2_HDLC) | 
 | 386 | 						procbytes = hdlc_loop(c, src, numbytes, inbuf); | 
 | 387 | 					else | 
 | 388 | 						procbytes = iraw_loop(c, src, numbytes, inbuf); | 
 | 389 |  | 
 | 390 | 					src += procbytes; | 
 | 391 | 					numbytes -= procbytes; | 
| Tilman Schmidt | 784d585 | 2006-04-10 22:55:04 -0700 | [diff] [blame] | 392 | 				} else {  /* DLE char */ | 
| Hansjoerg Lipp | 07dc1f9 | 2006-03-26 01:38:37 -0800 | [diff] [blame] | 393 | 					inbuf->inputstate &= ~INS_DLE_char; | 
 | 394 | 					switch (c) { | 
 | 395 | 					case 'X': /*begin of command*/ | 
 | 396 | #ifdef CONFIG_GIGASET_DEBUG | 
 | 397 | 						if (inbuf->inputstate & INS_command) | 
| Tilman Schmidt | 784d585 | 2006-04-10 22:55:04 -0700 | [diff] [blame] | 398 | 							dev_err(cs->dev, | 
 | 399 | 					"received <DLE> 'X' in command mode\n"); | 
| Hansjoerg Lipp | 07dc1f9 | 2006-03-26 01:38:37 -0800 | [diff] [blame] | 400 | #endif | 
 | 401 | 						inbuf->inputstate |= | 
 | 402 | 							INS_command | INS_DLE_command; | 
 | 403 | 						break; | 
 | 404 | 					case '.': /*end of command*/ | 
 | 405 | #ifdef CONFIG_GIGASET_DEBUG | 
 | 406 | 						if (!(inbuf->inputstate & INS_command)) | 
| Tilman Schmidt | 784d585 | 2006-04-10 22:55:04 -0700 | [diff] [blame] | 407 | 							dev_err(cs->dev, | 
 | 408 | 					"received <DLE> '.' in hdlc mode\n"); | 
| Hansjoerg Lipp | 07dc1f9 | 2006-03-26 01:38:37 -0800 | [diff] [blame] | 409 | #endif | 
 | 410 | 						inbuf->inputstate &= cs->dle ? | 
 | 411 | 							~(INS_DLE_command|INS_command) | 
 | 412 | 							: ~INS_DLE_command; | 
 | 413 | 						break; | 
 | 414 | 					//case DLE_FLAG: /*DLE_FLAG in data stream*/ /* schon oben behandelt! */ | 
 | 415 | 					default: | 
| Tilman Schmidt | 784d585 | 2006-04-10 22:55:04 -0700 | [diff] [blame] | 416 | 						dev_err(cs->dev, | 
 | 417 | 						      "received 0x10 0x%02x!\n", | 
 | 418 | 							(int) c); | 
| Hansjoerg Lipp | 07dc1f9 | 2006-03-26 01:38:37 -0800 | [diff] [blame] | 419 | 						/* FIXME: reset driver?? */ | 
 | 420 | 					} | 
 | 421 | 				} | 
 | 422 | 			} | 
 | 423 | nextbyte: | 
 | 424 | 			if (!numbytes) { | 
 | 425 | 				/* end of buffer, check for wrap */ | 
 | 426 | 				if (head > tail) { | 
 | 427 | 					head = 0; | 
 | 428 | 					src = inbuf->data; | 
 | 429 | 					numbytes = tail; | 
 | 430 | 				} else { | 
 | 431 | 					head = tail; | 
 | 432 | 					break; | 
 | 433 | 				} | 
 | 434 | 			} | 
 | 435 | 		} | 
 | 436 |  | 
| Tilman Schmidt | 784d585 | 2006-04-10 22:55:04 -0700 | [diff] [blame] | 437 | 		gig_dbg(DEBUG_INTR, "setting head to %u", head); | 
| Hansjoerg Lipp | 07dc1f9 | 2006-03-26 01:38:37 -0800 | [diff] [blame] | 438 | 		atomic_set(&inbuf->head, head); | 
 | 439 | 	} | 
 | 440 | } | 
 | 441 |  | 
 | 442 |  | 
 | 443 | /* == data output ========================================================== */ | 
 | 444 |  | 
 | 445 | /* Encoding of a PPP packet into an octet stuffed HDLC frame | 
 | 446 |  * with FCS, opening and closing flags. | 
 | 447 |  * parameters: | 
 | 448 |  *	skb	skb containing original packet (freed upon return) | 
 | 449 |  *	head	number of headroom bytes to allocate in result skb | 
 | 450 |  *	tail	number of tailroom bytes to allocate in result skb | 
 | 451 |  * Return value: | 
 | 452 |  *	pointer to newly allocated skb containing the result frame | 
 | 453 |  */ | 
 | 454 | static struct sk_buff *HDLC_Encode(struct sk_buff *skb, int head, int tail) | 
 | 455 | { | 
 | 456 | 	struct sk_buff *hdlc_skb; | 
 | 457 | 	__u16 fcs; | 
 | 458 | 	unsigned char c; | 
 | 459 | 	unsigned char *cp; | 
 | 460 | 	int len; | 
 | 461 | 	unsigned int stuf_cnt; | 
 | 462 |  | 
 | 463 | 	stuf_cnt = 0; | 
 | 464 | 	fcs = PPP_INITFCS; | 
 | 465 | 	cp = skb->data; | 
 | 466 | 	len = skb->len; | 
 | 467 | 	while (len--) { | 
 | 468 | 		if (muststuff(*cp)) | 
 | 469 | 			stuf_cnt++; | 
 | 470 | 		fcs = crc_ccitt_byte(fcs, *cp++); | 
 | 471 | 	} | 
| Tilman Schmidt | 784d585 | 2006-04-10 22:55:04 -0700 | [diff] [blame] | 472 | 	fcs ^= 0xffff;			/* complement */ | 
| Hansjoerg Lipp | 07dc1f9 | 2006-03-26 01:38:37 -0800 | [diff] [blame] | 473 |  | 
 | 474 | 	/* size of new buffer: original size + number of stuffing bytes | 
 | 475 | 	 * + 2 bytes FCS + 2 stuffing bytes for FCS (if needed) + 2 flag bytes | 
 | 476 | 	 */ | 
 | 477 | 	hdlc_skb = dev_alloc_skb(skb->len + stuf_cnt + 6 + tail + head); | 
 | 478 | 	if (!hdlc_skb) { | 
| Hansjoerg Lipp | 07dc1f9 | 2006-03-26 01:38:37 -0800 | [diff] [blame] | 479 | 		dev_kfree_skb(skb); | 
 | 480 | 		return NULL; | 
 | 481 | 	} | 
 | 482 | 	skb_reserve(hdlc_skb, head); | 
 | 483 |  | 
 | 484 | 	/* Copy acknowledge request into new skb */ | 
 | 485 | 	memcpy(hdlc_skb->head, skb->head, 2); | 
 | 486 |  | 
 | 487 | 	/* Add flag sequence in front of everything.. */ | 
 | 488 | 	*(skb_put(hdlc_skb, 1)) = PPP_FLAG; | 
 | 489 |  | 
 | 490 | 	/* Perform byte stuffing while copying data. */ | 
 | 491 | 	while (skb->len--) { | 
 | 492 | 		if (muststuff(*skb->data)) { | 
 | 493 | 			*(skb_put(hdlc_skb, 1)) = PPP_ESCAPE; | 
 | 494 | 			*(skb_put(hdlc_skb, 1)) = (*skb->data++) ^ PPP_TRANS; | 
 | 495 | 		} else | 
 | 496 | 			*(skb_put(hdlc_skb, 1)) = *skb->data++; | 
 | 497 | 	} | 
 | 498 |  | 
 | 499 | 	/* Finally add FCS (byte stuffed) and flag sequence */ | 
| Tilman Schmidt | 784d585 | 2006-04-10 22:55:04 -0700 | [diff] [blame] | 500 | 	c = (fcs & 0x00ff);	/* least significant byte first */ | 
| Hansjoerg Lipp | 07dc1f9 | 2006-03-26 01:38:37 -0800 | [diff] [blame] | 501 | 	if (muststuff(c)) { | 
 | 502 | 		*(skb_put(hdlc_skb, 1)) = PPP_ESCAPE; | 
 | 503 | 		c ^= PPP_TRANS; | 
 | 504 | 	} | 
 | 505 | 	*(skb_put(hdlc_skb, 1)) = c; | 
 | 506 |  | 
 | 507 | 	c = ((fcs >> 8) & 0x00ff); | 
 | 508 | 	if (muststuff(c)) { | 
 | 509 | 		*(skb_put(hdlc_skb, 1)) = PPP_ESCAPE; | 
 | 510 | 		c ^= PPP_TRANS; | 
 | 511 | 	} | 
 | 512 | 	*(skb_put(hdlc_skb, 1)) = c; | 
 | 513 |  | 
 | 514 | 	*(skb_put(hdlc_skb, 1)) = PPP_FLAG; | 
 | 515 |  | 
 | 516 | 	dev_kfree_skb(skb); | 
 | 517 | 	return hdlc_skb; | 
 | 518 | } | 
 | 519 |  | 
 | 520 | /* Encoding of a raw packet into an octet stuffed bit inverted frame | 
 | 521 |  * parameters: | 
 | 522 |  *	skb	skb containing original packet (freed upon return) | 
 | 523 |  *	head	number of headroom bytes to allocate in result skb | 
 | 524 |  *	tail	number of tailroom bytes to allocate in result skb | 
 | 525 |  * Return value: | 
 | 526 |  *	pointer to newly allocated skb containing the result frame | 
 | 527 |  */ | 
 | 528 | static struct sk_buff *iraw_encode(struct sk_buff *skb, int head, int tail) | 
 | 529 | { | 
 | 530 | 	struct sk_buff *iraw_skb; | 
 | 531 | 	unsigned char c; | 
 | 532 | 	unsigned char *cp; | 
 | 533 | 	int len; | 
 | 534 |  | 
 | 535 | 	/* worst case: every byte must be stuffed */ | 
 | 536 | 	iraw_skb = dev_alloc_skb(2*skb->len + tail + head); | 
 | 537 | 	if (!iraw_skb) { | 
| Hansjoerg Lipp | 07dc1f9 | 2006-03-26 01:38:37 -0800 | [diff] [blame] | 538 | 		dev_kfree_skb(skb); | 
 | 539 | 		return NULL; | 
 | 540 | 	} | 
 | 541 | 	skb_reserve(iraw_skb, head); | 
 | 542 |  | 
 | 543 | 	cp = skb->data; | 
 | 544 | 	len = skb->len; | 
 | 545 | 	while (len--) { | 
 | 546 | 		c = gigaset_invtab[*cp++]; | 
 | 547 | 		if (c == DLE_FLAG) | 
 | 548 | 			*(skb_put(iraw_skb, 1)) = c; | 
 | 549 | 		*(skb_put(iraw_skb, 1)) = c; | 
 | 550 | 	} | 
 | 551 | 	dev_kfree_skb(skb); | 
 | 552 | 	return iraw_skb; | 
 | 553 | } | 
 | 554 |  | 
 | 555 | /* gigaset_send_skb | 
 | 556 |  * called by common.c to queue an skb for sending | 
 | 557 |  * and start transmission if necessary | 
 | 558 |  * parameters: | 
 | 559 |  *	B Channel control structure | 
 | 560 |  *	skb | 
 | 561 |  * Return value: | 
 | 562 |  *	number of bytes accepted for sending | 
 | 563 |  *	(skb->len if ok, 0 if out of buffer space) | 
 | 564 |  *	or error code (< 0, eg. -EINVAL) | 
 | 565 |  */ | 
 | 566 | int gigaset_m10x_send_skb(struct bc_state *bcs, struct sk_buff *skb) | 
 | 567 | { | 
| Tilman Schmidt | d48c778 | 2006-04-10 22:55:08 -0700 | [diff] [blame] | 568 | 	unsigned len = skb->len; | 
| Tilman Schmidt | 69049cc | 2006-04-10 22:55:16 -0700 | [diff] [blame] | 569 | 	unsigned long flags; | 
| Hansjoerg Lipp | 07dc1f9 | 2006-03-26 01:38:37 -0800 | [diff] [blame] | 570 |  | 
 | 571 | 	if (bcs->proto2 == ISDN_PROTO_L2_HDLC) | 
 | 572 | 		skb = HDLC_Encode(skb, HW_HDR_LEN, 0); | 
 | 573 | 	else | 
 | 574 | 		skb = iraw_encode(skb, HW_HDR_LEN, 0); | 
| Tilman Schmidt | 784d585 | 2006-04-10 22:55:04 -0700 | [diff] [blame] | 575 | 	if (!skb) { | 
| Tilman Schmidt | 69049cc | 2006-04-10 22:55:16 -0700 | [diff] [blame] | 576 | 		err("unable to allocate memory for encoding!\n"); | 
| Hansjoerg Lipp | 07dc1f9 | 2006-03-26 01:38:37 -0800 | [diff] [blame] | 577 | 		return -ENOMEM; | 
| Tilman Schmidt | 784d585 | 2006-04-10 22:55:04 -0700 | [diff] [blame] | 578 | 	} | 
| Hansjoerg Lipp | 07dc1f9 | 2006-03-26 01:38:37 -0800 | [diff] [blame] | 579 |  | 
 | 580 | 	skb_queue_tail(&bcs->squeue, skb); | 
| Tilman Schmidt | 69049cc | 2006-04-10 22:55:16 -0700 | [diff] [blame] | 581 | 	spin_lock_irqsave(&bcs->cs->lock, flags); | 
 | 582 | 	if (bcs->cs->connected) | 
 | 583 | 		tasklet_schedule(&bcs->cs->write_tasklet); | 
 | 584 | 	spin_unlock_irqrestore(&bcs->cs->lock, flags); | 
| Hansjoerg Lipp | 07dc1f9 | 2006-03-26 01:38:37 -0800 | [diff] [blame] | 585 |  | 
 | 586 | 	return len;	/* ok so far */ | 
 | 587 | } |