OpenCores
URL https://opencores.org/ocsvn/or1k/or1k/trunk

Subversion Repositories or1k

[/] [or1k/] [trunk/] [linux/] [linux-2.4/] [drivers/] [usb/] [auerisdn.c] - Blame information for rev 1765

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 1275 phoenix
/*****************************************************************************/
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) \
41
do {                    \
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. */
59
static 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
*/
70
static 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 */
93
static 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 */
113
static 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 */
182
static 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 */
377
static void auerisdn_disconnect_dc(struct auerscon *scp)
378
{
379
}
380
 
381
 
382
/* confirmation helper function. */
383
static 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 */
398
static 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 */
667
static 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 */
685
static 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 */
726
void 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 */
745
int 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 */
942
void 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 */
1027
static 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 */
1034
static 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 */
1041
void 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 */
1060
void 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
}

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.