asyncdata.c 14.9 KB
Newer Older
1 2 3 4 5
/*
 * Common data handling layer for ser_gigaset and usb_gigaset
 *
 * Copyright (c) 2005 by Tilman Schmidt <tilman@imap.cc>,
 *                       Hansjoerg Lipp <hjlipp@web.de>,
6
 *                       Stefan Eilers.
7 8 9 10 11 12 13 14 15 16 17
 *
 * =====================================================================
 *	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>
18
#include <linux/bitrev.h>
19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42

/* 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,
43
			   struct inbuf_t *inbuf)
44 45 46 47 48 49 50 51 52
{
	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) {
53 54
			gig_dbg(DEBUG_TRANSCMD, "%s: End of Command (%d Bytes)",
				__func__, cbytes);
55
			cs->cbytes = cbytes;
56 57
			gigaset_handle_modem_response(cs); /* can change
							      cs->dle */
58 59 60 61 62 63 64 65 66 67 68 69
			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
70
				dev_warn(cs->dev, "response too large\n");
71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94
		}

		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,
95
			    struct inbuf_t *inbuf)
96 97 98
{
	struct cardstate *cs = inbuf->cs;

99
	gigaset_dbg_buffer(DEBUG_LOCKCMD, "received response",
100
			   numbytes, src);
101 102 103 104 105 106 107 108 109 110 111 112 113 114
	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,
115
			    struct inbuf_t *inbuf)
116 117 118
{
	struct cardstate *cs = inbuf->cs;
	struct bc_state *bcs = inbuf->bcs;
119 120 121
	int inputstate = bcs->inputstate;
	__u16 fcs = bcs->fcs;
	struct sk_buff *skb = bcs->skb;
122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145
	int startbytes = numbytes;

	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;
			if (unlikely(!muststuff(c)))
146
				gig_dbg(DEBUG_HDLC, "byte stuffed: 0x%02x", c);
147 148 149
		} else if (unlikely(c == PPP_FLAG)) {
			if (unlikely(inputstate & INS_skip_frame)) {
#ifdef CONFIG_GIGASET_DEBUG
Tilman Schmidt's avatar
Tilman Schmidt committed
150
				if (!(inputstate & INS_have_data)) { /* 7E 7E */
151 152
					++bcs->emptycount;
				} else
153
					gig_dbg(DEBUG_HDLC,
154
					    "7e----------------------------");
Tilman Schmidt's avatar
Tilman Schmidt committed
155
#endif
156 157

				/* end of frame */
158 159
				gigaset_isdn_rcv_err(bcs);
				dev_kfree_skb(skb);
160 161 162 163 164 165
			} else if (!(inputstate & INS_have_data)) { /* 7E 7E */
#ifdef CONFIG_GIGASET_DEBUG
				++bcs->emptycount;
#endif
				break;
			} else {
166 167
				gig_dbg(DEBUG_HDLC,
					"7e----------------------------");
168 169 170

				/* end of frame */
				if (unlikely(fcs != PPP_GOODFCS)) {
171
					dev_err(cs->dev,
172 173
				"Checksum failed, %u bytes corrupted!\n",
						skb->len);
174 175 176 177 178
					gigaset_isdn_rcv_err(bcs);
					dev_kfree_skb(skb);
				} else if (likely(skb->len > 2)) {
					__skb_trim(skb, skb->len - 2);
					gigaset_skb_rcvd(bcs, skb);
179
				} else {
180 181 182 183
					if (skb->len) {
						dev_err(cs->dev,
					"invalid packet size (%d)\n", skb->len);
						gigaset_isdn_rcv_err(bcs);
184
					}
185
					dev_kfree_skb(skb);
186 187 188 189 190 191 192 193 194
				}
			}

			fcs = PPP_INITFCS;
			inputstate &= ~(INS_have_data | INS_skip_frame);
			if (unlikely(bcs->ignore)) {
				inputstate |= INS_skip_frame;
				skb = NULL;
			} else {
195 196 197 198 199 200 201 202
				skb = dev_alloc_skb(SBUFSIZE + cs->hw_hdr_len);
				if (skb != NULL) {
					skb_reserve(skb, cs->hw_hdr_len);
				} else {
					dev_warn(cs->dev,
						"could not allocate new skb\n");
					inputstate |= INS_skip_frame;
				}
203 204 205 206 207
			}

			break;
		} else if (unlikely(muststuff(c))) {
			/* Should not happen. Possible after ZDLE=1<CR><LF>. */
208
			gig_dbg(DEBUG_HDLC, "not byte stuffed: 0x%02x", c);
209 210 211 212 213 214
		}

		/* add character */

#ifdef CONFIG_GIGASET_DEBUG
		if (unlikely(!(inputstate & INS_have_data))) {
215 216
			gig_dbg(DEBUG_HDLC, "7e (%d x) ================",
				bcs->emptycount);
217 218 219 220 221 222 223 224
			bcs->emptycount = 0;
		}
