bb6db28004a68c730f620b72457c2528246a6276
[dragonfly.git] / sys / net / i4b / driver / i4b_rbch.c
1 /*
2  * Copyright (c) 1997, 2001 Hellmuth Michaelis. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23  * SUCH DAMAGE.
24  *
25  *---------------------------------------------------------------------------
26  *
27  *      i4b_rbch.c - device driver for raw B channel data
28  *      ---------------------------------------------------
29  *
30  * $FreeBSD: src/sys/i4b/driver/i4b_rbch.c,v 1.10.2.3 2001/08/12 16:22:48 hm Exp $
31  * $DragonFly: src/sys/net/i4b/driver/i4b_rbch.c,v 1.22 2006/12/22 23:44:55 swildner Exp $
32  *
33  *      last edit-date: [Sat Aug 11 18:06:57 2001]
34  *
35  *---------------------------------------------------------------------------*/
36
37 #include "use_i4brbch.h"
38
39 #if NI4BRBCH > 0
40
41 #include <sys/param.h>
42 #include <sys/systm.h>
43
44 #include <sys/conf.h>
45 #include <sys/uio.h>
46 #include <sys/kernel.h>
47 #include <sys/mbuf.h>
48 #include <sys/socket.h>
49 #include <net/if.h>
50 #include <sys/tty.h>
51 #include <sys/thread2.h>
52 #include <sys/vnode.h>
53
54 #include <net/i4b/include/machine/i4b_ioctl.h>
55 #include <net/i4b/include/machine/i4b_rbch_ioctl.h>
56 #include <net/i4b/include/machine/i4b_debug.h>
57
58 #include "../include/i4b_global.h"
59 #include "../include/i4b_mbuf.h"
60 #include "../include/i4b_l3l4.h"
61 #include "../layer4/i4b_l4.h"
62
63 #include <sys/event.h>
64 #include <sys/filio.h>
65
66 static drvr_link_t rbch_drvr_linktab[NI4BRBCH];
67 static isdn_link_t *isdn_linktab[NI4BRBCH];
68
69 #define I4BRBCHACCT             1       /* enable accounting messages */
70 #define I4BRBCHACCTINTVL        2       /* accounting msg interval in secs */
71
72 static struct rbch_softc {
73
74         int sc_unit;                    /* unit number          */
75
76         int sc_devstate;                /* state of driver      */
77 #define ST_IDLE         0x00
78 #define ST_CONNECTED    0x01
79 #define ST_ISOPEN       0x02
80 #define ST_RDWAITDATA   0x04
81 #define ST_WRWAITEMPTY  0x08
82 #define ST_NOBLOCK      0x10
83
84         int sc_bprot;                   /* B-ch protocol used   */
85
86         call_desc_t *sc_cd;             /* Call Descriptor */
87
88         struct termios it_in;
89
90         struct ifqueue sc_hdlcq;        /* hdlc read queue      */
91 #define I4BRBCHMAXQLEN  10
92
93         struct selinfo selp;            /* select / poll        */
94
95 #if I4BRBCHACCT
96         struct callout  sc_timeout;
97
98         int             sc_iinb;        /* isdn driver # of inbytes     */
99         int             sc_ioutb;       /* isdn driver # of outbytes    */
100         int             sc_linb;        /* last # of bytes rx'd         */
101         int             sc_loutb;       /* last # of bytes tx'd         */
102         int             sc_fn;          /* flag, first null acct        */
103 #endif  
104 } rbch_softc[NI4BRBCH];
105
106 static void rbch_rx_data_rdy(int unit);
107 static void rbch_tx_queue_empty(int unit);
108 static void rbch_connect(int unit, void *cdp);
109 static void rbch_disconnect(int unit, void *cdp);
110 static void rbch_init_linktab(int unit);
111 static void rbch_clrq(int unit);
112
113 #define PDEVSTATIC      static
114 #define IOCTL_CMD_T     u_long
115
116 PDEVSTATIC d_open_t i4brbchopen;
117 PDEVSTATIC d_close_t i4brbchclose;
118 PDEVSTATIC d_read_t i4brbchread;
119 PDEVSTATIC d_write_t i4brbchwrite;
120 PDEVSTATIC d_ioctl_t i4brbchioctl;
121 PDEVSTATIC d_kqfilter_t i4brbchkqfilter;
122
123 PDEVSTATIC void i4brbchkfilt_detach(struct knote *);
124 PDEVSTATIC int i4brbchkfilt_read(struct knote *, long);
125 PDEVSTATIC int i4brbchkfilt_write(struct knote *, long);
126
127 #define CDEV_MAJOR 57
128
129 static struct dev_ops i4brbch_ops = {
130         { "i4brbch", CDEV_MAJOR, D_KQFILTER },
131         .d_open =       i4brbchopen,
132         .d_close =      i4brbchclose,
133         .d_read =       i4brbchread,
134         .d_write =      i4brbchwrite,
135         .d_ioctl =      i4brbchioctl,
136         .d_kqfilter =   i4brbchkqfilter
137 };
138
139 static void i4brbchattach(void *);
140 PSEUDO_SET(i4brbchattach, i4b_rbch);
141
142 /*===========================================================================*
143  *                      DEVICE DRIVER ROUTINES
144  *===========================================================================*/
145
146 /*---------------------------------------------------------------------------*
147  *      interface attach routine
148  *---------------------------------------------------------------------------*/
149 PDEVSTATIC void
150 i4brbchattach(void *dummy)
151 {
152         int i;
153
154 #ifndef HACK_NO_PSEUDO_ATTACH_MSG
155         kprintf("i4brbch: %d raw B channel access device(s) attached\n", NI4BRBCH);
156 #endif
157         
158         for(i=0; i < NI4BRBCH; i++)
159         {
160                 make_dev(&i4brbch_ops, i,
161                         UID_ROOT, GID_WHEEL, 0600, "i4brbch%d", i);
162
163 #if I4BRBCHACCT
164                 callout_init(&rbch_softc[i].sc_timeout);
165                 rbch_softc[i].sc_fn = 1;
166 #endif
167                 rbch_softc[i].sc_unit = i;
168                 rbch_softc[i].sc_devstate = ST_IDLE;
169                 rbch_softc[i].sc_hdlcq.ifq_maxlen = I4BRBCHMAXQLEN;
170                 rbch_softc[i].it_in.c_ispeed = rbch_softc[i].it_in.c_ospeed = 64000;
171                 termioschars(&rbch_softc[i].it_in);
172                 rbch_init_linktab(i);
173         }
174 }
175
176 /*---------------------------------------------------------------------------*
177  *      open rbch device
178  *---------------------------------------------------------------------------*/
179 PDEVSTATIC int
180 i4brbchopen(struct dev_open_args *ap)
181 {
182         cdev_t dev = ap->a_head.a_dev;
183         int unit = minor(dev);
184         
185         if(unit >= NI4BRBCH)
186                 return(ENXIO);
187
188         if(rbch_softc[unit].sc_devstate & ST_ISOPEN)
189                 return(EBUSY);
190
191 #if 0
192         rbch_clrq(unit);
193 #endif
194         
195         rbch_softc[unit].sc_devstate |= ST_ISOPEN;              
196
197         NDBGL4(L4_RBCHDBG, "unit %d, open", unit);      
198
199         return(0);
200 }
201
202 /*---------------------------------------------------------------------------*
203  *      close rbch device
204  *---------------------------------------------------------------------------*/
205 PDEVSTATIC int
206 i4brbchclose(struct dev_close_args *ap)
207 {
208         cdev_t dev = ap->a_head.a_dev;
209         int unit = minor(dev);
210         struct rbch_softc *sc = &rbch_softc[unit];
211         
212         if(sc->sc_devstate & ST_CONNECTED)
213                 i4b_l4_drvrdisc(BDRV_RBCH, unit);
214
215         sc->sc_devstate &= ~ST_ISOPEN;          
216
217         rbch_clrq(unit);
218         
219         NDBGL4(L4_RBCHDBG, "unit %d, closed", unit);
220         
221         return(0);
222 }
223
224 /*---------------------------------------------------------------------------*
225  *      read from rbch device
226  *---------------------------------------------------------------------------*/
227 PDEVSTATIC int
228 i4brbchread(struct dev_read_args *ap)
229 {
230         cdev_t dev = ap->a_head.a_dev;
231         struct uio *uio = ap->a_uio;
232         struct mbuf *m;
233         int error = 0;
234         int unit = minor(dev);
235         struct ifqueue *iqp;
236         struct rbch_softc *sc = &rbch_softc[unit];
237
238         CRIT_VAR;
239         
240         NDBGL4(L4_RBCHDBG, "unit %d, enter read", unit);
241         
242         CRIT_BEG;
243         if(!(sc->sc_devstate & ST_ISOPEN))
244         {
245                 CRIT_END;
246                 NDBGL4(L4_RBCHDBG, "unit %d, read while not open", unit);
247                 return(EIO);
248         }
249
250         if((sc->sc_devstate & ST_NOBLOCK) || (ap->a_ioflag & IO_NDELAY))
251         {
252                 if(!(sc->sc_devstate & ST_CONNECTED)) {
253                         CRIT_END;
254                         return(EWOULDBLOCK);
255                 }
256
257                 if(sc->sc_bprot == BPROT_RHDLC)
258                         iqp = &sc->sc_hdlcq;
259                 else
260                         iqp = isdn_linktab[unit]->rx_queue;     
261
262                 if(IF_QEMPTY(iqp) && (sc->sc_devstate & ST_ISOPEN)) {
263                         CRIT_END;
264                         return(EWOULDBLOCK);
265         }
266         }
267         else
268         {
269                 while(!(sc->sc_devstate & ST_CONNECTED))
270                 {
271                         NDBGL4(L4_RBCHDBG, "unit %d, wait read init", unit);
272                 
273                         if((error = tsleep((caddr_t) &rbch_softc[unit],
274                                                PCATCH, "rrrbch", 0 )) != 0)
275                         {
276                                 CRIT_END;
277                                 NDBGL4(L4_RBCHDBG, "unit %d, error %d tsleep", unit, error);
278                                 return(error);
279                         }
280                 }
281
282                 if(sc->sc_bprot == BPROT_RHDLC)
283                         iqp = &sc->sc_hdlcq;
284                 else
285                         iqp = isdn_linktab[unit]->rx_queue;     
286
287                 while(IF_QEMPTY(iqp) && (sc->sc_devstate & ST_ISOPEN))
288                 {
289                         sc->sc_devstate |= ST_RDWAITDATA;
290                 
291                         NDBGL4(L4_RBCHDBG, "unit %d, wait read data", unit);
292                 
293                         if((error = tsleep((caddr_t) &isdn_linktab[unit]->rx_queue,
294                                            PCATCH, "rrbch", 0 )) != 0)
295                         {
296                                 CRIT_END;
297                                 NDBGL4(L4_RBCHDBG, "unit %d, error %d tsleep read", unit, error);
298                                 sc->sc_devstate &= ~ST_RDWAITDATA;
299                                 return(error);
300                         }
301                 }
302         }
303
304         IF_DEQUEUE(iqp, m);
305
306         NDBGL4(L4_RBCHDBG, "unit %d, read %d bytes", unit, m->m_len);
307         
308         if(m && m->m_len)
309         {
310                 error = uiomove(m->m_data, m->m_len, uio);
311         }
312         else
313         {
314                 NDBGL4(L4_RBCHDBG, "unit %d, error %d uiomove", unit, error);
315                 error = EIO;
316         }
317                 
318         if(m)
319                 i4b_Bfreembuf(m);
320
321         CRIT_END;
322
323         return(error);
324 }
325
326 /*---------------------------------------------------------------------------*
327  *      write to rbch device
328  *---------------------------------------------------------------------------*/
329 PDEVSTATIC int
330 i4brbchwrite(struct dev_write_args *ap)
331 {
332         cdev_t dev = ap->a_head.a_dev;
333         struct uio *uio = ap->a_uio;
334         struct mbuf *m;
335         int error = 0;
336         int unit = minor(dev);
337         struct rbch_softc *sc = &rbch_softc[unit];
338
339         CRIT_VAR;
340         
341         NDBGL4(L4_RBCHDBG, "unit %d, write", unit);     
342
343         CRIT_BEG;
344         if(!(sc->sc_devstate & ST_ISOPEN))
345         {
346                 NDBGL4(L4_RBCHDBG, "unit %d, write while not open", unit);
347                 CRIT_END;
348                 return(EIO);
349         }
350
351         if((sc->sc_devstate & ST_NOBLOCK) || (ap->a_ioflag & IO_NDELAY))
352         {
353                 if(!(sc->sc_devstate & ST_CONNECTED)) {
354                         CRIT_END;
355                         return(EWOULDBLOCK);
356                 }
357                 if(IF_QFULL(isdn_linktab[unit]->tx_queue) && (sc->sc_devstate & ST_ISOPEN)) {
358                         CRIT_END;
359                         return(EWOULDBLOCK);
360         }
361         }
362         else
363         {
364                 while(!(sc->sc_devstate & ST_CONNECTED))
365                 {
366                         NDBGL4(L4_RBCHDBG, "unit %d, write wait init", unit);
367                 
368                         error = tsleep((caddr_t) &rbch_softc[unit],
369                                                    PCATCH, "wrrbch", 0 );
370                         if(error == ERESTART) {
371                                 CRIT_END;
372                                 return (ERESTART);
373                         }
374                         else if(error == EINTR)
375                         {
376                                 CRIT_END;
377                                 NDBGL4(L4_RBCHDBG, "unit %d, EINTR during wait init", unit);
378                                 return(EINTR);
379                         }
380                         else if(error)
381                         {
382                                 CRIT_END;
383                                 NDBGL4(L4_RBCHDBG, "unit %d, error %d tsleep init", unit, error);
384                                 return(error);
385                         }
386                         tsleep((caddr_t) &rbch_softc[unit], PCATCH, "xrbch", (hz*1));
387                 }
388
389                 while(IF_QFULL(isdn_linktab[unit]->tx_queue) && (sc->sc_devstate & ST_ISOPEN))
390                 {
391                         sc->sc_devstate |= ST_WRWAITEMPTY;
392
393                         NDBGL4(L4_RBCHDBG, "unit %d, write queue full", unit);
394                 
395                         if ((error = tsleep((caddr_t) &isdn_linktab[unit]->tx_queue,
396                                             PCATCH, "wrbch", 0)) != 0) {
397                                 sc->sc_devstate &= ~ST_WRWAITEMPTY;
398                                 if(error == ERESTART)
399                                 {
400                                         CRIT_END;
401                                         return(ERESTART);
402                                 }
403                                 else if(error == EINTR)
404                                 {
405                                         CRIT_END;
406                                         NDBGL4(L4_RBCHDBG, "unit %d, EINTR during wait write", unit);
407                                         return(error);
408                                 }
409                                 else if(error)
410                                 {
411                                         CRIT_END;
412                                         NDBGL4(L4_RBCHDBG, "unit %d, error %d tsleep write", unit, error);
413                                         return(error);
414                                 }
415                         }
416                 }
417         }
418
419         if(!(sc->sc_devstate & ST_ISOPEN))
420         {
421                 NDBGL4(L4_RBCHDBG, "unit %d, not open anymore", unit);
422                 CRIT_END;
423                 return(EIO);
424         }
425
426         if((m = i4b_Bgetmbuf(BCH_MAX_DATALEN)) != NULL)
427         {
428                 m->m_len = (int)szmin(BCH_MAX_DATALEN, uio->uio_resid);
429
430                 NDBGL4(L4_RBCHDBG, "unit %d, write %d bytes", unit, m->m_len);
431                 
432                 error = uiomove(m->m_data, (size_t)m->m_len, uio);
433
434                 if(IF_QFULL(isdn_linktab[unit]->tx_queue))
435                         m_freem(m);
436                 else
437                         IF_ENQUEUE(isdn_linktab[unit]->tx_queue, m);
438                 (*isdn_linktab[unit]->bch_tx_start)(isdn_linktab[unit]->unit, isdn_linktab[unit]->channel);
439         }
440
441         CRIT_END;
442         
443         return(error);
444 }
445
446 /*---------------------------------------------------------------------------*
447  *      rbch device ioctl handlibg
448  *---------------------------------------------------------------------------*/
449 PDEVSTATIC int
450 i4brbchioctl(struct dev_ioctl_args *ap)
451 {
452         cdev_t dev = ap->a_head.a_dev;
453         int error = 0;
454         int unit = minor(dev);
455         struct rbch_softc *sc = &rbch_softc[unit];
456         
457         switch(ap->a_cmd)
458         {
459                 case FIOASYNC:  /* Set async mode */
460                         if (*(int *)ap->a_data)
461                         {
462                                 NDBGL4(L4_RBCHDBG, "unit %d, setting async mode", unit);
463                         }
464                         else
465                         {
466                                 NDBGL4(L4_RBCHDBG, "unit %d, clearing async mode", unit);
467                         }
468                         break;
469
470                 case TIOCCDTR:  /* Clear DTR */
471                         if(sc->sc_devstate & ST_CONNECTED)
472                         {
473                                 NDBGL4(L4_RBCHDBG, "unit %d, disconnecting for DTR down", unit);
474                                 i4b_l4_drvrdisc(BDRV_RBCH, unit);
475                         }
476                         break;
477
478                 case I4B_RBCH_DIALOUT:
479                 {
480                         size_t l;
481
482                         for (l = 0; l < TELNO_MAX && ((char *)ap->a_data)[l]; l++)
483                                 ;
484                         if (l)
485                         {
486                                 NDBGL4(L4_RBCHDBG, "unit %d, attempting dialout to %s", unit, (char *)ap->a_data);
487                                 i4b_l4_dialoutnumber(BDRV_RBCH, unit, l, (char *)ap->a_data);
488                                 break;
489                         }
490                         /* fall through to SDTR */
491                 }
492
493                 case TIOCSDTR:  /* Set DTR */
494                         NDBGL4(L4_RBCHDBG, "unit %d, attempting dialout (DTR)", unit);
495                         i4b_l4_dialout(BDRV_RBCH, unit);
496                         break;
497
498                 case TIOCSETA:  /* Set termios struct */
499                         break;
500
501                 case TIOCGETA:  /* Get termios struct */
502                         *(struct termios *)ap->a_data = sc->it_in;
503                         break;
504
505                 case TIOCMGET:
506                         *(int *)ap->a_data = TIOCM_LE|TIOCM_DTR|TIOCM_RTS|TIOCM_CTS|TIOCM_DSR;
507                         if (sc->sc_devstate & ST_CONNECTED)
508                                 *(int *)ap->a_data |= TIOCM_CD;
509                         break;
510
511                 case I4B_RBCH_VR_REQ:
512                 {
513                         msg_vr_req_t *mvr;
514
515                         mvr = (msg_vr_req_t *)ap->a_data;
516
517                         mvr->version = VERSION;
518                         mvr->release = REL;
519                         mvr->step = STEP;                       
520                         break;
521                 }
522
523                 default:        /* Unknown stuff */
524                         NDBGL4(L4_RBCHDBG, "unit %d, ioctl, unknown cmd %lx", unit, ap->a_cmd);
525                         error = EINVAL;
526                         break;
527         }
528         return(error);
529 }
530
531 /*---------------------------------------------------------------------------*
532  *      device driver poll
533  *---------------------------------------------------------------------------*/
534 static struct filterops i4brbchkfiltops_read =
535         { 1, NULL, i4brbchkfilt_detach, i4brbchkfilt_read };
536 static struct filterops i4brbchkfiltops_write =
537         { 1, NULL, i4brbchkfilt_detach, i4brbchkfilt_write };
538
539 PDEVSTATIC int
540 i4brbchkqfilter(struct dev_kqfilter_args *ap)
541 {
542         cdev_t dev = ap->a_head.a_dev;
543         int unit = minor(dev);
544         struct rbch_softc *sc = &rbch_softc[unit];
545         struct knote *kn = ap->a_kn;
546         struct klist *klist;
547
548         ap->a_result = 0;
549
550         switch (kn->kn_filter) {
551         case EVFILT_READ:
552                 kn->kn_fop = &i4brbchkfiltops_read;
553                 kn->kn_hook = (caddr_t)dev;
554                 break;
555         case EVFILT_WRITE:
556                 kn->kn_fop = &i4brbchkfiltops_write;
557                 kn->kn_hook = (caddr_t)dev;
558                 break;
559         default:
560                 ap->a_result = EOPNOTSUPP;
561                 return (0);
562         }
563
564         crit_enter();
565         klist = &sc->selp.si_note;
566         SLIST_INSERT_HEAD(klist, kn, kn_selnext);
567         crit_exit();
568
569         return (0);
570 }
571
572 PDEVSTATIC void
573 i4brbchkfilt_detach(struct knote *kn)
574 {
575         cdev_t dev = (cdev_t)kn->kn_hook;
576         int unit = minor(dev);
577         struct rbch_softc *sc = &rbch_softc[unit];
578         struct klist *klist;
579
580         crit_enter();
581         klist = &sc->selp.si_note;
582         SLIST_REMOVE(klist, kn, knote, kn_selnext);
583         crit_exit();
584 }
585
586 PDEVSTATIC int
587 i4brbchkfilt_read(struct knote *kn, long hint)
588 {
589         cdev_t dev = (cdev_t)kn->kn_hook;
590         int unit = minor(dev);
591         struct rbch_softc *sc = &rbch_softc[unit];
592         int ready = 0;
593
594         crit_enter();
595
596         if (!(sc->sc_devstate & ST_ISOPEN)) {
597                 crit_exit();
598                 kn->kn_flags |= EV_ERROR;
599                 kn->kn_data = EBADF;
600                 return (0);
601         }
602
603         if (sc->sc_devstate & ST_CONNECTED) {
604                 struct ifqueue *iqp;
605
606                 if(sc->sc_bprot == BPROT_RHDLC)
607                         iqp = &sc->sc_hdlcq;
608                 else
609                         iqp = isdn_linktab[unit]->rx_queue;
610
611                 if(!IF_QEMPTY(iqp))
612                         ready = 1;
613         }
614
615         crit_exit();
616
617         return (ready);
618 }
619
620 PDEVSTATIC int
621 i4brbchkfilt_write(struct knote *kn, long hint)
622 {
623         cdev_t dev = (cdev_t)kn->kn_hook;
624         int unit = minor(dev);
625         struct rbch_softc *sc = &rbch_softc[unit];
626         int ready = 0;
627
628         crit_enter();
629
630         if (!(sc->sc_devstate & ST_ISOPEN)) {
631                 crit_exit();
632                 kn->kn_flags |= EV_ERROR;
633                 kn->kn_data = EBADF;
634                 return (0);
635         }
636
637         /*
638          * Writes are OK if we are connected and the
639          * transmit queue can take them
640          */
641         if ((sc->sc_devstate & ST_CONNECTED) &&
642            !IF_QFULL(isdn_linktab[unit]->tx_queue))
643         {
644                 ready = 1;
645         }
646
647         crit_exit();
648
649         return (ready);
650 }
651
652 #if I4BRBCHACCT
653 /*---------------------------------------------------------------------------*
654  *      watchdog routine
655  *---------------------------------------------------------------------------*/
656 static void
657 rbch_timeout(struct rbch_softc *sc)
658 {
659         bchan_statistics_t bs;
660         int unit = sc->sc_unit;
661
662         /* get # of bytes in and out from the HSCX driver */ 
663         
664         (*isdn_linktab[unit]->bch_stat)
665                 (isdn_linktab[unit]->unit, isdn_linktab[unit]->channel, &bs);
666
667         sc->sc_ioutb += bs.outbytes;
668         sc->sc_iinb += bs.inbytes;
669         
670         if((sc->sc_iinb != sc->sc_linb) || (sc->sc_ioutb != sc->sc_loutb) || sc->sc_fn) 
671         {
672                 int ri = (sc->sc_iinb - sc->sc_linb)/I4BRBCHACCTINTVL;
673                 int ro = (sc->sc_ioutb - sc->sc_loutb)/I4BRBCHACCTINTVL;
674
675                 if((sc->sc_iinb == sc->sc_linb) && (sc->sc_ioutb == sc->sc_loutb))
676                         sc->sc_fn = 0;
677                 else
678                         sc->sc_fn = 1;
679                         
680                 sc->sc_linb = sc->sc_iinb;
681                 sc->sc_loutb = sc->sc_ioutb;
682
683                 i4b_l4_accounting(BDRV_RBCH, unit, ACCT_DURING,
684                          sc->sc_ioutb, sc->sc_iinb, ro, ri, sc->sc_ioutb, sc->sc_iinb);
685         }
686         callout_reset(&sc->sc_timeout, I4BRBCHACCTINTVL * hz,
687                         (void *)rbch_timeout, sc);
688 }
689 #endif /* I4BRBCHACCT */
690
691 /*===========================================================================*
692  *                      ISDN INTERFACE ROUTINES
693  *===========================================================================*/
694
695 /*---------------------------------------------------------------------------*
696  *      this routine is called from L4 handler at connect time
697  *---------------------------------------------------------------------------*/
698 static void
699 rbch_connect(int unit, void *cdp)
700 {
701         call_desc_t *cd = (call_desc_t *)cdp;
702         struct rbch_softc *sc = &rbch_softc[unit];
703
704         sc->sc_bprot = cd->bprot;
705
706 #if I4BRBCHACCT
707         if(sc->sc_bprot == BPROT_RHDLC)
708         {       
709                 sc->sc_iinb = 0;
710                 sc->sc_ioutb = 0;
711                 sc->sc_linb = 0;
712                 sc->sc_loutb = 0;
713
714                 callout_reset(&sc->sc_timeout, I4BRBCHACCTINTVL * hz,
715                                 (void *)rbch_timeout, sc);
716         }
717 #endif          
718         if(!(sc->sc_devstate & ST_CONNECTED))
719         {
720                 NDBGL4(L4_RBCHDBG, "unit %d, wakeup", unit);
721                 sc->sc_devstate |= ST_CONNECTED;
722                 sc->sc_cd = cdp;
723                 wakeup((caddr_t)sc);
724         }
725 }
726
727 /*---------------------------------------------------------------------------*
728  *      this routine is called from L4 handler at disconnect time
729  *---------------------------------------------------------------------------*/
730 static void
731 rbch_disconnect(int unit, void *cdp)
732 {
733         call_desc_t *cd = (call_desc_t *)cdp;
734         struct rbch_softc *sc = &rbch_softc[unit];
735
736         CRIT_VAR;
737         
738         if(cd != sc->sc_cd)
739         {
740                 NDBGL4(L4_RBCHDBG, "rbch%d: channel %d not active",
741                         cd->driver_unit, cd->channelid);
742                 return;
743         }
744
745         CRIT_BEG;
746         
747         NDBGL4(L4_RBCHDBG, "unit %d, disconnect", unit);
748
749         sc->sc_devstate &= ~ST_CONNECTED;
750
751         sc->sc_cd = NULL;
752         
753 #if I4BRBCHACCT
754         i4b_l4_accounting(BDRV_RBCH, unit, ACCT_FINAL,
755                  sc->sc_ioutb, sc->sc_iinb, 0, 0, sc->sc_ioutb, sc->sc_iinb);
756         callout_stop(&sc->sc_timeout);
757 #endif          
758         CRIT_END;
759 }
760         
761 /*---------------------------------------------------------------------------*
762  *      feedback from daemon in case of dial problems
763  *---------------------------------------------------------------------------*/
764 static void
765 rbch_dialresponse(int unit, int status, cause_t cause)
766 {
767 }
768         
769 /*---------------------------------------------------------------------------*
770  *      interface up/down
771  *---------------------------------------------------------------------------*/
772 static void
773 rbch_updown(int unit, int updown)
774 {
775 }
776         
777 /*---------------------------------------------------------------------------*
778  *      this routine is called from the HSCX interrupt handler
779  *      when a new frame (mbuf) has been received and is to be put on
780  *      the rx queue.
781  *---------------------------------------------------------------------------*/
782 static void
783 rbch_rx_data_rdy(int unit)
784 {
785         if(rbch_softc[unit].sc_bprot == BPROT_RHDLC)
786         {
787                 struct mbuf *m;
788                 
789                 if((m = *isdn_linktab[unit]->rx_mbuf) == NULL)
790                         return;
791
792                 m->m_pkthdr.len = m->m_len;
793
794                 if(IF_QFULL(&(rbch_softc[unit].sc_hdlcq)))
795                 {
796                         NDBGL4(L4_RBCHDBG, "unit %d: hdlc rx queue full!", unit);
797                         m_freem(m);
798                 }                       
799                 else
800                 {
801                         IF_ENQUEUE(&(rbch_softc[unit].sc_hdlcq), m);
802                 }
803         }
804
805         if(rbch_softc[unit].sc_devstate & ST_RDWAITDATA)
806         {
807                 NDBGL4(L4_RBCHDBG, "unit %d, wakeup", unit);
808                 rbch_softc[unit].sc_devstate &= ~ST_RDWAITDATA;
809                 wakeup((caddr_t) &isdn_linktab[unit]->rx_queue);
810         }
811         else
812         {
813                 NDBGL4(L4_RBCHDBG, "unit %d, NO wakeup", unit);
814         }
815         selwakeup(&rbch_softc[unit].selp);
816 }
817
818 /*---------------------------------------------------------------------------*
819  *      this routine is called from the HSCX interrupt handler
820  *      when the last frame has been sent out and there is no
821  *      further frame (mbuf) in the tx queue.
822  *---------------------------------------------------------------------------*/
823 static void
824 rbch_tx_queue_empty(int unit)
825 {
826         if(rbch_softc[unit].sc_devstate & ST_WRWAITEMPTY)
827         {
828                 NDBGL4(L4_RBCHDBG, "unit %d, wakeup", unit);
829                 rbch_softc[unit].sc_devstate &= ~ST_WRWAITEMPTY;
830                 wakeup((caddr_t) &isdn_linktab[unit]->tx_queue);
831         }
832         else
833         {
834                 NDBGL4(L4_RBCHDBG, "unit %d, NO wakeup", unit);
835         }
836         selwakeup(&rbch_softc[unit].selp);
837 }
838
839 /*---------------------------------------------------------------------------*
840  *      this routine is called from the HSCX interrupt handler
841  *      each time a packet is received or transmitted
842  *---------------------------------------------------------------------------*/
843 static void
844 rbch_activity(int unit, int rxtx)
845 {
846         if (rbch_softc[unit].sc_cd)
847                 rbch_softc[unit].sc_cd->last_active_time = SECOND;
848         selwakeup(&rbch_softc[unit].selp);
849 }
850
851 /*---------------------------------------------------------------------------*
852  *      clear an hdlc rx queue for a rbch unit
853  *---------------------------------------------------------------------------*/
854 static void
855 rbch_clrq(int unit)
856 {
857         CRIT_VAR;
858
859         CRIT_BEG;
860         IF_DRAIN(&rbch_softc[unit].sc_hdlcq);
861         CRIT_END;
862 }
863                                 
864 /*---------------------------------------------------------------------------*
865  *      return this drivers linktab address
866  *---------------------------------------------------------------------------*/
867 drvr_link_t *
868 rbch_ret_linktab(int unit)
869 {
870         rbch_init_linktab(unit);
871         return(&rbch_drvr_linktab[unit]);
872 }
873
874 /*---------------------------------------------------------------------------*
875  *      setup the isdn_linktab for this driver
876  *---------------------------------------------------------------------------*/
877 void
878 rbch_set_linktab(int unit, isdn_link_t *ilt)
879 {
880         isdn_linktab[unit] = ilt;
881 }
882
883 /*---------------------------------------------------------------------------*
884  *      initialize this drivers linktab
885  *---------------------------------------------------------------------------*/
886 static void
887 rbch_init_linktab(int unit)
888 {
889         rbch_drvr_linktab[unit].unit = unit;
890         rbch_drvr_linktab[unit].bch_rx_data_ready = rbch_rx_data_rdy;
891         rbch_drvr_linktab[unit].bch_tx_queue_empty = rbch_tx_queue_empty;
892         rbch_drvr_linktab[unit].bch_activity = rbch_activity;   
893         rbch_drvr_linktab[unit].line_connected = rbch_connect;
894         rbch_drvr_linktab[unit].line_disconnected = rbch_disconnect;
895         rbch_drvr_linktab[unit].dial_response = rbch_dialresponse;
896         rbch_drvr_linktab[unit].updown_ind = rbch_updown;       
897 }
898
899 /*===========================================================================*/
900
901 #endif /* NI4BRBCH > 0 */