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