#endif

		inputstate |= INS_have_data;

		if (likely(!(inputstate & INS_skip_frame))) {
			if (unlikely(skb->len == SBUFSIZE)) {
225
				dev_warn(cs->dev, "received packet too long\n");
226 227 228 229 230
				dev_kfree_skb_any(skb);
				skb = NULL;
				inputstate |= INS_skip_frame;
				break;
			}
231
			*__skb_put(skb, 1) = c;
232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259
			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,
260
			    struct inbuf_t *inbuf)
261 262 263
{
	struct cardstate *cs = inbuf->cs;
	struct bc_state *bcs = inbuf->bcs;
264 265
	int inputstate = bcs->inputstate;
	struct sk_buff *skb = bcs->skb;
266 267 268 269 270 271 272 273 274
	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
275
				dev_warn(cs->dev, "received packet too long\n");
276 277 278 279 280
				dev_kfree_skb_any(skb);
				skb = NULL;
				inputstate |= INS_skip_frame;
				break;
			}
281
			*__skb_put(skb, 1) = bitrev8(c);
282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298
		}

		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))) {
299
			gigaset_skb_rcvd(bcs, skb);
300 301 302 303 304 305
		}
		inputstate &= ~(INS_have_data | INS_skip_frame);
		if (unlikely(bcs->ignore)) {
			inputstate |= INS_skip_frame;
			skb = NULL;
		} else {
306 307 308 309 310 311 312 313
			skb = dev_alloc_skb(SBUFSIZE + cs->hw_hdr_len);
			if (skb != NULL) {
				skb_reserve(skb, cs->hw_hdr_len);
			} else {
				dev_warn(cs->dev,
					 "could not allocate new skb\n");
				inputstate |= INS_skip_frame;
			}
314 315 316 317 318 319 320 321
		}
	}

	bcs->inputstate = inputstate;
	bcs->skb = skb;
	return startbytes - numbytes;
}

322 323 324 325 326 327 328 329
/**
 * gigaset_m10x_input() - process a block of data received from the device
 * @inbuf:	received data and device descriptor structure.
 *
 * Called by hardware module {ser,usb}_gigaset with a block of received
 * bytes. Separates the bytes received over the serial data channel into
 * user data and command replies (locked/unlocked) according to the
 * current state of the interface.
330 331 332 333 334 335 336 337
 */
