- patches.suse/slab-handle-memoryless-nodes-v2a.patch: Refresh.
[linux-flexiantxendom0-3.2.10.git] / drivers / isdn / gigaset / asyncdata.c
index 44a58e6..ccb2a7b 100644 (file)
@@ -19,7 +19,7 @@
 
 /* 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
+ * Therefore I will go the hard way and encode every value
  * less than 0x20, the flag sequence and the control escape char.
  */
 static inline int muststuff(unsigned char c)
@@ -35,303 +35,383 @@ static inline int muststuff(unsigned char c)
 
 /* == data input =========================================================== */
 
-/* process a block of received bytes in command mode (modem response)
+/* process a block of received bytes in command mode
+ * (mstate != MS_LOCKED && (inputstate & INS_command))
+ * Append received bytes to the command response buffer and forward them
+ * line by line to the response handler. Exit whenever a mode/state change
+ * might have occurred.
  * Return value:
  *     number of processed bytes
  */
-static inline int cmd_loop(unsigned char c, unsigned char *src, int numbytes,
-                          struct inbuf_t *inbuf)
+static unsigned cmd_loop(unsigned numbytes, struct inbuf_t *inbuf)
 {
+       unsigned char *src = inbuf->data + inbuf->head;
        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)",
+       unsigned cbytes = cs->cbytes;
+       unsigned procbytes = 0;
+       unsigned char c;
+
+       while (procbytes < numbytes) {
+               c = *src++;
+               procbytes++;
+
+               switch (c) {
+               case '\n':
+                       if (cbytes == 0 && cs->respdata[0] == '\r') {
+                               /* collapse LF with preceding CR */
+                               cs->respdata[0] = 0;
+                               break;
+                       }
+                       /* --v-- fall through --v-- */
+               case '\r':
+                       /* end of message line, pass to response handler */
+                       gig_dbg(DEBUG_TRANSCMD, "%s: End of Message (%d Bytes)",
                                __func__, cbytes);
+                       if (cbytes >= MAX_RESP_SIZE) {
+                               dev_warn(cs->dev, "response too large (%d)\n",
+                                        cbytes);
+                               cbytes = MAX_RESP_SIZE;
+                       }
                        cs->cbytes = cbytes;
-                       gigaset_handle_modem_response(cs); /* can change
-                                                             cs->dle */
+                       gigaset_handle_modem_response(cs);
                        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");
-               }
+                       /* store EOL byte for CRLF collapsing */
+                       cs->respdata[0] = c;
 
-               if (!numbytes)
-                       break;
-               c = *src++;
-               --numbytes;
-               if (c == DLE_FLAG &&
-                   (cs->dle || inputstate & INS_DLE_command)) {
-                       inputstate |= INS_DLE_char;
-                       break;
+                       /* cs->dle may have changed */
+                       if (cs->dle && !(inbuf->inputstate & INS_DLE_command))
+                               inbuf->inputstate &= ~INS_command;
+
+                       /* return for reevaluating state */
+                       goto exit;
+
+               case DLE_FLAG:
+                       if (inbuf->inputstate & INS_DLE_char) {
+                               /* quoted DLE: clear quote flag */
+                               inbuf->inputstate &= ~INS_DLE_char;
+                       } else if (cs->dle ||
+                                  (inbuf->inputstate & INS_DLE_command)) {
+                               /* DLE escape, pass up for handling */
+                               inbuf->inputstate |= INS_DLE_char;
+                               goto exit;
+                       }
+                       /* quoted or not in DLE mode: treat as regular data */
+                       /* --v-- fall through --v-- */
+               default:
+                       /* append to line buffer if possible */
+                       if (cbytes < MAX_RESP_SIZE)
+                               cs->respdata[cbytes] = c;
+                       cbytes++;
                }
        }
-
+exit:
        cs->cbytes = cbytes;
-       inbuf->inputstate = inputstate;
-
-       return startbytes - numbytes;
+       return procbytes;
 }
 
-/* process a block of received bytes in lock mode (tty i/f)
+/* process a block of received bytes in lock mode
+ * All received bytes are passed unmodified to the tty i/f.
  * Return value:
  *     number of processed bytes
  */
