linux-old/drivers/usb/auerisdn.c
<<
>>
Prefs
   1/*****************************************************************************/
   2/*
   3 *      auerisdn.c  --  Auerswald PBX/System Telephone ISDN interface.
   4 *
   5 *      Copyright (C) 2002  Wolfgang Mües (wolfgang@iksw-muees.de)
   6 *
   7 *      This program is free software; you can redistribute it and/or modify
   8 *      it under the terms of the GNU General Public License as published by
   9 *      the Free Software Foundation; either version 2 of the License, or
  10 *      (at your option) any later version.
  11 *
  12 *      This program is distributed in the hope that it will be useful,
  13 *      but WITHOUT ANY WARRANTY; without even the implied warranty of
  14 *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  15 *      GNU General Public License for more details.
  16 *
  17 *      You should have received a copy of the GNU General Public License
  18 *      along with this program; if not, write to the Free Software
  19 *      Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  20 */
  21 /*****************************************************************************/
  22
  23#include <linux/isdnif.h>
  24#include <linux/netdevice.h>
  25#include <linux/sched.h>
  26
  27#undef  DEBUG                   /* include debug macros until it's done */
  28#include <linux/usb.h>
  29
  30#include "auerisdn.h"
  31#include "auermain.h"
  32
  33/*-------------------------------------------------------------------*/
  34/* ISDN support defines                                              */
  35#define AUISDN_TEI      64      /* use a constant TEI */
  36
  37/*-------------------------------------------------------------------*/
  38/* Debug support                                                     */
  39#ifdef DEBUG
  40#define dump( desc, adr, len) \
  41do {                    \
  42        unsigned int u; \
  43        printk (KERN_DEBUG); \
  44        printk (desc); \
  45        for (u = 0; u < len; u++) \
  46                printk (" %02X", adr[u] & 0xFF); \
  47        printk ("\n"); \
  48} while (0)
  49#else
  50#define dump( desc, adr, len)
  51#endif
  52
  53/*-------------------------------------------------------------------*/
  54/* Hisax Interface.                                                  */
  55
  56/* The interface to hisax is long-lasting because hisax_unregister()
  57   don't work well in Linux 2.4.x. So we have to hold each registered
  58   hisax interface until driver removal. */
  59static struct auerhisax auerhisax_table[AUER_MAX_DEVICES];
  60
  61
  62/*-------------------------------------------------------------------*/
  63
  64/* Callback to L2 for HISAX */
  65/* This callback can be called from 3 sources:
  66   a) from hisax context (answer from a l2l1 function)
  67   b) from interrupt context (a D channel paket arrived)
  68   c) from kernel daemon context (probe/disconnecting)
  69*/
  70static void auerisdn_d_l1l2(struct auerisdn *ip, int pr, void *arg)
  71{
  72        struct sk_buff *skb;
  73        struct auerhisax *ahp;
  74
  75        /* do the callback */
  76        ahp = ip->ahp;
  77        if (ahp) {
  78                ahp->hisax_d_if.ifc.l1l2(&ahp->hisax_d_if.ifc, pr, arg);
  79        } else {
  80                dbg("auerisdn_d_l1l2 with ahp == NULL");
  81                if (pr == (PH_DATA | INDICATION)) {
  82                        skb = (struct sk_buff *) arg;
  83                        if (skb) {
  84                                skb_pull(skb, skb->len);
  85                                dev_kfree_skb_any(skb);
  86                        }
  87                }
  88        }
  89}
  90
  91
  92/* D-Channel sending completion function */
  93static void auerisdn_dcw_complete(struct urb *urb)
  94{
  95        struct auerbuf *bp = (struct auerbuf *) urb->context;
  96        struct auerswald *cp =
  97            ((struct auerswald *) ((char *) (bp->list) -
  98                                   (unsigned
  99                                    long) (&((struct auerswald *) 0)->
 100                                           bufctl)));
 101
 102        dbg("auerisdn_dcw_complete with status %d", urb->status);
 103
 104        /* reuse the buffer */
 105        auerbuf_releasebuf(bp);
 106
 107        /* Wake up all processes waiting for a buffer */
 108        wake_up(&cp->bufferwait);
 109}
 110
 111
 112/* Translate non-ETSI ISDN messages from the device */
 113static void auerisdn_translate_incoming(struct auerswald *cp,
 114                                        unsigned char *msg,
 115                                        unsigned int len)
 116{
 117        struct auerbuf *bp;
 118        int ret;
 119
 120        /* Translate incomming CONNECT -> CONNECT_ACK */
 121        /* Format:   0   1    2     3     4        5        6    7      */
 122        /*         SAPI TEI TXSEQ RXSEQ PID=08 CREFLEN=01 CREF MSG=7 ...*/
 123        /* CREF.7 == 0 -> Incoming Call                                 */
 124
 125        /* Check for minimum length */
 126        if (len < 8)
 127                return;
 128
 129        /* Check for a CONNECT, call originated from device */
 130        if (((msg[6] & 0x80) == 0) && (msg[7] == 0x07)) {
 131                dbg("false CONNECT from device found");
 132                /* change into CONNECT_ACK */
 133                msg[7] = 0x0F;
 134
 135                /* Send a CONNECT_ACK back to the device */
 136
 137                /* get a new data buffer */
 138                bp = auerbuf_getbuf(&cp->bufctl);
 139                if (!bp) {
 140                        warn("no auerbuf free");
 141                        return;
 142                }
 143
 144                /* Form a CONNECT ACK */
 145                bp->bufp[0] =
 146                    cp->isdn.dchannelservice.id | AUH_DIRECT | AUH_UNSPLIT;
 147                bp->bufp[1] = 0x08;
 148                bp->bufp[2] = 0x01;
 149                bp->bufp[3] = msg[6] | 0x80;
 150                bp->bufp[4] = 0x0F;
 151
 152                /* Set the transfer Parameters */
 153                bp->len = 5;
 154                bp->dr->bRequestType = AUT_WREQ;
 155                bp->dr->bRequest = AUV_WBLOCK;
 156                bp->dr->wValue = cpu_to_le16(0);
 157                bp->dr->wIndex =
 158                    cpu_to_le16(cp->isdn.dchannelservice.
 159                                id | AUH_DIRECT | AUH_UNSPLIT);
 160                bp->dr->wLength = cpu_to_le16(5);
 161                FILL_CONTROL_URB(bp->urbp, cp->usbdev,
 162                                 usb_sndctrlpipe(cp->usbdev, 0),
 163                                 (unsigned char *) bp->dr, bp->bufp, 5,
 164                                 auerisdn_dcw_complete, bp);
 165                /* up we go */
 166                ret = auerchain_submit_urb(&cp->controlchain, bp->urbp);
 167                if (ret)
 168                        auerisdn_dcw_complete(bp->urbp);
 169                else
 170                        dbg("auerisdn_translate: Write OK");
 171        }
 172        /* Check for a DISCONNECT and change to RELEASE */
 173        if (msg[7] == 0x45) {
 174                dbg("DISCONNECT changed to RELEASE");
 175                msg[7] = 0x4D;
 176                return;
 177        }
 178}
 179
 180
 181/* a D-channel paket arrived from the device */
 182static void auerisdn_dispatch_dc(struct auerscon *scp, struct auerbuf *bp)
 183{
 184        struct sk_buff *skb;
 185        struct auerhisax *ahp;
 186        struct auerswald *cp =
 187            ((struct auerswald *) ((char *) (scp) -
 188                                   (unsigned
 189                                    long) (&((struct auerswald *) 0)->isdn.
 190                                           dchannelservice)));
 191        unsigned char *sp;
 192        unsigned int l2_index;
 193        unsigned char c;
 194        unsigned char l2_header[10];
 195        unsigned long flags;
 196
 197        dump("D-Channel paket arrived:", bp->bufp, bp->len);
 198        if (cp->disconnecting)
 199                return;
 200
 201        /* add a self-generated L2 message header */
 202        l2_index = 0;
 203        l2_header[l2_index++] = 0x02;   /* SAPI 0, C/R = 1 */
 204
 205        /* Parse the L3 message */
 206        sp = bp->bufp + AUH_SIZE;
 207
 208        c = *sp++;              /* Protocol discriminator */
 209        if (c != 0x08) {
 210                warn("D channel paket is not ETSI");
 211                return;
 212        }
 213        c = *sp++;              /* Call Reference length byte */
 214        sp += c;                /* Skip Call Reference */
 215
 216        /* translate charge IEs */
 217        /* Format of Auerswald Header:
 218           0x32 len=0x0B 0xFF 0xFF 0x73 len=0x07 0x27 */
 219        /* Format of IE2_UNIT:
 220           0x49 len=0x04 uu1 uu2 uu3 uu4 */
 221        /* Translate into: (?? Bytes)
 222           0x1C Facility
 223           0x?? restlen
 224           0x91 Sup. Services
 225           0xA1 Invoke
 226           0x?? restlen
 227           0x02 Invoke ID Tag
 228           0x02 Invoke ID len
 229           0x12 Invoke ID = 0x1234
 230           0x34
 231           0x02 OP Value Tag
 232           0x01 Length of OPvalue
 233           0x24 OpValue = AOCE
 234           0x30 Universal Constructor Sequence
 235           0x?? restlen
 236           0x30 Universal Constructor Sequence
 237           0x?? restlen
 238           0xA1 Context Specific Constructor Recorded Units List
 239           0x?? restlen
 240           0x30 Universal Constructor Sequence
 241           0x?? restlen
 242           0x02 Universal Primitive Integer
 243           0x?? len from IE2_UNIT
 244           uu1  Recorded Units List
 245           uu2
 246           uu3
 247           uu4
 248         */
 249        {
 250                unsigned char *ucp = sp;        // pointer to start of msg
 251                int l = bp->len;        // length until EOP
 252                unsigned char alen;     // length of auerswald msg
 253                l -= (int) (ucp - bp->bufp);
 254                // scan for Auerswald Header
 255                for (; l >= 9; l--, ucp++) {    // 9 = minimal length of auerswald msg
 256                        if (ucp[0] != 0x32)
 257                                continue;
 258                        if (ucp[2] != 0xFF)
 259                                continue;
 260                        if (ucp[3] != 0xFF)
 261                                continue;
 262                        if (ucp[4] != 0x73)
 263                                continue;
 264                        if (ucp[6] != 0x27)
 265                                continue;
 266                        // Auerswald Header found. Is it units?
 267                        dbg("Auerswald msg header found");
 268                        alen = ucp[1] + 2;
 269                        if (ucp[7] == 0x49) {
 270                                // yes
 271                                unsigned char ul = ucp[8] + 1;  // length of charge integer
 272                                unsigned char charge[32];
 273                                // Copy charge info into new buffer
 274                                unsigned char *xp = &ucp[8];
 275                                int count;
 276                                for (count = 0; count < ul; count++)
 277                                        charge[count] = *xp++;
 278                                // Erase auerswald msg
 279                                count = l - alen;
 280                                xp = ucp;
 281                                for (; count; count--, xp++)
 282                                        xp[0] = xp[alen];
 283                                l -= alen;
 284                                bp->len -= alen;
 285                                // make room for new message
 286                                count = l;
 287                                xp = &ucp[l - 1];
 288                                for (; count; count--, xp--);
 289                                xp[21 + ul] = xp[0];
 290                                l += (21 + ul);
 291                                bp->len += (21 + ul);
 292                                // insert IE header
 293                                ucp[0] = 0x1C;
 294                                ucp[1] = 19 + ul;
 295                                ucp[2] = 0x91;
 296                                ucp[3] = 0xA1;
 297                                ucp[4] = 16 + ul;
 298                                ucp[5] = 0x02;
 299                                ucp[6] = 0x02;
 300                                ucp[7] = 0x12;
 301                                ucp[8] = 0x34;
 302                                ucp[9] = 0x02;
 303                                ucp[10] = 0x01;
 304                                ucp[11] = 0x24;
 305                                ucp[12] = 0x30;
 306                                ucp[13] = 7 + ul;
 307                                ucp[14] = 0x30;
 308                                ucp[15] = 5 + ul;
 309                                ucp[16] = 0xA1;
 310                                ucp[17] = 3 + ul;
 311                                ucp[18] = 0x30;
 312                                ucp[19] = 1 + ul;
 313                                ucp[20] = 0x02;
 314                                // Insert charge units
 315                                xp = &ucp[21];
 316                                for (count = 0; count < ul; count++)
 317                                        *xp++ = charge[count];
 318                                dump("Rearranged message:", bp->bufp,
 319                                     bp->len);
 320                                break;
 321                        } else {
 322                                // we can't handle something else, erase it
 323                                int count = l - alen;
 324                                unsigned char *xp = ucp;
 325                                for (; count; count--, xp++)
 326                                        xp[0] = xp[alen];
 327                                l -= alen;
 328                                bp->len -= alen;
 329                                dump("Shortened message:", bp->bufp,
 330                                     bp->len);
 331                        }
 332                }
 333        }
 334
 335
 336        c = *sp;                /* Message type */
 337        if (c == 0x05) {
 338                /* SETUP. Use an UI frame */
 339                dbg("SETUP");
 340                l2_header[l2_index++] = 0xFF;   /* TEI 127 */
 341                l2_header[l2_index++] = 0x03;   /* UI control field */
 342                skb = dev_alloc_skb(bp->len - AUH_SIZE + l2_index);
 343        } else {
 344                /* use an I frame */
 345                dbg("I Frame");
 346                l2_header[l2_index++] = (AUISDN_TEI << 1) | 0x01;       /* TEI byte */
 347                skb = dev_alloc_skb(bp->len - AUH_SIZE + l2_index + 2);
 348                if (skb) {
 349                        ahp = cp->isdn.ahp;
 350                        if (!ahp) {
 351                                err("ahp == NULL");
 352                                return;
 353                        }
 354                        spin_lock_irqsave(&ahp->seq_lock, flags);
 355                        l2_header[l2_index++] = ahp->txseq;     /* transmitt sequence number */
 356                        l2_header[l2_index++] = ahp->rxseq;     /* receive sequence number */
 357                        ahp->txseq += 2;                        /* next paket gets next number */
 358                        spin_unlock_irqrestore(&ahp->seq_lock, flags);
 359                }
 360        }
 361        if (!skb) {
 362                err("no memory - skipped");
 363                return;
 364        }
 365        sp = skb_put(skb, bp->len - AUH_SIZE + l2_index);
 366        /* Add L2 header */
 367        memcpy(sp, l2_header, l2_index);
 368        memcpy(sp + l2_index, bp->bufp + AUH_SIZE, bp->len - AUH_SIZE);
 369        /* Translate false messages */
 370        auerisdn_translate_incoming(cp, sp, bp->len - AUH_SIZE + l2_index);
 371        /* Send message to L2 */
 372        auerisdn_d_l1l2(&cp->isdn, PH_DATA | INDICATION, skb);
 373}
 374
 375/* D-channel is closed because the device is removed */
 376/* This is a no-op because ISDN close is handled different */
 377static void auerisdn_disconnect_dc(struct auerscon *scp)
 378{
 379}
 380
 381
 382/* confirmation helper function. */
 383static void auerisdn_d_confirmskb(struct auerswald *cp,
 384                                  struct sk_buff *skb)
 385{
 386        /* free the skb */
 387        if (skb) {
 388                skb_pull(skb, skb->len);
 389                dev_kfree_skb_any(skb);
 390        }
 391
 392        /* confirm the sending of data */
 393        dbg("Confirm PH_DATA");
 394        auerisdn_d_l1l2(&cp->isdn, PH_DATA | CONFIRM, NULL);
 395}
 396
 397/* D-channel transfer function L2->L1 */
 398static void auerisdn_d_l2l1(struct hisax_if *hisax_d_if, int pr, void *arg)
 399{
 400        struct auerhisax *ahp;
 401        struct sk_buff *skb;
 402        unsigned int len;
 403        int ret;
 404        struct auerbuf *bp;
 405        struct auerswald *cp;
 406        unsigned long flags;
 407        unsigned int l2_index;
 408        unsigned char c;
 409        unsigned char l2_header[32];
 410        unsigned char *sp;
 411
 412        dbg("hisax D-Channel l2l1 called");
 413
 414        /* Get reference to auerhisax struct */
 415        cp = NULL;
 416        ahp = hisax_d_if->priv;
 417        if (ahp)
 418                cp = ahp->cp;
 419        if (cp && !cp->disconnecting) {
 420                /* normal usage */
 421                switch (pr) {
 422                case PH_ACTIVATE | REQUEST:     /* activation request */
 423                        dbg("Activation Request");
 424                        cp->isdn.dc_activated = 1;
 425                        /* send activation back to layer 2 */
 426                        auerisdn_d_l1l2(&cp->isdn,
 427                                        PH_ACTIVATE | INDICATION, NULL);
 428                        break;
 429                case PH_DEACTIVATE | REQUEST:   /* deactivation request */
 430                        dbg("Deactivation Request");
 431                        cp->isdn.dc_activated = 0;
 432                        /* send deactivation back to layer 2 */
 433                        auerisdn_d_l1l2(&cp->isdn,
 434                                        PH_DEACTIVATE | INDICATION, NULL);
 435                        break;
 436                case PH_DATA | REQUEST: /* Transmit data request */
 437                        skb = (struct sk_buff *) arg;
 438                        len = skb->len;
 439                        l2_index = 0;
 440                        sp = skb->data;
 441                        dump("Data Request:", sp, len);
 442
 443                        /* Parse the L2 header */
 444                        if (!len)
 445                                goto phd_free;
 446                        c = *sp++;      /* SAPI */
 447                        l2_header[l2_index++] = c;
 448                        len--;
 449                        if (!len)
 450                                goto phd_free;
 451                        c = *sp++;      /* TEI */
 452                        l2_header[l2_index++] = c;
 453                        len--;
 454                        if (!len)
 455                                goto phd_free;
 456                        c = *sp++;      /* Control Field, Byte 1 */
 457                        len--;
 458                        if (!(c & 0x01)) {
 459                                /* I FRAME */
 460                                dbg("I Frame");
 461                                if (!len)
 462                                        goto phd_free;
 463                                spin_lock_irqsave(&ahp->seq_lock, flags);
 464                                ahp->rxseq = c + 2;     /* store new sequence info */
 465                                spin_unlock_irqrestore(&ahp->seq_lock,
 466                                                       flags);
 467                                sp++;   /* skip Control Field, Byte 2 */
 468                                len--;
 469                                /* Check for RELEASE command */
 470                                /* and change to RELEASE_COMPLETE */
 471                                if (sp[3] == 0x4D)
 472                                        sp[3] = 0x5A;
 473                                goto phd_send;
 474                        }
 475                        /* check the frame type */
 476                        switch (c) {
 477                        case 0x03:      /* UI frame */
 478                                dbg("UI Frame");
 479                                if (l2_header[0] == 0xFC) {
 480                                        dbg("TEI Managment");
 481                                        l2_header[0] = 0xFE;    /* set C/R bit in answer */
 482                                        l2_header[l2_index++] = c;      /* Answer is UI frame */
 483                                        if (!len)
 484                                                break;
 485                                        c = *sp++;      /* Managment ID */
 486                                        len--;
 487                                        if (c != 0x0F)
 488                                                break;
 489                                        l2_header[l2_index++] = c;
 490                                        /* Read Reference Number */
 491                                        if (!len)
 492                                                break;
 493                                        l2_header[l2_index++] = *sp++;
 494                                        len--;
 495                                        if (!len)
 496                                                break;
 497                                        l2_header[l2_index++] = *sp++;
 498                                        len--;
 499                                        if (!len)
 500                                                break;
 501                                        c = *sp++;      /* Message Type */
 502                                        len--;
 503                                        switch (c) {
 504                                        case 0x01:      /* Identity Request */
 505                                                dbg("Identity Request");
 506                                                l2_header[l2_index++] = 0x02;   /* Identity Assign */
 507                                                l2_header[l2_index++] =
 508                                                    (AUISDN_TEI << 1) |
 509                                                    0x01;
 510                                                goto phd_answer;
 511                                        default:
 512                                                dbg("Unhandled TEI Managment %X", (int) c);
 513                                                break;
 514                                        }
 515                                        // throw away
 516                                        goto phd_free;
 517                                }
 518                                /* else send UI frame out */
 519                                goto phd_send;
 520                        case 0x01:      /* RR frame */
 521                        case 0x05:      /* RNR frame */
 522                                dbg("RR/RNR Frame");
 523                                if (!len)
 524                                        break;
 525                                c = *sp++;      /* Control Field, Byte 2 */
 526                                len--;
 527                                if (!(c & 0x01))
 528                                        break;  /* P/F = 1 in commands */
 529                                if (l2_header[0] & 0x02)
 530                                        break;  /* C/R = 0 from TE */
 531                                dbg("Send RR as answer");
 532                                l2_header[l2_index++] = 0x01;   /* send an RR as Answer */
 533                                spin_lock_irqsave(&ahp->seq_lock, flags);
 534                                l2_header[l2_index++] = ahp->rxseq | 0x01;
 535                                spin_unlock_irqrestore(&ahp->seq_lock,
 536                                                       flags);
 537                                goto phd_answer;
 538                        case 0x7F:      /* SABME */
 539                                dbg("SABME");
 540                                spin_lock_irqsave(&ahp->seq_lock, flags);
 541                                ahp->txseq = 0;
 542                                ahp->rxseq = 0;
 543                                spin_unlock_irqrestore(&ahp->seq_lock,
 544                                                       flags);
 545                                l2_header[l2_index++] = 0x73;   /* UA */
 546                                goto phd_answer;
 547                        case 0x53:      /* DISC */
 548                                dbg("DISC");
 549                                /* Send back a UA */
 550                                l2_header[l2_index++] = 0x73;   /* UA */
 551                                goto phd_answer;
 552                        default:
 553                                dbg("Unhandled L2 Message %X", (int) c);
 554                                break;
 555                        }
 556                        /* all done */
 557                        goto phd_free;
 558
 559                        /* we have to generate a local answer */
 560                        /* first, confirm old message, free old skb */
 561                      phd_answer:auerisdn_d_confirmskb(cp,
 562                                              skb);
 563
 564                        /* allocate a new skbuff */
 565                        skb = dev_alloc_skb(l2_index);
 566                        if (!skb) {
 567                                err("no memory for new skb");
 568                                break;
 569                        }
 570                        dump("local answer to L2 is:", l2_header,
 571                             l2_index);
 572                        memcpy(skb_put(skb, l2_index), l2_header,
 573                               l2_index);
 574                        auerisdn_d_l1l2(&cp->isdn, PH_DATA | INDICATION,
 575                                        skb);
 576                        break;
 577
 578                        /* we have to send the L3 message out */
 579                      phd_send:if (!len)
 580                                goto phd_free;  /* no message left */
 581
 582                        /* get a new data buffer */
 583                        bp = auerbuf_getbuf(&cp->bufctl);
 584                        if (!bp) {
 585                                warn("no auerbuf free");
 586                                goto phd_free;
 587                        }
 588                        /* protect against too big write requests */
 589                        /* Should not happen */
 590                        if (len > cp->maxControlLength) {
 591                                err("too long D-channel paket truncated");
 592                                len = cp->maxControlLength;
 593                        }
 594
 595                        /* Copy the data */
 596                        memcpy(bp->bufp + AUH_SIZE, sp, len);
 597
 598                        /* set the header byte */
 599                        *(bp->bufp) =
 600                            cp->isdn.dchannelservice.
 601                            id | AUH_DIRECT | AUH_UNSPLIT;
 602
 603                        /* Set the transfer Parameters */
 604                        bp->len = len + AUH_SIZE;
 605                        bp->dr->bRequestType = AUT_WREQ;
 606                        bp->dr->bRequest = AUV_WBLOCK;
 607                        bp->dr->wValue = cpu_to_le16(0);
 608                        bp->dr->wIndex =
 609                            cpu_to_le16(cp->isdn.dchannelservice.
 610                                        id | AUH_DIRECT | AUH_UNSPLIT);
 611                        bp->dr->wLength = cpu_to_le16(len + AUH_SIZE);
 612                        FILL_CONTROL_URB(bp->urbp, cp->usbdev,
 613                                         usb_sndctrlpipe(cp->usbdev, 0),
 614                                         (unsigned char *) bp->dr,
 615                                         bp->bufp, len + AUH_SIZE,
 616                                         auerisdn_dcw_complete, bp);
 617                        /* up we go */
 618                        ret =
 619                            auerchain_submit_urb(&cp->controlchain,
 620                                                 bp->urbp);
 621                        if (ret)
 622                                auerisdn_dcw_complete(bp->urbp);
 623                        else
 624                                dbg("auerisdn_dwrite: Write OK");
 625                        /* confirm message, free skb */
 626                      phd_free:auerisdn_d_confirmskb(cp,
 627                                              skb);
 628                        break;
 629
 630                default:
 631                        warn("pr %#x\n", pr);
 632                        break;
 633                }
 634        } else {
 635                /* hisax interface is down */
 636                switch (pr) {
 637                case PH_ACTIVATE | REQUEST:     /* activation request */
 638                        dbg("D channel PH_ACTIVATE | REQUEST with interface down");
 639                        /* don't answer this request! Endless... */
 640                        break;
 641                case PH_DEACTIVATE | REQUEST:   /* deactivation request */
 642                        dbg("D channel PH_DEACTIVATE | REQUEST with interface down");
 643                        hisax_d_if->l1l2(hisax_d_if,
 644                                         PH_DEACTIVATE | INDICATION, NULL);
 645                        break;
 646                case PH_DATA | REQUEST: /* Transmit data request */
 647                        dbg("D channel PH_DATA | REQUEST with interface down");
 648                        skb = (struct sk_buff *) arg;
 649                        /* free data buffer */
 650                        if (skb) {
 651                                skb_pull(skb, skb->len);
 652                                dev_kfree_skb_any(skb);
 653                        }
 654                        /* send confirmation back to layer 2 */
 655                        hisax_d_if->l1l2(hisax_d_if, PH_DATA | CONFIRM,
 656                                         NULL);
 657                        break;
 658                default:
 659                        warn("pr %#x\n", pr);
 660                        break;
 661                }
 662        }
 663}
 664
 665
 666/* Completion function for D channel open */
 667static void auerisdn_dcopen_complete(struct urb *urbp)
 668{
 669        struct auerbuf *bp = (struct auerbuf *) urbp->context;
 670        struct auerswald *cp =
 671            ((struct auerswald *) ((char *) (bp->list) -
 672                                   (unsigned
 673                                    long) (&((struct auerswald *) 0)->
 674                                           bufctl)));
 675        dbg("auerisdn_dcopen_complete called");
 676
 677        auerbuf_releasebuf(bp);
 678
 679        /* Wake up all processes waiting for a buffer */
 680        wake_up(&cp->bufferwait);
 681}
 682
 683
 684/* Open the D-channel once more */
 685static void auerisdn_dcopen(unsigned long data)
 686{
 687        struct auerswald *cp = (struct auerswald *) data;
 688        struct auerbuf *bp;
 689        int ret;
 690
 691        if (cp->disconnecting)
 692                return;
 693        dbg("auerisdn_dcopen running");
 694
 695        /* get a buffer for the command */
 696        bp = auerbuf_getbuf(&cp->bufctl);
 697        /* if no buffer available: can't change the mode */
 698        if (!bp) {
 699                err("auerisdn_dcopen: no data buffer available");
 700                return;
 701        }
 702
 703        /* fill the control message */
 704        bp->dr->bRequestType = AUT_WREQ;
 705        bp->dr->bRequest = AUV_CHANNELCTL;
 706        bp->dr->wValue = cpu_to_le16(1);
 707        bp->dr->wIndex = cpu_to_le16(0);
 708        bp->dr->wLength = cpu_to_le16(0);
 709        FILL_CONTROL_URB(bp->urbp, cp->usbdev,
 710                         usb_sndctrlpipe(cp->usbdev, 0),
 711                         (unsigned char *) bp->dr, bp->bufp, 0,
 712                         (usb_complete_t) auerisdn_dcopen_complete, bp);
 713
 714        /* submit the control msg */
 715        ret = auerchain_submit_urb(&cp->controlchain, bp->urbp);
 716        dbg("dcopen submitted");
 717        if (ret) {
 718                bp->urbp->status = ret;
 719                auerisdn_dcopen_complete(bp->urbp);
 720        }
 721        return;
 722}
 723
 724
 725/* Initialize the isdn related items in struct auerswald */
 726void auerisdn_init_dev(struct auerswald *cp)
 727{
 728        unsigned int u;
 729        cp->isdn.dchannelservice.id = AUH_UNASSIGNED;
 730        cp->isdn.dchannelservice.dispatch = auerisdn_dispatch_dc;
 731        cp->isdn.dchannelservice.disconnect = auerisdn_disconnect_dc;
 732        init_timer(&cp->isdn.dcopen_timer);
 733        cp->isdn.dcopen_timer.data = (unsigned long) cp;
 734        cp->isdn.dcopen_timer.function = auerisdn_dcopen;
 735        for (u = 0; u < AUISDN_BCHANNELS; u++) {
 736                cp->isdn.bc[u].cp = cp;
 737                cp->isdn.bc[u].mode = L1_MODE_NULL;
 738                cp->isdn.bc[u].channel = u;
 739                spin_lock_init(&cp->isdn.bc[u].txskb_lock);
 740        }
 741}
 742
 743
 744/* Connect to the HISAX interface. Returns 0 if successfull */
 745int auerisdn_probe(struct auerswald *cp)
 746{
 747        struct hisax_b_if *b_if[AUISDN_BCHANNELS];
 748        struct usb_endpoint_descriptor *ep;
 749        struct auerhisax *ahp;
 750        DECLARE_WAIT_QUEUE_HEAD(wqh);
 751        unsigned int u;
 752        unsigned char *ucp;
 753        unsigned int first_time;
 754        int ret;
 755
 756        /* First allocate resources, then register hisax interface */
 757
 758        /* Allocate RX buffers */
 759        for (u = 0; u < AUISDN_BCHANNELS; u++) {
 760                if (!cp->isdn.bc[u].rxbuf) {
 761                        cp->isdn.bc[u].rxbuf =
 762                            (char *) kmalloc(AUISDN_RXSIZE, GFP_KERNEL);
 763                        if (!cp->isdn.bc[u].rxbuf) {
 764                                err("can't allocate buffer for B channel RX data");
 765                                return -1;
 766                        }
 767                }
 768        }
 769
 770        /* Read out B-Channel output fifo size */
 771        ucp = kmalloc(32, GFP_KERNEL);
 772        if (!ucp) {
 773                err("Out of memory");
 774                return -3;
 775        }
 776        ret = usb_control_msg(cp->usbdev,                       /* pointer to device */
 777                              usb_rcvctrlpipe(cp->usbdev, 0),   /* pipe to control endpoint */
 778                              AUV_GETINFO,                      /* USB message request value */
 779                              AUT_RREQ,                         /* USB message request type value */
 780                              0,                                /* USB message value */
 781                              AUDI_OUTFSIZE,                    /* USB message index value */
 782                              ucp,                              /* pointer to the receive buffer */
 783                              32,                               /* length of the buffer */
 784                              HZ * 2);                          /* time to wait for the message to complete before timing out */
 785        if (ret < 4) {
 786                kfree(ucp);
 787                err("can't read TX Fifo sizes for B1,B2");
 788                return -4;
 789        }
 790        for (u = 0; u < AUISDN_BCHANNELS; u++) {
 791                ret = le16_to_cpup(ucp + u * 2);
 792                cp->isdn.bc[u].ofsize = ret;
 793                cp->isdn.bc[u].txfree = ret;
 794        }
 795        kfree(ucp);
 796        for (u = 0; u < AUISDN_BCHANNELS; u++) {
 797                dbg("B%d buffer size is %d", u, cp->isdn.bc[u].ofsize);
 798        }
 799
 800        /* get the B channel output INT size */
 801        cp->isdn.intbo_endp = AU_IRQENDPBO;
 802        ep = usb_epnum_to_ep_desc(cp->usbdev, USB_DIR_OUT | AU_IRQENDPBO);
 803        if (!ep) {
 804                /* Some devices have another endpoint number here ... */
 805                cp->isdn.intbo_endp = AU_IRQENDPBO_2;
 806                ep = usb_epnum_to_ep_desc(cp->usbdev,
 807                                          USB_DIR_OUT | AU_IRQENDPBO_2);
 808                if (!ep) {
 809                        err("can't get B channel OUT endpoint");
 810                        return -5;
 811                }
 812        }
 813        cp->isdn.outsize = ep->wMaxPacketSize;
 814        cp->isdn.outInterval = ep->bInterval;
 815        cp->isdn.usbdev = cp->usbdev;
 816
 817        /* allocate the urb and data buffer */
 818        if (!cp->isdn.intbo_urbp) {
 819                cp->isdn.intbo_urbp = usb_alloc_urb(0);
 820                if (!cp->isdn.intbo_urbp) {
 821                        err("can't allocate urb for B channel output endpoint");
 822                        return -6;
 823                }
 824        }
 825        if (!cp->isdn.intbo_bufp) {
 826                cp->isdn.intbo_bufp =
 827                    (char *) kmalloc(cp->isdn.outsize, GFP_KERNEL);
 828                if (!cp->isdn.intbo_bufp) {
 829                        err("can't allocate buffer for B channel output endpoint");
 830                        return -7;
 831                }
 832        }
 833
 834        /* get the B channel input INT size */
 835        ep = usb_epnum_to_ep_desc(cp->usbdev, USB_DIR_IN | AU_IRQENDPBI);
 836        if (!ep) {
 837                err("can't get B channel IN endpoint");
 838                return -8;
 839        }
 840        cp->isdn.insize = ep->wMaxPacketSize;
 841
 842        /* allocate the urb and data buffer */
 843        if (!cp->isdn.intbi_urbp) {
 844                cp->isdn.intbi_urbp = usb_alloc_urb(0);
 845                if (!cp->isdn.intbi_urbp) {
 846                        err("can't allocate urb for B channel input endpoint");
 847                        return -9;
 848                }
 849        }
 850        if (!cp->isdn.intbi_bufp) {
 851                cp->isdn.intbi_bufp =
 852                    (char *) kmalloc(cp->isdn.insize, GFP_KERNEL);
 853                if (!cp->isdn.intbi_bufp) {
 854                        err("can't allocate buffer for B channel input endpoint");
 855                        return -10;
 856                }
 857        }
 858
 859        /* setup urb */
 860        FILL_INT_URB(cp->isdn.intbi_urbp, cp->usbdev,
 861                     usb_rcvintpipe(cp->usbdev, AU_IRQENDPBI),
 862                     cp->isdn.intbi_bufp, cp->isdn.insize,
 863                     auerisdn_intbi_complete, cp, ep->bInterval);
 864        /* start the urb */
 865        cp->isdn.intbi_urbp->status = 0;        /* needed! */
 866        ret = usb_submit_urb(cp->isdn.intbi_urbp);
 867        if (ret < 0) {
 868                err("activation of B channel input int failed %d", ret);
 869                usb_free_urb(cp->isdn.intbi_urbp);
 870                cp->isdn.intbi_urbp = NULL;
 871                return -11;
 872        }
 873
 874        /* request the D-channel service now */
 875        dbg("Requesting D channel now");
 876        cp->isdn.dchannelservice.id = AUH_DCHANNEL;
 877        if (auerswald_addservice(cp, &cp->isdn.dchannelservice)) {
 878                err("can not open D-channel");
 879                cp->isdn.dchannelservice.id = AUH_UNASSIGNED;
 880                return -2;
 881        }
 882
 883        /* Find a free hisax interface */
 884        for (u = 0; u < AUER_MAX_DEVICES; u++) {
 885                ahp = &auerhisax_table[u];
 886                if (!ahp->cp) {
 887                        first_time = (u == 0);
 888                        goto ahp_found;
 889                }
 890        }
 891        /* no free interface found */
 892        return -12;
 893
 894        /* we found a free hisax interface */
 895      ahp_found:
 896        /* Wait until ipppd timeout expired. The reason behind this ugly construct:
 897           If we connect to a hisax device without waiting for ipppd we are not able
 898           to make a new IP connection. */
 899        if (ahp->last_close) {
 900                unsigned long timeout = jiffies - ahp->last_close;
 901                if (timeout < AUISDN_IPTIMEOUT) {
 902                        info("waiting for ipppd to timeout");
 903                        sleep_on_timeout(&wqh, AUISDN_IPTIMEOUT - timeout);
 904                }
 905        }
 906
 907        cp->isdn.ahp = ahp;
 908        u = ahp->hisax_registered;
 909        ahp->hisax_registered = 1;
 910        ahp->cp = cp;
 911
 912        /* now do the registration */
 913        if (!u) {
 914                for (u = 0; u < AUISDN_BCHANNELS; u++) {
 915                        b_if[u] = &ahp->hisax_b_if[u];
 916                }
 917                if (hisax_register
 918                    (&ahp->hisax_d_if, b_if, "auerswald_usb",
 919                     ISDN_PTYPE_EURO)) {
 920                        err("hisax registration failed");
 921                        ahp->cp = NULL;
 922                        cp->isdn.ahp = NULL;
 923                        ahp->hisax_registered = 0;
 924                        return -13;
 925                }
 926                dbg("hisax interface registered");
 927        }
 928
 929        /* send a D channel L1 activation indication to hisax */
 930        auerisdn_d_l1l2(&cp->isdn, PH_ACTIVATE | INDICATION, NULL);
 931        cp->isdn.dc_activated = 1;
 932
 933        /* do another D channel activation for problematic devices */
 934        cp->isdn.dcopen_timer.expires = jiffies + HZ;
 935        dbg("add timer");
 936        add_timer(&cp->isdn.dcopen_timer);
 937
 938        return 0;
 939}
 940
 941/* The USB device was disconnected */
 942void auerisdn_disconnect(struct auerswald *cp)
 943{
 944        struct auerhisax *ahp;
 945        DECLARE_WAIT_QUEUE_HEAD(wqh);
 946        unsigned long flags;
 947        unsigned int u;
 948        int ret;
 949        unsigned int stop_bc;
 950
 951        dbg("auerisdn_disconnect called");
 952
 953        /* stop a running timer */
 954        del_timer_sync(&cp->isdn.dcopen_timer);
 955
 956        /* first, stop the B channels */
 957        stop_bc = auerisdn_b_disconnect(cp);
 958
 959        /* stop the D channels */
 960        auerisdn_d_l1l2(&cp->isdn, PH_DEACTIVATE | INDICATION, NULL);
 961        cp->isdn.dc_activated = 0;
 962        dbg("D-Channel disconnected");
 963
 964        /* Wait a moment */
 965        sleep_on_timeout(&wqh, HZ / 10);
 966
 967        /* Shut the connection to the hisax interface */
 968        ahp = cp->isdn.ahp;
 969        if (ahp) {
 970                dbg("closing connection to hisax interface");
 971                ahp->cp = NULL;
 972                cp->isdn.ahp = NULL;
 973                /* time of last closure */
 974                if (stop_bc)
 975                        /* if we kill a running connection ... */
 976                        ahp->last_close = jiffies;
 977                else
 978                        ahp->last_close = 0;
 979        }
 980
 981        /* Now free the memory */
 982        if (cp->isdn.intbi_urbp) {
 983                ret = usb_unlink_urb(cp->isdn.intbi_urbp);
 984                if (ret)
 985                        dbg("B in: nonzero int unlink result received: %d",
 986                            ret);
 987                usb_free_urb(cp->isdn.intbi_urbp);
 988                cp->isdn.intbi_urbp = NULL;
 989        }
 990        kfree(cp->isdn.intbi_bufp);
 991        cp->isdn.intbi_bufp = NULL;
 992        
 993        if (cp->isdn.intbo_urbp) {
 994                cp->isdn.intbo_urbp->transfer_flags &= ~USB_ASYNC_UNLINK;
 995                ret = usb_unlink_urb(cp->isdn.intbo_urbp);
 996                if (ret)
 997                        dbg("B out: nonzero int unlink result received: %d", ret);
 998                usb_free_urb(cp->isdn.intbo_urbp);
 999                cp->isdn.intbo_urbp = NULL;
1000        }
1001        kfree(cp->isdn.intbo_bufp);
1002        cp->isdn.intbo_bufp = NULL;
1003
1004        /* Remove the rx and tx buffers */
1005        for (u = 0; u < AUISDN_BCHANNELS; u++) {
1006                kfree(cp->isdn.bc[u].rxbuf);
1007                cp->isdn.bc[u].rxbuf = NULL;
1008                spin_lock_irqsave(&cp->isdn.bc[u].txskb_lock, flags);
1009                if (cp->isdn.bc[u].txskb) {
1010                        skb_pull(cp->isdn.bc[u].txskb,
1011                                 cp->isdn.bc[u].txskb->len);
1012                        dev_kfree_skb_any(cp->isdn.bc[u].txskb);
1013                        cp->isdn.bc[u].txskb = NULL;
1014                }
1015                spin_unlock_irqrestore(&cp->isdn.bc[u].txskb_lock, flags);
1016        }
1017
1018        /* Remove the D-channel connection */
1019        auerswald_removeservice(cp, &cp->isdn.dchannelservice);
1020}
1021
1022
1023/*-------------------------------------------------------------------*/
1024/* Environment for long-lasting hisax interface                      */
1025
1026/* Wrapper for hisax B0 channel L2L1 */
1027static void auerisdn_b0_l2l1_wrapper(struct hisax_if *ifc, int pr,
1028                                     void *arg)
1029{
1030        auerisdn_b_l2l1(ifc, pr, arg, 0);
1031}
1032
1033/* Wrapper for hisax B1 channel L2L1 */
1034static void auerisdn_b1_l2l1_wrapper(struct hisax_if *ifc, int pr,
1035                                     void *arg)
1036{
1037        auerisdn_b_l2l1(ifc, pr, arg, 1);
1038}
1039
1040/* Init the global variables */
1041void auerisdn_init(void)
1042{
1043        struct auerhisax *ahp;
1044        unsigned int u;
1045
1046        memset(&auerhisax_table, 0, sizeof(auerhisax_table));
1047        for (u = 0; u < AUER_MAX_DEVICES; u++) {
1048                ahp = &auerhisax_table[u];
1049                spin_lock_init(&ahp->seq_lock);
1050                ahp->hisax_d_if.ifc.priv = ahp;
1051                ahp->hisax_d_if.ifc.l2l1 = auerisdn_d_l2l1;
1052                ahp->hisax_b_if[0].ifc.priv = ahp;
1053                ahp->hisax_b_if[0].ifc.l2l1 = auerisdn_b0_l2l1_wrapper;
1054                ahp->hisax_b_if[1].ifc.priv = ahp;
1055                ahp->hisax_b_if[1].ifc.l2l1 = auerisdn_b1_l2l1_wrapper;
1056        }
1057}
1058
1059/* Deinit the global variables */
1060void auerisdn_cleanup(void)
1061{
1062        struct auerhisax *ahp;
1063        int i;
1064
1065        /* cleanup last allocated device first */
1066        for (i = AUER_MAX_DEVICES - 1; i >= 0; i--) {
1067                ahp = &auerhisax_table[i];
1068                if (ahp->cp) {
1069                        err("hisax device %d open at cleanup", i);
1070                }
1071                if (ahp->hisax_registered) {
1072                        hisax_unregister(&ahp->hisax_d_if);
1073                        dbg("hisax interface %d freed", i);
1074                }
1075        }
1076}
1077
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.