1/*****************************************************************************/ 2/* 3 * auerisdn_b.c -- Auerswald PBX/System Telephone ISDN B-channel 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> /* ISDN constants */ 24#include <linux/netdevice.h> /* skb functions */ 25 26#undef DEBUG /* include debug macros until it's done */ 27#include <linux/usb.h> /* standard usb header */ 28 29#include "auerisdn.h" 30#include "auermain.h" 31 32/*-------------------------------------------------------------------*/ 33/* ISDN B channel support defines */ 34#define AUISDN_BC_1MS 8 /* Bytes per channel and ms */ 35#define AUISDN_BC_INC 4 /* change INT OUT size increment */ 36#define AUISDN_BCDATATHRESHOLD 48 /* for unsymmetric 2-B-channels */ 37#define AUISDN_TOGGLETIME 6 /* Timeout for unsymmetric serve */ 38 39/*-------------------------------------------------------------------*/ 40/* Debug support */ 41#ifdef DEBUG 42#define dump( desc, adr, len) \ 43do { \ 44 unsigned int u; \ 45 printk (KERN_DEBUG); \ 46 printk (desc); \ 47 for (u = 0; u < len; u++) \ 48 printk (" %02X", adr[u] & 0xFF); \ 49 printk ("\n"); \ 50} while (0) 51#else 52#define dump( desc, adr, len) 53#endif 54 55/*-------------------------------------------------------------------*/ 56 57/* Callback to L2 for HISAX */ 58/* This callback can be called from 3 sources: 59 a) from hisax context (answer from a l2l1 function) 60 b) from interrupt context (a B channel paket arrived, a B channel paket was sent) 61 c) from kernel daemon context (probe/disconnecting) 62*/ 63void auerisdn_b_l1l2(struct auerisdnbc *bc, int pr, void *arg) 64{ 65 struct auerhisax *ahp; 66 struct sk_buff *skb; 67 68 /* do the callback */ 69 ahp = bc->cp->isdn.ahp; 70 if (ahp) { 71 ahp->hisax_b_if[bc->channel].ifc.l1l2(&ahp-> 72 hisax_b_if[bc-> 73 channel]. 74 ifc, pr, arg); 75 } else { 76 dbg("auerisdn_b_l1l2 called without ahp"); 77 if (pr == (PH_DATA | INDICATION)) { 78 skb = (struct sk_buff *) arg; 79 if (skb) { 80 skb_pull(skb, skb->len); 81 dev_kfree_skb_any(skb); 82 } 83 } 84 } 85} 86 87/* fill the INT OUT data buffer with new data */ 88/* Transfer buffer size to fill is in urbp->transfer_buffer_length */ 89static void auerisdn_bintbo_newdata(struct auerisdn *ip) 90{ 91 unsigned long flags; 92 struct urb *urbp = ip->intbo_urbp; 93 struct auerisdnbc *bc = &ip->bc[0]; /* start with B-channel 0 */ 94 struct sk_buff *skb; 95 unsigned char *ucp; 96 int buf_size; 97 int len; 98 int bytes_sent; 99 int i; 100 101 /* FIXME: this algorithm is fixed to 2 B-channels */ 102 /* Which B channel should we serve? */ 103 if (ip->bc[1].mode != L1_MODE_NULL) { 104 /* B channel 1 is used */ 105 if (bc->mode != L1_MODE_NULL) { 106 /* both B-channels are used */ 107 if (ip->intbo_toggletimer) { 108 /* simply toggling */ 109 ip->intbo_toggletimer--; 110 i = ip->intbo_index ^ 1; /* serve both channels equal */ 111 } else { 112 /* search the B channel with the most demand of data */ 113 i = bc->txfree - ip->bc[1].txfree; 114 if (i < -AUISDN_BCDATATHRESHOLD) 115 i = 1; /* B channel 1 needs more data */ 116 else if (i > AUISDN_BCDATATHRESHOLD) 117 i = 0; /* B channel 0 needs more data */ 118 else 119 i = ip->intbo_index ^ 1; /* serve both channels equal */ 120 if (i == ip->intbo_index) 121 ip->intbo_toggletimer = 122 AUISDN_TOGGLETIME; 123 } 124 bc = &ip->bc[i]; 125 ip->intbo_index = i; 126 } else { 127 bc = &ip->bc[1]; 128 } 129 } 130 dbg("INTBO: Fill B%d with %d Bytes, %d Bytes free", 131 bc->channel + 1, urbp->transfer_buffer_length - AUH_SIZE, 132 bc->txfree); 133 134 /* Fill the buffer with data */ 135 ucp = ip->intbo_bufp; 136 *ucp++ = AUH_B1CHANNEL + bc->channel; /* First byte is channel nr. */ 137 buf_size = urbp->transfer_buffer_length - AUH_SIZE; 138 len = 0; 139 while (len < buf_size) { 140 spin_lock_irqsave(&bc->txskb_lock, flags); 141 if ((skb = bc->txskb)) { 142 /* dump ("raw tx data:", skb->data, skb->len); */ 143 if (bc->mode == L1_MODE_TRANS) { 144 bytes_sent = buf_size - len; 145 if (skb->len < bytes_sent) 146 bytes_sent = skb->len; 147 { /* swap tx bytes */ 148 register unsigned char *src = 149 skb->data; 150 unsigned int count; 151 for (count = 0; count < bytes_sent; 152 count++) 153 *ucp++ = 154 isdnhdlc_bit_rev_tab 155 [*src++]; 156 } 157 len += bytes_sent; 158 bc->lastbyte = skb->data[bytes_sent - 1]; 159 } else { 160 int bs = 161 isdnhdlc_encode(&bc->outp_hdlc_state, 162 skb->data, skb->len, 163 &bytes_sent, 164 ucp, buf_size - len); 165 /* dump ("hdlc data:", ucp, bs); */ 166 len += bs; 167 ucp += bs; 168 } 169 skb_pull(skb, bytes_sent); 170 171 if (!skb->len) { 172 // Frame sent 173 bc->txskb = NULL; 174 spin_unlock_irqrestore(&bc->txskb_lock, 175 flags); 176 auerisdn_b_l1l2(bc, PH_DATA | CONFIRM, 177 (void *) skb->truesize); 178 dev_kfree_skb_any(skb); 179 continue; //while 180 } 181 } else { 182 if (bc->mode == L1_MODE_TRANS) { 183 memset(ucp, bc->lastbyte, buf_size - len); 184 ucp += buf_size - len; 185 len = buf_size; 186 /* dbg ("fill = 0xFF"); */ 187 } else { 188 // Send flags 189 int bs = 190 isdnhdlc_encode(&bc->outp_hdlc_state, 191 NULL, 0, &bytes_sent, 192 ucp, buf_size - len); 193 /* dbg ("fill = 0x%02X", (int)*ucp); */ 194 len += bs; 195 ucp += bs; 196 } 197 } 198 spin_unlock_irqrestore(&bc->txskb_lock, flags); 199 } 200 /* dbg ("%d Bytes to TX buffer", len); */ 201} 202 203 204/* INT OUT completion handler */ 205static void auerisdn_bintbo_complete(struct urb *urbp) 206{ 207 struct auerisdn *ip = urbp->context; 208 209 /* unlink completion? */ 210 if ((urbp->status == -ENOENT) || (urbp->status == -ECONNRESET)) { 211 /* should we restart with another size? */ 212 if (ip->intbo_state == INTBOS_CHANGE) { 213 dbg("state => RESTART"); 214 ip->intbo_state = INTBOS_RESTART; 215 } else { 216 /* set up variables for later restart */ 217 dbg("INTBO stopped"); 218 ip->intbo_state = INTBOS_IDLE; 219 } 220 /* nothing more to do */ 221 return; 222 } 223 224 /* other state != 0? */ 225 if (urbp->status) { 226 warn("auerisdn_bintbo_complete: status = %d", 227 urbp->status); 228 return; 229 } 230 231 /* Should we fill in new data? */ 232 if (ip->intbo_state == INTBOS_CHANGE) { 233 dbg("state == INTBOS_CHANGE, no new data"); 234 return; 235 } 236 237 /* fill in new data */ 238 auerisdn_bintbo_newdata(ip); 239} 240 241/* set up the INT OUT URB the first time */ 242/* Don't start the URB */ 243static void auerisdn_bintbo_setup(struct auerisdn *ip, unsigned int len) 244{ 245 ip->intbo_state = INTBOS_IDLE; 246 FILL_INT_URB(ip->intbo_urbp, ip->usbdev, 247 usb_sndintpipe(ip->usbdev, ip->intbo_endp), 248 ip->intbo_bufp, len, auerisdn_bintbo_complete, ip, 249 ip->outInterval); 250 ip->intbo_urbp->transfer_flags |= USB_ASYNC_UNLINK; 251 ip->intbo_urbp->status = 0; 252} 253 254/* restart the INT OUT endpoint */ 255static void auerisdn_bintbo_restart(struct auerisdn *ip) 256{ 257 struct urb *urbp = ip->intbo_urbp; 258 int status; 259 260 /* dbg ("auerisdn_intbo_restart"); */ 261 262 /* fresh restart */ 263 auerisdn_bintbo_setup(ip, ip->paketsize + AUH_SIZE); 264 265 /* Fill in new data */ 266 auerisdn_bintbo_newdata(ip); 267 268 /* restart the urb */ 269 ip->intbo_state = INTBOS_RUNNING; 270 status = usb_submit_urb(urbp); 271 if (status < 0) { 272 err("can't submit INT OUT urb, status = %d", status); 273 urbp->status = status; 274 urbp->complete(urbp); 275 } 276} 277 278/* change the size of the INT OUT endpoint */ 279static void auerisdn_bchange(struct auerisdn *ip, unsigned int paketsize) 280{ 281 /* changing... */ 282 dbg("txfree[0] = %d, txfree[1] = %d, old size = %d, new size = %d", 283 ip->bc[0].txfree, ip->bc[1].txfree, ip->paketsize, paketsize); 284 ip->paketsize = paketsize; 285 286 if (paketsize == 0) { 287 /* stop the INT OUT endpoint */ 288 dbg("stop unlinking INT out urb"); 289 ip->intbo_state = INTBOS_IDLE; 290 usb_unlink_urb(ip->intbo_urbp); 291 return; 292 } 293 if (ip->intbo_state != INTBOS_IDLE) { 294 /* dbg ("unlinking INT out urb"); */ 295 ip->intbo_state = INTBOS_CHANGE; 296 usb_unlink_urb(ip->intbo_urbp); 297 } else { 298 /* dbg ("restart immediately"); */ 299 auerisdn_bintbo_restart(ip); 300 } 301} 302 303/* serve the outgoing B channel interrupt */ 304/* Called from the INT IN completion handler */ 305static void auerisdn_bserv(struct auerisdn *ip) 306{ 307 struct auerisdnbc *bc; 308 unsigned int u; 309 unsigned int paketsize; 310 311 /* should we start the INT OUT endpoint again? */ 312 if (ip->intbo_state == INTBOS_RESTART) { 313 /* dbg ("Restart INT OUT from INT IN"); */ 314 auerisdn_bintbo_restart(ip); 315 return; 316 } 317 /* no new calculation if change already in progress */ 318 if (ip->intbo_state == INTBOS_CHANGE) 319 return; 320 321 /* calculation of transfer parameters for INT OUT endpoint */ 322 paketsize = 0; 323 for (u = 0; u < AUISDN_BCHANNELS; u++) { 324 bc = &ip->bc[u]; 325 if (bc->mode != L1_MODE_NULL) { /* B channel is active */ 326 unsigned int bpp = AUISDN_BC_1MS * ip->outInterval; 327 if (bc->txfree < bpp) { /* buffer is full, throttle */ 328 bc->txsize = bpp - AUISDN_BC_INC; 329 paketsize += bpp - AUISDN_BC_INC; 330 } else if (bc->txfree < bpp * 2) { 331 paketsize += bc->txsize; /* schmidt-trigger, continue */ 332 } else if (bc->txfree < bpp * 4) { /* we are in synch */ 333 bc->txsize = bpp; 334 paketsize += bpp; 335 } else if (bc->txfree > bc->ofsize / 2) {/* we have to fill the buffer */ 336 bc->txsize = bpp + AUISDN_BC_INC; 337 paketsize += bpp + AUISDN_BC_INC; 338 } else { 339 paketsize += bc->txsize; /* schmidt-trigger, continue */ 340 } 341 } 342 } 343 344 /* check if we have to change the paket size */ 345 if (paketsize != ip->paketsize) 346 auerisdn_bchange(ip, paketsize); 347} 348 349/* Send activation/deactivation state to L2 */ 350static void auerisdn_bconf(struct auerisdnbc *bc) 351{ 352 unsigned long flags; 353 struct sk_buff *skb; 354 355 if (bc->mode == L1_MODE_NULL) { 356 auerisdn_b_l1l2(bc, PH_DEACTIVATE | INDICATION, NULL); 357 /* recycle old txskb */ 358 spin_lock_irqsave(&bc->txskb_lock, flags); 359 skb = bc->txskb; 360 bc->txskb = NULL; 361 spin_unlock_irqrestore(&bc->txskb_lock, flags); 362 if (skb) { 363 skb_pull(skb, skb->len); 364 auerisdn_b_l1l2(bc, PH_DATA | CONFIRM, 365 (void *) skb->truesize); 366 dev_kfree_skb_any(skb); 367 } 368 } else { 369 auerisdn_b_l1l2(bc, PH_ACTIVATE | INDICATION, NULL); 370 } 371} 372 373/* B channel setup completion handler */ 374static void auerisdn_bmode_complete(struct urb *urb) 375{ 376 struct auerswald *cp; 377 struct auerbuf *bp = (struct auerbuf *) urb->context; 378 struct auerisdnbc *bc; 379 int channel; 380 381 dbg("auerisdn_bmode_complete called"); 382 cp = ((struct auerswald *) ((char *) (bp->list) - 383 (unsigned 384 long) (&((struct auerswald *) 0)-> 385 bufctl))); 386 387 /* select the B-channel */ 388 channel = le16_to_cpu(bp->dr->wIndex); 389 channel -= AUH_B1CHANNEL; 390 if (channel < 0) 391 goto rel; 392 if (channel >= AUISDN_BCHANNELS) 393 goto rel; 394 bc = &cp->isdn.bc[channel]; 395 396 /* Check for success */ 397 if (urb->status) { 398 err("complete with non-zero status: %d", urb->status); 399 } else { 400 bc->mode = *bp->bufp; 401 } 402 /* Signal current mode to L2 */ 403 auerisdn_bconf(bc); 404 405 /* reuse the buffer */ 406 rel:auerbuf_releasebuf(bp); 407 408 /* Wake up all processes waiting for a buffer */ 409 wake_up(&cp->bufferwait); 410} 411 412/* Setup a B channel transfer mode */ 413static void auerisdn_bmode(struct auerisdnbc *bc, unsigned int mode) 414{ 415 struct auerswald *cp = bc->cp; 416 struct auerbuf *bp; 417 int ret; 418 419 /* don't allow activation on disconnect */ 420 if (cp->disconnecting) { 421 mode = L1_MODE_NULL; 422 423 /* Else check if something changed */ 424 } else if (bc->mode != mode) { 425 if ((mode != L1_MODE_NULL) && (mode != L1_MODE_TRANS)) { 426 /* init RX hdlc decoder */ 427 dbg("rcv init"); 428 isdnhdlc_rcv_init(&bc->inp_hdlc_state, 0); 429 /* init TX hdlc decoder */ 430 dbg("out init"); 431 isdnhdlc_out_init(&bc->outp_hdlc_state, 0, 0); 432 } 433 /* stop ASAP */ 434 if (mode == L1_MODE_NULL) 435 bc->mode = mode; 436 if ((bc->mode == L1_MODE_NULL) || (mode == L1_MODE_NULL)) { 437 /* Activation or deactivation required */ 438 439 /* get a buffer for the command */ 440 bp = auerbuf_getbuf(&cp->bufctl); 441 /* if no buffer available: can't change the mode */ 442 if (!bp) { 443 err("auerisdn_bmode: no data buffer available"); 444 return; 445 } 446 447 /* fill the control message */ 448 bp->dr->bRequestType = AUT_WREQ; 449 bp->dr->bRequest = AUV_CHANNELCTL; 450 if (mode != L1_MODE_NULL) 451 bp->dr->wValue = cpu_to_le16(1); 452 else 453 bp->dr->wValue = cpu_to_le16(0); 454 bp->dr->wIndex = 455 cpu_to_le16(AUH_B1CHANNEL + bc->channel); 456 bp->dr->wLength = cpu_to_le16(0); 457 *bp->bufp = mode; 458 FILL_CONTROL_URB(bp->urbp, cp->usbdev, 459 usb_sndctrlpipe(cp->usbdev, 0), 460 (unsigned char *) bp->dr, 461 bp->bufp, 0, 462 (usb_complete_t) 463 auerisdn_bmode_complete, bp); 464 465 /* submit the control msg */ 466 ret = 467 auerchain_submit_urb(&cp->controlchain, 468 bp->urbp); 469 if (ret) { 470 bp->urbp->status = ret; 471 auerisdn_bmode_complete(bp->urbp); 472 } 473 return; 474 } 475 } 476 /* new mode is set */ 477 bc->mode = mode; 478 479 /* send confirmation to L2 */ 480 auerisdn_bconf(bc); 481} 482 483/* B-channel transfer function L2->L1 */ 484void auerisdn_b_l2l1(struct hisax_if *ifc, int pr, void *arg, 485 unsigned int channel) 486{ 487 struct auerhisax *ahp; 488 struct auerisdnbc *bc; 489 struct auerswald *cp; 490 struct sk_buff *skb; 491 unsigned long flags; 492 int mode; 493 494 cp = NULL; 495 ahp = (struct auerhisax *) ifc->priv; 496 if (ahp) 497 cp = ahp->cp; 498 if (cp && !cp->disconnecting) { 499 /* normal execution */ 500 bc = &cp->isdn.bc[channel]; 501 switch (pr) { 502 case PH_ACTIVATE | REQUEST: /* activation request */ 503 mode = (int) arg; /* one of the L1_MODE constants */ 504 dbg("B%d, PH_ACTIVATE_REQUEST Mode = %d", 505 bc->channel + 1, mode); 506 auerisdn_bmode(bc, mode); 507 break; 508 case PH_DEACTIVATE | REQUEST: /* deactivation request */ 509 dbg("B%d, PH_DEACTIVATE_REQUEST", bc->channel + 1); 510 auerisdn_bmode(bc, L1_MODE_NULL); 511 break; 512 case PH_DATA | REQUEST: /* Transmit data request */ 513 skb = (struct sk_buff *) arg; 514 spin_lock_irqsave(&bc->txskb_lock, flags); 515 if (bc->txskb) { 516 err("Overflow in B channel TX"); 517 skb_pull(skb, skb->len); 518 dev_kfree_skb_any(skb); 519 } else { 520 if (cp->disconnecting 521 || (bc->mode == L1_MODE_NULL)) { 522 skb_pull(skb, skb->len); 523 spin_unlock_irqrestore(&bc-> 524 txskb_lock, 525 flags); 526 auerisdn_b_l1l2(bc, 527 PH_DATA | CONFIRM, 528 (void *) skb-> 529 truesize); 530 dev_kfree_skb_any(skb); 531 goto next; 532 } else 533 bc->txskb = skb; 534 } 535 spin_unlock_irqrestore(&bc->txskb_lock, flags); 536 next:break; 537 default: 538 warn("pr %#x\n", pr); 539 break; 540 } 541 } else { 542 /* hisax interface is down */ 543 switch (pr) { 544 case PH_ACTIVATE | REQUEST: /* activation request */ 545 dbg("B channel: PH_ACTIVATE | REQUEST with interface down"); 546 /* don't answer this request! Endless... */ 547 break; 548 case PH_DEACTIVATE | REQUEST: /* deactivation request */ 549 dbg("B channel: PH_DEACTIVATE | REQUEST with interface down"); 550 ifc->l1l2(ifc, PH_DEACTIVATE | INDICATION, NULL); 551 break; 552 case PH_DATA | REQUEST: /* Transmit data request */ 553 dbg("B channel: PH_DATA | REQUEST with interface down"); 554 skb = (struct sk_buff *) arg; 555 /* free data buffer */ 556 if (skb) { 557 skb_pull(skb, skb->len); 558 dev_kfree_skb_any(skb); 559 } 560 /* send confirmation back to layer 2 */ 561 ifc->l1l2(ifc, PH_DATA | CONFIRM, NULL); 562 break; 563 default: 564 warn("pr %#x\n", pr); 565 break; 566 } 567 } 568} 569 570/* Completion handler for B channel input endpoint */ 571void auerisdn_intbi_complete(struct urb *urb) 572{ 573 unsigned int bytecount; 574 unsigned char *ucp; 575 int channel; 576 unsigned int syncbit; 577 unsigned int syncdata; 578 struct auerisdnbc *bc; 579 struct sk_buff *skb; 580 int count; 581 int status; 582 struct auerswald *cp = (struct auerswald *) urb->context; 583 /* do not respond to an error condition */ 584 if (urb->status != 0) { 585 dbg("nonzero URB status = %d", urb->status); 586 return; 587 } 588 if (cp->disconnecting) 589 return; 590 591 /* Parse and extract the header information */ 592 bytecount = urb->actual_length; 593 ucp = cp->isdn.intbi_bufp; 594 if (!bytecount) 595 return; /* no data */ 596 channel = *ucp & AUH_TYPEMASK; 597 syncbit = *ucp & AUH_SYNC; 598 ucp++; 599 bytecount--; 600 channel -= AUH_B1CHANNEL; 601 if (channel < 0) 602 return; /* unknown data channel, no B1,B2 */ 603 if (channel >= AUISDN_BCHANNELS) 604 return; /* unknown data channel, no B1,B2 */ 605 bc = &cp->isdn.bc[channel]; 606 if (!bytecount) 607 return; 608 /* Calculate amount of bytes which are free in tx device buffer */ 609 bc->txfree = ((255 - *ucp++) * bc->ofsize) / 256; 610 /* dbg ("%d Bytes free in TX buffer", bc->txfree); */ 611 bytecount--; 612 613 /* Next Byte: TX sync information */ 614 if (syncbit) { 615 if (!bytecount) 616 goto int_tx; 617 syncdata = *ucp++; 618 dbg("Sync data = %d", syncdata); 619 bytecount--; 620 } 621 /* The rest of the paket is plain data */ 622 if (!bytecount) 623 goto int_tx; 624 /* dump ("RX Data is:", ucp, bytecount); */ 625 626 /* Send B channel data to upper layers */ 627 while (bytecount > 0) { 628 if (bc->mode == L1_MODE_NULL) { 629 /* skip the data. Nobody needs them */ 630 status = 0; 631 bytecount = 0; 632 } else if (bc->mode == L1_MODE_TRANS) { 633 { /* swap rx bytes */ 634 register unsigned char *dest = bc->rxbuf; 635 status = bytecount; 636 for (; bytecount; bytecount--) 637 *dest++ = 638 isdnhdlc_bit_rev_tab[*ucp++]; 639 } 640 641 } else { 642 status = isdnhdlc_decode(&bc->inp_hdlc_state, ucp, 643 bytecount, &count, 644 bc->rxbuf, AUISDN_RXSIZE); 645 ucp += count; 646 bytecount -= count; 647 } 648 if (status > 0) { 649 /* Good frame received */ 650 if (!(skb = dev_alloc_skb(status))) { 651 warn("receive out of memory"); 652 break; 653 } 654 memcpy(skb_put(skb, status), bc->rxbuf, status); 655 /* dump ("HDLC Paket", bc->rxbuf, status); */ 656 auerisdn_b_l1l2(bc, PH_DATA | INDICATION, skb); 657 /* these errors may actually happen at the start of a connection! */ 658 } else if (status == -HDLC_CRC_ERROR) { 659 dbg("CRC error"); 660 } else if (status == -HDLC_FRAMING_ERROR) { 661 dbg("framing error"); 662 } else if (status == -HDLC_LENGTH_ERROR) { 663 dbg("length error"); 664 } 665 } 666 667 int_tx: /* serve the outgoing B channel */ 668 auerisdn_bserv(&cp->isdn); 669} 670 671/* Stop the B channel activity. The device is disconnecting */ 672/* This function is called after cp->disconnecting is true */ 673unsigned int auerisdn_b_disconnect(struct auerswald *cp) 674{ 675 unsigned int u; 676 struct auerisdnbc *bc; 677 unsigned int result = 0; 678 679 /* Close the B channels */ 680 for (u = 0; u < AUISDN_BCHANNELS; u++) { 681 bc = &cp->isdn.bc[u]; 682 if (bc->mode != L1_MODE_NULL) { /* B channel is active */ 683 auerisdn_bmode(bc, L1_MODE_NULL); 684 result = 1; 685 } 686 } 687 /* return 1 if there is B channel traffic */ 688 return result; 689} 690