-static inline int lock_loop(unsigned char *src, int numbytes,
-                           struct inbuf_t *inbuf)
+static unsigned lock_loop(unsigned 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);
+       unsigned char *src = inbuf->data + inbuf->head;
 
+       gigaset_dbg_buffer(DEBUG_LOCKCMD, "received response", numbytes, src);
+       gigaset_if_receive(inbuf->cs, src, numbytes);
        return numbytes;
 }
 
+/* set up next receive skb for data mode
+ */
+static void new_rcv_skb(struct bc_state *bcs)
+{
+       struct cardstate *cs = bcs->cs;
+       unsigned short hw_hdr_len = cs->hw_hdr_len;
+
+       if (bcs->ignore) {
+               bcs->skb = NULL;
+               return;
+       }
+
+       bcs->skb = dev_alloc_skb(SBUFSIZE + hw_hdr_len);
+       if (bcs->skb == NULL) {
+               dev_warn(cs->dev, "could not allocate new skb\n");
+               return;
+       }
+       skb_reserve(bcs->skb, hw_hdr_len);
+}
+
 /* process a block of received bytes in HDLC data mode
+ * (mstate != MS_LOCKED && !(inputstate & INS_command) && proto2 == L2_HDLC)
  * 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)
+static unsigned hdlc_loop(unsigned numbytes, struct inbuf_t *inbuf)
 {
        struct cardstate *cs = inbuf->cs;
-       struct bc_state *bcs = inbuf->bcs;
+       struct bc_state *bcs = cs->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;
+       unsigned char *src = inbuf->data + inbuf->head;
+       unsigned procbytes = 0;
+       unsigned char c;
 
-       if (unlikely(inputstate & INS_byte_stuff)) {
+       if (inputstate & INS_byte_stuff) {
+               if (!numbytes)
+                       return 0;
                inputstate &= ~INS_byte_stuff;
                goto byte_stuff;
        }
-       for (;;) {
-               if (unlikely(c == PPP_ESCAPE)) {
-                       if (unlikely(!numbytes)) {
-                               inputstate |= INS_byte_stuff;
+
+       while (procbytes < numbytes) {
+               c = *src++;
+               procbytes++;
+               if (c == DLE_FLAG) {
+                       if (inputstate & INS_DLE_char) {
+                               /* quoted DLE: clear quote flag */
+                               inputstate &= ~INS_DLE_char;
+                       } else if (cs->dle || (inputstate & INS_DLE_command)) {
+                               /* DLE escape, pass up for handling */
+                               inputstate |= INS_DLE_char;
                                break;
                        }
-                       c = *src++;
-                       --numbytes;
-                       if (unlikely(c == DLE_FLAG &&
-                                    (cs->dle ||
-                                     inbuf->inputstate & INS_DLE_command))) {
-                               inbuf->inputstate |= INS_DLE_char;
+               }
+
+               if (c == PPP_ESCAPE) {
+                       /* byte stuffing indicator: pull in next byte */
+                       if (procbytes >= numbytes) {
+                               /* end of buffer, save for later processing */
                                inputstate |= INS_byte_stuff;
                                break;
                        }
 byte_stuff:
+                       c = *src++;
+                       procbytes++;
+                       if (c == DLE_FLAG) {
+                               if (inputstate & INS_DLE_char) {
+                                       /* quoted DLE: clear quote flag */
+                                       inputstate &= ~INS_DLE_char;
+                               } else if (cs->dle ||
+                                          (inputstate & INS_DLE_command)) {
+                                       /* DLE escape, pass up for handling */
+                                       inputstate |=
+                                               INS_DLE_char | INS_byte_stuff;
+                                       break;
+                               }
+                       }
                        c ^= PPP_TRANS;
-                       if (unlikely(!muststuff(c)))
-                               gig_dbg(DEBUG_HDLC, "byte stuffed: 0x%02x", c);
-               } else if (unlikely(c == PPP_FLAG)) {
-                       if (unlikely(inputstate & INS_skip_frame)) {
-#ifdef CONFIG_GIGASET_DEBUG
-                               if (!(inputstate & INS_have_data)) { /* 7E 7E */
-                                       ++bcs->emptycount;
-                               } else
-                                       gig_dbg(DEBUG_HDLC,
-                                           "7e----------------------------");
-#endif
-
-                               /* 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;
+                       if (!muststuff(c))
+                               gig_dbg(DEBUG_HDLC, "byte stuffed: 0x%02x", c);
 #endif
-                               break;
-                       } else {
+               } else if (c == PPP_FLAG) {
+                       /* end of frame: process content if any */
+                       if (inputstate & INS_have_data) {
                                gig_dbg(DEBUG_HDLC,
                                        "7e----------------------------");
 
-                               /* end of frame */
-                               error = 0;
-
-                               if (unlikely(fcs != PPP_GOODFCS)) {
+                               /* check and pass received frame */
+                               if (!skb) {
+                                       /* skipped frame */
+                                       gigaset_isdn_rcv_err(bcs);
+                               } else if (skb->len < 2) {
+                                       /* frame too short for FCS */
+                                       dev_warn(cs->dev,
+                                                "short frame (%d)\n",
+                                                skb->len);
+                                       gigaset_isdn_rcv_err(bcs);
+                                       dev_kfree_skb_any(skb);
+                               } else if (fcs != PPP_GOODFCS) {
+                                       /* frame check error */
                                        dev_err(cs->dev,
                                "Checksum failed, %u bytes corrupted!\n",
                                                skb->len);
-                                       compskb = NULL;
-                                       gigaset_rcv_error(compskb, cs, bcs);
-                                       error = 1;
+                                       gigaset_isdn_rcv_err(bcs);
+                                       dev_kfree_skb_any(skb);
                                } 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);
-                                       }
+                                       /* good frame */
+                                       __skb_trim(skb, skb->len - 2);
+                                       gigaset_skb_rcvd(bcs, skb);
                                }
-                       }
 
