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