2 * Common data handling layer for ser_gigaset and usb_gigaset
4 * Copyright (c) 2005 by Tilman Schmidt <tilman@imap.cc>,
5 * Hansjoerg Lipp <hjlipp@web.de>,
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 * =====================================================================
17 #include <linux/crc-ccitt.h>
18 #include <linux/bitrev.h>
20 /* check if byte must be stuffed/escaped
21 * I'm not sure which data should be encoded.
22 * Therefore I will go the hard way and decode every value
23 * less than 0x20, the flag sequence and the control escape char.
25 static inline int muststuff(unsigned char c)
27 if (c < PPP_TRANS) return 1;
28 if (c == PPP_FLAG) return 1;
29 if (c == PPP_ESCAPE) return 1;
30 /* other possible candidates: */
31 /* 0x91: XON with parity set */
32 /* 0x93: XOFF with parity set */
36 /* == data input =========================================================== */
38 /* process a block of received bytes in command mode (modem response)
40 * number of processed bytes
42 static inline int cmd_loop(unsigned char c, unsigned char *src, int numbytes,
43 struct inbuf_t *inbuf)
45 struct cardstate *cs = inbuf->cs;
46 unsigned cbytes = cs->cbytes;
47 int inputstate = inbuf->inputstate;
48 int startbytes = numbytes;
51 cs->respdata[cbytes] = c;
52 if (c == 10 || c == 13) {
53 gig_dbg(DEBUG_TRANSCMD, "%s: End of Command (%d Bytes)",
56 gigaset_handle_modem_response(cs); /* can change
61 !(inputstate & INS_DLE_command)) {
62 inputstate &= ~INS_command;
66 /* advance in line buffer, checking for overflow */
67 if (cbytes < MAX_RESP_SIZE - 1)
70 dev_warn(cs->dev, "response too large\n");
78 (cs->dle || inputstate & INS_DLE_command)) {
79 inputstate |= INS_DLE_char;
85 inbuf->inputstate = inputstate;
87 return startbytes - numbytes;
90 /* process a block of received bytes in lock mode (tty i/f)
92 * number of processed bytes
94 static inline int lock_loop(unsigned char *src, int numbytes,
95 struct inbuf_t *inbuf)
97 struct cardstate *cs = inbuf->cs;
99 gigaset_dbg_buffer(DEBUG_LOCKCMD, "received response",
101 gigaset_if_receive(cs, src, numbytes);
106 /* process a block of received bytes in HDLC data mode
107 * Collect HDLC frames, undoing byte stuffing and watching for DLE escapes.
108 * When a frame is complete, check the FCS and pass valid frames to the LL.
109 * If DLE is encountered, return immediately to let the caller handle it.
111 * number of processed bytes
112 * numbytes (all bytes processed) on error --FIXME
114 static inline int hdlc_loop(unsigned char c, unsigned char *src, int numbytes,
115 struct inbuf_t *inbuf)
117 struct cardstate *cs = inbuf->cs;
118 struct bc_state *bcs = inbuf->bcs;
119 int inputstate = bcs->inputstate;
120 __u16 fcs = bcs->fcs;
121 struct sk_buff *skb = bcs->skb;
123 struct sk_buff *compskb;
124 int startbytes = numbytes;
127 if (unlikely(inputstate & INS_byte_stuff)) {
128 inputstate &= ~INS_byte_stuff;
132 if (unlikely(c == PPP_ESCAPE)) {
133 if (unlikely(!numbytes)) {
134 inputstate |= INS_byte_stuff;
139 if (unlikely(c == DLE_FLAG &&
141 inbuf->inputstate & INS_DLE_command))) {
142 inbuf->inputstate |= INS_DLE_char;
143 inputstate |= INS_byte_stuff;
148 if (unlikely(!muststuff(c)))
149 gig_dbg(DEBUG_HDLC, "byte stuffed: 0x%02x", c);
150 } else if (unlikely(c == PPP_FLAG)) {
151 if (unlikely(inputstate & INS_skip_frame)) {
152 #ifdef CONFIG_GIGASET_DEBUG
153 if (!(inputstate & INS_have_data)) { /* 7E 7E */
157 "7e----------------------------");
162 gigaset_rcv_error(NULL, cs, bcs);
163 } else if (!(inputstate & INS_have_data)) { /* 7E 7E */
164 #ifdef CONFIG_GIGASET_DEBUG
170 "7e----------------------------");
175 if (unlikely(fcs != PPP_GOODFCS)) {
177 "Checksum failed, %u bytes corrupted!\n",
180 gigaset_rcv_error(compskb, cs, bcs);
183 if (likely((l = skb->len) > 2)) {
189 inputstate |= INS_skip_frame;
192 "invalid packet size (1)!\n");
194 gigaset_rcv_error(NULL,
198 if (likely(!(error ||
201 gigaset_rcv_skb(skb, cs, bcs);
211 inputstate &= ~(INS_have_data | INS_skip_frame);
212 if (unlikely(bcs->ignore)) {
213 inputstate |= INS_skip_frame;
215 } else if (likely((skb = dev_alloc_skb(SBUFSIZE + HW_HDR_LEN)) != NULL)) {
216 skb_reserve(skb, HW_HDR_LEN);
219 "could not allocate new skb\n");
220 inputstate |= INS_skip_frame;
224 } else if (unlikely(muststuff(c))) {
225 /* Should not happen. Possible after ZDLE=1<CR><LF>. */
226 gig_dbg(DEBUG_HDLC, "not byte stuffed: 0x%02x", c);
231 #ifdef CONFIG_GIGASET_DEBUG
232 if (unlikely(!(inputstate & INS_have_data))) {
233 gig_dbg(DEBUG_HDLC, "7e (%d x) ================",
239 inputstate |= INS_have_data;
241 if (likely(!(inputstate & INS_skip_frame))) {
242 if (unlikely(skb->len == SBUFSIZE)) {
243 dev_warn(cs->dev, "received packet too long\n");
244 dev_kfree_skb_any(skb);
246 inputstate |= INS_skip_frame;
249 *__skb_put(skb, 1) = c;
250 fcs = crc_ccitt_byte(fcs, c);
253 if (unlikely(!numbytes))
257 if (unlikely(c == DLE_FLAG &&
259 inbuf->inputstate & INS_DLE_command))) {
260 inbuf->inputstate |= INS_DLE_char;
264 bcs->inputstate = inputstate;
267 return startbytes - numbytes;
270 /* process a block of received bytes in transparent data mode
271 * Invert bytes, undoing byte stuffing and watching for DLE escapes.
272 * If DLE is encountered, return immediately to let the caller handle it.
274 * number of processed bytes
275 * numbytes (all bytes processed) on error --FIXME
277 static inline int iraw_loop(unsigned char c, unsigned char *src, int numbytes,
278 struct inbuf_t *inbuf)
280 struct cardstate *cs = inbuf->cs;
281 struct bc_state *bcs = inbuf->bcs;
282 int inputstate = bcs->inputstate;
283 struct sk_buff *skb = bcs->skb;
284 int startbytes = numbytes;
288 inputstate |= INS_have_data;
290 if (likely(!(inputstate & INS_skip_frame))) {
291 if (unlikely(skb->len == SBUFSIZE)) {
292 //FIXME just pass skb up and allocate a new one
293 dev_warn(cs->dev, "received packet too long\n");
294 dev_kfree_skb_any(skb);
296 inputstate |= INS_skip_frame;
299 *__skb_put(skb, 1) = bitrev8(c);
302 if (unlikely(!numbytes))
306 if (unlikely(c == DLE_FLAG &&
308 inbuf->inputstate & INS_DLE_command))) {
309 inbuf->inputstate |= INS_DLE_char;
315 if (likely(inputstate & INS_have_data)) {
316 if (likely(!(inputstate & INS_skip_frame))) {
317 gigaset_rcv_skb(skb, cs, bcs);
319 inputstate &= ~(INS_have_data | INS_skip_frame);
320 if (unlikely(bcs->ignore)) {
321 inputstate |= INS_skip_frame;
323 } else if (likely((skb = dev_alloc_skb(SBUFSIZE + HW_HDR_LEN))
325 skb_reserve(skb, HW_HDR_LEN);
327 dev_warn(cs->dev, "could not allocate new skb\n");
328 inputstate |= INS_skip_frame;
332 bcs->inputstate = inputstate;
334 return startbytes - numbytes;
338 * gigaset_m10x_input() - process a block of data received from the device
339 * @inbuf: received data and device descriptor structure.
341 * Called by hardware module {ser,usb}_gigaset with a block of received
342 * bytes. Separates the bytes received over the serial data channel into
343 * user data and command replies (locked/unlocked) according to the
344 * current state of the interface.
346 void gigaset_m10x_input(struct inbuf_t *inbuf)
348 struct cardstate *cs;
349 unsigned tail, head, numbytes;
350 unsigned char *src, c;
355 gig_dbg(DEBUG_INTR, "buffer state: %u -> %u", head, tail);
359 src = inbuf->data + head;
360 numbytes = (head > tail ? RBUFSIZE : tail) - head;
361 gig_dbg(DEBUG_INTR, "processing %u bytes", numbytes);
364 if (cs->mstate == MS_LOCKED) {
365 procbytes = lock_loop(src, numbytes, inbuf);
367 numbytes -= procbytes;
371 if (c == DLE_FLAG && (cs->dle ||
372 inbuf->inputstate & INS_DLE_command)) {
373 if (!(inbuf->inputstate & INS_DLE_char)) {
374 inbuf->inputstate |= INS_DLE_char;
377 /* <DLE> <DLE> => <DLE> in data stream */
378 inbuf->inputstate &= ~INS_DLE_char;
381 if (!(inbuf->inputstate & INS_DLE_char)) {
383 /* FIXME use function pointers? */
384 if (inbuf->inputstate & INS_command)
385 procbytes = cmd_loop(c, src, numbytes, inbuf);
386 else if (inbuf->bcs->proto2 == ISDN_PROTO_L2_HDLC)
387 procbytes = hdlc_loop(c, src, numbytes, inbuf);
389 procbytes = iraw_loop(c, src, numbytes, inbuf);
392 numbytes -= procbytes;
393 } else { /* DLE char */
394 inbuf->inputstate &= ~INS_DLE_char;
396 case 'X': /*begin of command*/
397 if (inbuf->inputstate & INS_command)
399 "received <DLE> 'X' in command mode\n");
401 INS_command | INS_DLE_command;
403 case '.': /*end of command*/
404 if (!(inbuf->inputstate & INS_command))
406 "received <DLE> '.' in hdlc mode\n");
407 inbuf->inputstate &= cs->dle ?
408 ~(INS_DLE_command|INS_command)
411 //case DLE_FLAG: /*DLE_FLAG in data stream*/ /* schon oben behandelt! */
414 "received 0x10 0x%02x!\n",
416 /* FIXME: reset driver?? */
422 /* end of buffer, check for wrap */
434 gig_dbg(DEBUG_INTR, "setting head to %u", head);
438 EXPORT_SYMBOL_GPL(gigaset_m10x_input);
441 /* == data output ========================================================== */
443 /* Encoding of a PPP packet into an octet stuffed HDLC frame
444 * with FCS, opening and closing flags.
446 * skb skb containing original packet (freed upon return)
447 * head number of headroom bytes to allocate in result skb
448 * tail number of tailroom bytes to allocate in result skb
450 * pointer to newly allocated skb containing the result frame
452 static struct sk_buff *HDLC_Encode(struct sk_buff *skb, int head, int tail)
454 struct sk_buff *hdlc_skb;
459 unsigned int stuf_cnt;
468 fcs = crc_ccitt_byte(fcs, *cp++);
470 fcs ^= 0xffff; /* complement */
472 /* size of new buffer: original size + number of stuffing bytes
473 * + 2 bytes FCS + 2 stuffing bytes for FCS (if needed) + 2 flag bytes
475 hdlc_skb = dev_alloc_skb(skb->len + stuf_cnt + 6 + tail + head);
480 skb_reserve(hdlc_skb, head);
482 /* Copy acknowledge request into new skb */
483 memcpy(hdlc_skb->head, skb->head, 2);
485 /* Add flag sequence in front of everything.. */
486 *(skb_put(hdlc_skb, 1)) = PPP_FLAG;
488 /* Perform byte stuffing while copying data. */
490 if (muststuff(*skb->data)) {
491 *(skb_put(hdlc_skb, 1)) = PPP_ESCAPE;
492 *(skb_put(hdlc_skb, 1)) = (*skb->data++) ^ PPP_TRANS;
494 *(skb_put(hdlc_skb, 1)) = *skb->data++;
497 /* Finally add FCS (byte stuffed) and flag sequence */
498 c = (fcs & 0x00ff); /* least significant byte first */
500 *(skb_put(hdlc_skb, 1)) = PPP_ESCAPE;
503 *(skb_put(hdlc_skb, 1)) = c;
505 c = ((fcs >> 8) & 0x00ff);
507 *(skb_put(hdlc_skb, 1)) = PPP_ESCAPE;
510 *(skb_put(hdlc_skb, 1)) = c;
512 *(skb_put(hdlc_skb, 1)) = PPP_FLAG;
518 /* Encoding of a raw packet into an octet stuffed bit inverted frame
520 * skb skb containing original packet (freed upon return)
521 * head number of headroom bytes to allocate in result skb
522 * tail number of tailroom bytes to allocate in result skb
524 * pointer to newly allocated skb containing the result frame
526 static struct sk_buff *iraw_encode(struct sk_buff *skb, int head, int tail)
528 struct sk_buff *iraw_skb;
533 /* worst case: every byte must be stuffed */
534 iraw_skb = dev_alloc_skb(2*skb->len + tail + head);
539 skb_reserve(iraw_skb, head);
546 *(skb_put(iraw_skb, 1)) = c;
547 *(skb_put(iraw_skb, 1)) = c;
554 * gigaset_m10x_send_skb() - queue an skb for sending
555 * @bcs: B channel descriptor structure.
556 * @skb: data to send.
558 * Called by i4l.c to encode and queue an skb for sending, and start
559 * transmission if necessary.
562 * number of bytes accepted for sending (skb->len) if ok,
563 * error code < 0 (eg. -ENOMEM) on error
565 int gigaset_m10x_send_skb(struct bc_state *bcs, struct sk_buff *skb)
567 unsigned len = skb->len;
570 if (bcs->proto2 == ISDN_PROTO_L2_HDLC)
571 skb = HDLC_Encode(skb, HW_HDR_LEN, 0);
573 skb = iraw_encode(skb, HW_HDR_LEN, 0);
575 dev_err(bcs->cs->dev,
576 "unable to allocate memory for encoding!\n");
580 skb_queue_tail(&bcs->squeue, skb);
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);
586 return len; /* ok so far */
588 EXPORT_SYMBOL_GPL(gigaset_m10x_send_skb);