-                       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);
+                               /* prepare reception of next frame */
+                               inputstate &= ~INS_have_data;
+                               new_rcv_skb(bcs);
+                               skb = bcs->skb;
                        } else {
-                               dev_warn(cs->dev,
-                                        "could not allocate new skb\n");
-                               inputstate |= INS_skip_frame;
+                               /* empty frame (7E 7E) */
+#ifdef CONFIG_GIGASET_DEBUG
+                               ++bcs->emptycount;
+#endif
+                               if (!skb) {
+                                       /* skipped (?) */
+                                       gigaset_isdn_rcv_err(bcs);
+                                       new_rcv_skb(bcs);
+                                       skb = bcs->skb;
+                               }
                        }
 
-                       break;
-               } else if (unlikely(muststuff(c))) {
+                       fcs = PPP_INITFCS;
+                       continue;
+#ifdef CONFIG_GIGASET_DEBUG
+               } else if (muststuff(c)) {
                        /* Should not happen. Possible after ZDLE=1<CR><LF>. */
                        gig_dbg(DEBUG_HDLC, "not byte stuffed: 0x%02x", c);
+#endif
                }
 
-               /* add character */
-
+               /* regular data byte, append to skb */
 #ifdef CONFIG_GIGASET_DEBUG
-               if (unlikely(!(inputstate & INS_have_data))) {
+               if (!(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)) {
+               if (skb) {
+                       if (skb->len == SBUFSIZE) {
                                dev_warn(cs->dev, "received packet too long\n");
                                dev_kfree_skb_any(skb);
-                               skb = NULL;
-                               inputstate |= INS_skip_frame;
-                               break;
+                               /* skip remainder of packet */
+                               bcs->skb = skb = NULL;
+                       } else {
+                               *__skb_put(skb, 1) = c;
+                               fcs = crc_ccitt_byte(fcs, c);
                        }
-                       *__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;
+       return procbytes;
 }
 
 /* process a block of received bytes in transparent data mode
+ * (mstate != MS_LOCKED && !(inputstate & INS_command) && proto2 != L2_HDLC)
  * 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)
+static unsigned iraw_loop(unsigned numbytes, struct inbuf_t *inbuf)
 {
        struct cardstate *cs = inbuf->cs;
-       struct bc_state *bcs = inbuf->bcs;
+       struct bc_state *bcs = cs->bcs;
        int inputstate = bcs->inputstate;
        struct sk_buff *skb = bcs->skb;
-       int startbytes = numbytes;
+       unsigned char *src = inbuf->data + inbuf->head;
+       unsigned procbytes = 0;
+       unsigned char c;
 
-       for (;;) {
-               /* add character */
-               inputstate |= INS_have_data;
+       if (!skb) {
+               /* skip this block */
+               new_rcv_skb(bcs);
+               return numbytes;
+       }
 
-               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;
+       while (procbytes < numbytes && skb->len < SBUFSIZE) {
+               c = *src++;
+               procbytes++;
+
+               if (c == DLE_FLAG) {
+                       if (inputstate & INS_DLE_char) {
+                               /* quoted DLE: clear quote flag */
+                               inputstate &= ~INS_DLE_char;
+                       } else if (cs->dle || (inputstate & INS_DLE_command)) {
+                               /* DLE escape, pass up for handling */
+                               inputstate |= INS_DLE_char;
                                break;
                        }
-                       *__skb_put(skb, 1) = bitrev8(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;
-               }
+               /* regular data byte: append to current skb */
+               inputstate |= INS_have_data;
+               *__skb_put(skb, 1) = bitrev8(c);
        }
 
        /* 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);
+       if (inputstate & INS_have_data) {
+               gigaset_skb_rcvd(bcs, skb);
+               inputstate &= ~INS_have_data;
+               new_rcv_skb(bcs);
+       }
+
+       bcs->inputstate = inputstate;
+       return procbytes;
+}
+
+/* process DLE escapes
+ * Called whenever a DLE sequence might be encountered in the input stream.
+ * Either processes the entire DLE sequence or, if that isn't possible,
+ * notes the fact that an initial DLE has been received in the INS_DLE_char
+ * inputstate flag and resumes processing of the sequence on the next call.
+ */
+static void handle_dle(struct inbuf_t *inbuf)
+{
+       struct cardstate *cs = inbuf->cs;
+
+       if (cs->mstate == MS_LOCKED)
+               return;         /* no DLE processing in lock mode */
+
+       if (!(inbuf->inputstate & INS_DLE_char)) {
+               /* no DLE pending */
+               if (inbuf->data[inbuf->head] == DLE_FLAG &&
+                   (cs->dle || inbuf->inputstate & INS_DLE_command)) {
+                       /* start of DLE sequence */
+                       inbuf->head++;
+                       if (inbuf->head == inbuf->tail ||
+                           inbuf->head == RBUFSIZE) {
+                               /* end of buffer, save for later processing */
+                               inbuf->inputstate |= INS_DLE_char;
+                               return;
+                       }
                } else {
-                       dev_warn(cs->dev, "could not allocate new skb\n");
-                       inputstate |= INS_skip_frame;
+                       /* regular data byte */
+                       return;
                }
        }
 
-       bcs->inputstate = inputstate;
-       bcs->skb = skb;
-       return startbytes - numbytes;
+       /* consume pending DLE */
+       inbuf->inputstate &= ~INS_DLE_char;
+
+       switch (inbuf->data[inbuf->head]) {
+       case 'X':       /* begin of event message */
+               if (inbuf->inputstate & INS_command)
+                       dev_notice(cs->dev,
+                                  "received <DLE>X in command mode\n");
+               inbuf->inputstate |= INS_command | INS_DLE_command;
+               inbuf->head++;  /* byte consumed */
+               break;
+       case '.':       /* end of event message */
+               if (!(inbuf->inputstate & INS_DLE_command))
+                       dev_notice(cs->dev,
+                                  "received <DLE>. without <DLE>X\n");
+               inbuf->inputstate &= ~INS_DLE_command;
+               /* return to data mode if in DLE mode */
+               if (cs->dle)
+                       inbuf->inputstate &= ~INS_command;
+               inbuf->head++;  /* byte consumed */
+               break;
+       case DLE_FLAG:  /* DLE in data stream */
+               /* mark as quoted */
+               inbuf->inputstate |= INS_DLE_char;
+               if (!(cs->dle || inbuf->inputstate & INS_DLE_command))
+                       dev_notice(cs->dev,
+                                  "received <DLE><DLE> not in DLE mode\n");
+               break;  /* quoted byte left in buffer */
+       default:
+               dev_notice(cs->dev, "received <DLE><%02x>\n",
+                          inbuf->data[inbuf->head]);
+               /* quoted byte left in buffer */
+       }
 }
 
 /**
@@ -345,94 +425,39 @@ static inline int iraw_loop(unsigned char c, unsigned char *src, int numbytes,
  */
 void gigaset_m10x_input(struct inbuf_t *inbuf)
 {
-       struct cardstate *cs;
-       unsigned tail, head, numbytes;
-       unsigned char *src, c;
-       int procbytes;
-
-       head = inbuf->head;
-       tail = 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);
+       struct cardstate *cs = inbuf->cs;
+       unsigned numbytes, procbytes;
 
-               while (numbytes) {
-                       if (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;
-                               }
+       gig_dbg(DEBUG_INTR, "buffer state: %u -> %u", inbuf->head, inbuf->tail);
 
-                               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*/
-                                               if (inbuf->inputstate & INS_command)
-                                                       dev_warn(cs->dev,
-                                       "received <DLE> 'X' in command mode\n");
-                                               inbuf->inputstate |=
-                                                       INS_command | INS_DLE_command;
-                                               break;
-                                       case '.': /*end of command*/
-                                               if (!(inbuf->inputstate & INS_command))
-                                                       dev_warn(cs->dev,
-                                       "received <DLE> '.' in hdlc mode\n");
-                                               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;
-                               }
-                       }
-               }
+       while (inbuf->head != inbuf->tail) {
+               /* check for DLE escape */
+               handle_dle(inbuf);
 
-               gig_dbg(DEBUG_INTR, "setting head to %u", head);
-               inbuf->head = head;
+               /* process a contiguous block of bytes */
+               numbytes = (inbuf->head > inbuf->tail ?
+                           RBUFSIZE : inbuf->tail) - inbuf->head;
+               gig_dbg(DEBUG_INTR, "processing %u bytes", numbytes);
+               /*
+                * numbytes may be 0 if handle_dle() ate the last byte.
+                * This does no harm, *_loop() will just return 0 immediately.
+                */
+
+               if (cs->mstate == MS_LOCKED)
+                       procbytes = lock_loop(numbytes, inbuf);
+               else if (inbuf->inputstate & INS_command)
+                       procbytes = cmd_loop(numbytes, inbuf);
+               else if (cs->bcs->proto2 == L2_HDLC)
+                       procbytes = hdlc_loop(numbytes, inbuf);
+               else
+                       procbytes = iraw_loop(numbytes, inbuf);
+               inbuf->head += procbytes;
+
+               /* check for buffer wraparound */
+               if (inbuf->head >= RBUFSIZE)
+                       inbuf->head = 0;
+
+               gig_dbg(DEBUG_INTR, "head set to %u", inbuf->head);
        }
 }
 EXPORT_SYMBOL_GPL(gigaset_m10x_input);