void gigaset_m10x_input(struct inbuf_t *inbuf)
{
	struct cardstate *cs;
	unsigned tail, head, numbytes;
	unsigned char *src, c;
	int procbytes;

Tilman Schmidt's avatar
Tilman Schmidt committed
338 339
	head = inbuf->head;
	tail = inbuf->tail;
340
	gig_dbg(DEBUG_INTR, "buffer state: %u -> %u", head, tail);
341 342 343 344 345

	if (head != tail) {
		cs = inbuf->cs;
		src = inbuf->data + head;
		numbytes = (head > tail ? RBUFSIZE : tail) - head;
346
		gig_dbg(DEBUG_INTR, "processing %u bytes", numbytes);
347 348

		while (numbytes) {
Tilman Schmidt's avatar
Tilman Schmidt committed
349
			if (cs->mstate == MS_LOCKED) {
350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367
				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)) {

368
					/* FIXME use function pointers?  */
369 370
					if (inbuf->inputstate & INS_command)
						procbytes = cmd_loop(c, src, numbytes, inbuf);
371
					else if (inbuf->bcs->proto2 == L2_HDLC)
372 373 374 375 376 377
						procbytes = hdlc_loop(c, src, numbytes, inbuf);
					else
						procbytes = iraw_loop(c, src, numbytes, inbuf);

					src += procbytes;
					numbytes -= procbytes;
378
				} else {  /* DLE char */
379 380 381 382
					inbuf->inputstate &= ~INS_DLE_char;
					switch (c) {
					case 'X': /*begin of command*/
						if (inbuf->inputstate & INS_command)
Tilman Schmidt's avatar
Tilman Schmidt committed
383
							dev_warn(cs->dev,
384
					"received <DLE> 'X' in command mode\n");
385 386 387 388 389
						inbuf->inputstate |=
							INS_command | INS_DLE_command;
						break;
					case '.': /*end of command*/
						if (!(inbuf->inputstate & INS_command))
Tilman Schmidt's avatar
Tilman Schmidt committed
390
							dev_warn(cs->dev,
391
					"received <DLE> '.' in hdlc mode\n");
392 393 394 395 396 397
						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:
398 399 400
						dev_err(cs->dev,
						      "received 0x10 0x%02x!\n",
							(int) c);
401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418
						/* 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;
				}
			}
		}

419
		gig_dbg(DEBUG_INTR, "setting head to %u", head);
Tilman Schmidt's avatar
Tilman Schmidt committed
420
		inbuf->head = head;
421 422
	}
}
423
EXPORT_SYMBOL_GPL(gigaset_m10x_input);
424 425 426 427


/* == data output ========================================================== */

428 429 430
/*
 * Encode a data packet into an octet stuffed HDLC frame with FCS,
 * opening and closing flags, preserving headroom data.
431
 * parameters:
432 433
 *	skb		skb containing original packet (freed upon return)
 *	headroom	number of headroom bytes to preserve
434 435 436
 * Return value:
 *	pointer to newly allocated skb containing the result frame
 */
437
static struct sk_buff *HDLC_Encode(struct sk_buff *skb, int headroom)
438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454
{
	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++);
	}
455
	fcs ^= 0xffff;			/* complement */
456 457 458

	/* size of new buffer: original size + number of stuffing bytes
	 * + 2 bytes FCS + 2 stuffing bytes for FCS (if needed) + 2 flag bytes
459
	 * + room for acknowledgement header
460
	 */
461
	hdlc_skb = dev_alloc_skb(skb->len + stuf_cnt + 6 + headroom);
462 463 464 465 466
	if (!hdlc_skb) {
		dev_kfree_skb(skb);
		return NULL;
	}

467 468 469
	/* Copy acknowledgement header into new skb */
	skb_reserve(hdlc_skb, headroom);
	memcpy(hdlc_skb->head, skb->head, headroom);
470 471 472 473 474 475 476 477 478 479 480 481 482 483

	/* 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 */
484
	c = (fcs & 0x00ff);	/* least significant byte first */
485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503
	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;
}

504 505 506
/*
 * Encode a data packet into an octet stuffed raw bit inverted frame,
 * preserving headroom data.
507
 * parameters:
508 509
 *	skb		skb containing original packet (freed upon return)
 *	headroom	number of headroom bytes to preserve
510 511 512
 * Return value:
 *	pointer to newly allocated skb containing the result frame
 */
513
static struct sk_buff *iraw_encode(struct sk_buff *skb, int headroom)
514 515 516 517 518 519 520
{
	struct sk_buff *iraw_skb;
	unsigned char c;
	unsigned char *cp;
	int len;

	/* worst case: every byte must be stuffed */
521
	iraw_skb = dev_alloc_skb(2*skb->len + headroom);
522 523 524 525
	if (!iraw_skb) {
		dev_kfree_skb(skb);
		return NULL;
	}
526 527 528 529

	/* Copy acknowledgement header into new skb */
	skb_reserve(iraw_skb, headroom);
	memcpy(iraw_skb->head, skb->head, headroom);
530 531 532 533

	cp = skb->data;
	len = skb->len;
	while (len--) {
534
		c = bitrev8(*cp++);
535 536 537 538 539 540 541 542
		if (c == DLE_FLAG)
			*(skb_put(iraw_skb, 1)) = c;
		*(skb_put(iraw_skb, 1)) = c;
	}
	dev_kfree_skb(skb);
	return iraw_skb;
}

543 544 545 546 547
/**
 * gigaset_m10x_send_skb() - queue an skb for sending
 * @bcs:	B channel descriptor structure.
 * @skb:	data to send.
 *
548
 * Called by LL to encode and queue an skb for sending, and start
549
 * transmission if necessary.
550 551
 * Once the payload data has been transmitted completely, gigaset_skb_sent()
 * will be called with the first cs->hw_hdr_len bytes of skb->head preserved.
552
 *
553
 * Return value:
554 555
 *	number of bytes accepted for sending (skb->len) if ok,
 *	error code < 0 (eg. -ENOMEM) on error
556 557 558
 */
int gigaset_m10x_send_skb(struct bc_state *bcs, struct sk_buff *skb)
{
559
	unsigned len = skb->len;
560
	unsigned long flags;
561

562 563
	if (bcs->proto2 == L2_HDLC)
		skb = HDLC_Encode(skb, bcs->cs->hw_hdr_len);
564
	else
565
		skb = iraw_encode(skb, bcs->cs->hw_hdr_len);
566
	if (!skb) {
567 568
		dev_err(bcs->cs->dev,
			"unable to allocate memory for encoding!\n");
569
		return -ENOMEM;
570
	}
571 572

	skb_queue_tail(&bcs->squeue, skb);
573 574 575 576
	spin_lock_irqsave(&bcs->cs->lock, flags);
	if (bcs->cs->connected)
		tasklet_schedule(&bcs->cs->write_tasklet);
	spin_unlock_irqrestore(&bcs->cs->lock, flags);
577 578 579

	return len;	/* ok so far */
}
580
EXPORT_SYMBOL_GPL(gigaset_m10x_send_skb);