@@ -440,16 +465,16 @@ EXPORT_SYMBOL_GPL(gigaset_m10x_input);
 
 /* == data output ========================================================== */
 
-/* Encoding of a PPP packet into an octet stuffed HDLC frame
- * with FCS, opening and closing flags.
+/*
+ * Encode a data packet into an octet stuffed HDLC frame with FCS,
+ * opening and closing flags, preserving headroom data.
  * 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
+ *     skb             skb containing original packet (freed upon return)
  * Return value:
  *     pointer to newly allocated skb containing the result frame
+ *     and the original link layer header, NULL on error
  */
-static struct sk_buff *HDLC_Encode(struct sk_buff *skb, int head, int tail)
+static struct sk_buff *HDLC_Encode(struct sk_buff *skb)
 {
        struct sk_buff *hdlc_skb;
        __u16 fcs;
@@ -471,16 +496,19 @@ static struct sk_buff *HDLC_Encode(struct sk_buff *skb, int head, int tail)
 
        /* size of new buffer: original size + number of stuffing bytes
         * + 2 bytes FCS + 2 stuffing bytes for FCS (if needed) + 2 flag bytes
+        * + room for link layer header
         */
-       hdlc_skb = dev_alloc_skb(skb->len + stuf_cnt + 6 + tail + head);
+       hdlc_skb = dev_alloc_skb(skb->len + stuf_cnt + 6 + skb->mac_len);
        if (!hdlc_skb) {
-               dev_kfree_skb(skb);
+               dev_kfree_skb_any(skb);
                return NULL;
        }
-       skb_reserve(hdlc_skb, head);
 
-       /* Copy acknowledge request into new skb */
-       memcpy(hdlc_skb->head, skb->head, 2);
+       /* Copy link layer header into new skb */
+       skb_reset_mac_header(hdlc_skb);
+       skb_reserve(hdlc_skb, skb->mac_len);
+       memcpy(skb_mac_header(hdlc_skb), skb_mac_header(skb), skb->mac_len);
+       hdlc_skb->mac_len = skb->mac_len;
 
        /* Add flag sequence in front of everything.. */
        *(skb_put(hdlc_skb, 1)) = PPP_FLAG;
@@ -511,33 +539,42 @@ static struct sk_buff *HDLC_Encode(struct sk_buff *skb, int head, int tail)
 
        *(skb_put(hdlc_skb, 1)) = PPP_FLAG;
 
-       dev_kfree_skb(skb);
+       dev_kfree_skb_any(skb);
        return hdlc_skb;
 }
 
-/* Encoding of a raw packet into an octet stuffed bit inverted frame
+/*
+ * Encode a data packet into an octet stuffed raw bit inverted frame,
+ * preserving headroom data.
  * 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
+ *     skb             skb containing original packet (freed upon return)
  * Return value:
  *     pointer to newly allocated skb containing the result frame
+ *     and the original link layer header, NULL on error
  */
-static struct sk_buff *iraw_encode(struct sk_buff *skb, int head, int tail)
+static struct sk_buff *iraw_encode(struct sk_buff *skb)
 {
        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);
+       /* size of new buffer (worst case = every byte must be stuffed):
+        * 2 * original size + room for link layer header
+        */
+       iraw_skb = dev_alloc_skb(2*skb->len + skb->mac_len);
        if (!iraw_skb) {
-               dev_kfree_skb(skb);
+               dev_kfree_skb_any(skb);
                return NULL;
        }
-       skb_reserve(iraw_skb, head);
 
+       /* copy link layer header into new skb */
+       skb_reset_mac_header(iraw_skb);
+       skb_reserve(iraw_skb, skb->mac_len);
+       memcpy(skb_mac_header(iraw_skb), skb_mac_header(skb), skb->mac_len);
+       iraw_skb->mac_len = skb->mac_len;
+
+       /* copy and stuff data */
        cp = skb->data;
        len = skb->len;
        while (len--) {
@@ -546,7 +583,7 @@ static struct sk_buff *iraw_encode(struct sk_buff *skb, int head, int tail)
                        *(skb_put(iraw_skb, 1)) = c;
                *(skb_put(iraw_skb, 1)) = c;
        }
-       dev_kfree_skb(skb);
+       dev_kfree_skb_any(skb);
        return iraw_skb;
 }
 
@@ -555,8 +592,10 @@ static struct sk_buff *iraw_encode(struct sk_buff *skb, int head, int tail)
  * @bcs:       B channel descriptor structure.
  * @skb:       data to send.
  *
- * Called by i4l.c to encode and queue an skb for sending, and start
+ * Called by LL to encode and queue an skb for sending, and start
  * transmission if necessary.
+ * Once the payload data has been transmitted completely, gigaset_skb_sent()
+ * will be called with the skb's link layer header preserved.
  *
  * Return value:
  *     number of bytes accepted for sending (skb->len) if ok,
@@ -564,24 +603,25 @@ static struct sk_buff *iraw_encode(struct sk_buff *skb, int head, int tail)
  */
 int gigaset_m10x_send_skb(struct bc_state *bcs, struct sk_buff *skb)
 {
+       struct cardstate *cs = bcs->cs;
        unsigned len = skb->len;
        unsigned long flags;
 
-       if (bcs->proto2 == ISDN_PROTO_L2_HDLC)
-               skb = HDLC_Encode(skb, HW_HDR_LEN, 0);
+       if (bcs->proto2 == L2_HDLC)
+               skb = HDLC_Encode(skb);
        else
-               skb = iraw_encode(skb, HW_HDR_LEN, 0);
+               skb = iraw_encode(skb);
        if (!skb) {
-               dev_err(bcs->cs->dev,
+               dev_err(cs->dev,
                        "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);
+       spin_lock_irqsave(&cs->lock, flags);
+       if (cs->connected)
+               tasklet_schedule(&cs->write_tasklet);
+       spin_unlock_irqrestore(&cs->lock, flags);
 
        return len;     /* ok so far */
 }