kernel - Make filters able to be marked MPSAFE
[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 kqinfo kqp;              /* select / poll / kevent */
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         { FILTEROP_ISFD, NULL, i4brbchkfilt_detach, i4brbchkfilt_read };
536 static struct filterops i4brbchkfiltops_write =
537         { FILTEROP_ISFD, 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         klist = &sc->kqp.ki_note;
565         knote_insert(klist, kn);
566
567         return (0);
568 }
569
570 PDEVSTATIC void
571 i4brbchkfilt_detach(struct knote *kn)
572 {
573         cdev_t dev = (cdev_t)kn->kn_hook;
574         int unit = minor(dev);
575         struct rbch_softc *sc = &rbch_softc[unit];
576         struct klist *klist;
577
578         klist = &sc->kqp.ki_note;
579         knote_remove(klist, kn);
580 }
581
582 PDEVSTATIC int
583 i4brbchkfilt_read(struct knote *kn, long hint)
584 {
585         cdev_t dev = (cdev_t)kn->kn_hook;
586         int unit = minor(dev);
587         struct rbch_softc *sc = &rbch_softc[unit];
588         int ready = 0;
589
590         crit_enter();
591
592         if (!(sc->sc_devstate & ST_ISOPEN)) {
593                 crit_exit();
594                 kn->kn_flags |= EV_ERROR;
595                 kn->kn_data = EBADF;
596                 return (0);
597         }
598
599         if (sc->sc_devstate & ST_CONNECTED) {
600                 struct ifqueue *iqp;
601
602                 if(sc->sc_bprot == BPROT_RHDLC)
603                         iqp = &sc->sc_hdlcq;
604                 else
605                         iqp = isdn_linktab[unit]->rx_queue;
606
607                 if(!IF_QEMPTY(iqp))
608                         ready = 1;
609         }
610
611         crit_exit();
612
613         return (ready);
614 }
615
616 PDEVSTATIC int
617 i4brbchkfilt_write(struct knote *kn, long hint)
618 {
619         cdev_t dev = (cdev_t)kn->kn_hook;
620         int unit = minor(dev);
621         struct rbch_softc *sc = &rbch_softc[unit];
622         int ready = 0;
623
624         crit_enter();
625
626         if (!(sc->sc_devstate & ST_ISOPEN)) {
627                 crit_exit();
628                 kn->kn_flags |= EV_ERROR;
629                 kn->kn_data = EBADF;
630                 return (0);
631         }
632
633         /*
634          * Writes are OK if we are connected and the
635          * transmit queue can take them
636          */
637         if ((sc->sc_devstate & ST_CONNECTED) &&
638            !IF_QFULL(isdn_linktab[unit]->tx_queue))
639         {
640                 ready = 1;
641         }
642
643         crit_exit();
644
645         return (ready);
646 }
647
648 #if I4BRBCHACCT
649 /*---------------------------------------------------------------------------*
650  *      watchdog routine
651  *---------------------------------------------------------------------------*/
652 static void
653 rbch_timeout(struct rbch_softc *sc)
654 {
655         bchan_statistics_t bs;
656         int unit = sc->sc_unit;
657
658         /* get # of bytes in and out from the HSCX driver */ 
659         
660         (*isdn_linktab[unit]->bch_stat)
661                 (isdn_linktab[unit]->unit, isdn_linktab[unit]->channel, &bs);
662
663         sc->sc_ioutb += bs.outbytes;
664         sc->sc_iinb += bs.inbytes;
665         
666         if((sc->sc_iinb != sc->sc_linb) || (sc->sc_ioutb != sc->sc_loutb) || sc->sc_fn) 
667         {
668                 int ri = (sc->sc_iinb - sc->sc_linb)/I4BRBCHACCTINTVL;
669                 int ro = (sc->sc_ioutb - sc->sc_loutb)/I4BRBCHACCTINTVL;
670
671                 if((sc->sc_iinb == sc->sc_linb) && (sc->sc_ioutb == sc->sc_loutb))
672                         sc->sc_fn = 0;
673                 else
674                         sc->sc_fn = 1;
675                         
676                 sc->sc_linb = sc->sc_iinb;
677                 sc->sc_loutb = sc->sc_ioutb;
678
679                 i4b_l4_accounting(BDRV_RBCH, unit, ACCT_DURING,
680                          sc->sc_ioutb, sc->sc_iinb, ro, ri, sc->sc_ioutb, sc->sc_iinb);
681         }
682         callout_reset(&sc->sc_timeout, I4BRBCHACCTINTVL * hz,
683                         (void *)rbch_timeout, sc);
684 }
685 #endif /* I4BRBCHACCT */
686
687 /*===========================================================================*
688  *                      ISDN INTERFACE ROUTINES
689  *===========================================================================*/
690
691 /*---------------------------------------------------------------------------*
692  *      this routine is called from L4 handler at connect time
693  *---------------------------------------------------------------------------*/
694 static void
695 rbch_connect(int unit, void *cdp)
696 {
697         call_desc_t *cd = (call_desc_t *)cdp;
698         struct rbch_softc *sc = &rbch_softc[unit];
699
700         sc->sc_bprot = cd->bprot;
701
702 #if I4BRBCHACCT
703         if(sc->sc_bprot == BPROT_RHDLC)
704         {       
705                 sc->sc_iinb = 0;
706                 sc->sc_ioutb = 0;
707                 sc->sc_linb = 0;
708                 sc->sc_loutb = 0;
709
710                 callout_reset(&sc->sc_timeout, I4BRBCHACCTINTVL * hz,
711                                 (void *)rbch_timeout, sc);
712         }
713 #endif          
714         if(!(sc->sc_devstate & ST_CONNECTED))
715         {
716                 NDBGL4(L4_RBCHDBG, "unit %d, wakeup", unit);
717                 sc->sc_devstate |= ST_CONNECTED;
718                 sc->sc_cd = cdp;
719                 wakeup((caddr_t)sc);
720         }
721 }
722
723 /*---------------------------------------------------------------------------*
724  *      this routine is called from L4 handler at disconnect time
725  *---------------------------------------------------------------------------*/
726 static void
727 rbch_disconnect(int unit, void *cdp)
728 {
729         call_desc_t *cd = (call_desc_t *)cdp;
730         struct rbch_softc *sc = &rbch_softc[unit];
731
732         CRIT_VAR;
733         
734         if(cd != sc->sc_cd)
735         {
736                 NDBGL4(L4_RBCHDBG, "rbch%d: channel %d not active",
737                         cd->driver_unit, cd->channelid);
738                 return;
739         }
740
741         CRIT_BEG;
742         
743         NDBGL4(L4_RBCHDBG, "unit %d, disconnect", unit);
744
745         sc->sc_devstate &= ~ST_CONNECTED;
746
747         sc->sc_cd = NULL;
748         
749 #if I4BRBCHACCT
750         i4b_l4_accounting(BDRV_RBCH, unit, ACCT_FINAL,
751                  sc->sc_ioutb, sc->sc_iinb, 0, 0, sc->sc_ioutb, sc->sc_iinb);
752         callout_stop(&sc->sc_timeout);
753 #endif          
754         CRIT_END;
755 }
756         
757 /*---------------------------------------------------------------------------*
758  *      feedback from daemon in case of dial problems
759  *---------------------------------------------------------------------------*/
760 static void
761 rbch_dialresponse(int unit, int status, cause_t cause)
762 {
763 }
764         
765 /*---------------------------------------------------------------------------*
766  *      interface up/down
767  *---------------------------------------------------------------------------*/
768 static void
769 rbch_updown(int unit, int updown)
770 {
771 }
772         
773 /*---------------------------------------------------------------------------*
774  *      this routine is called from the HSCX interrupt handler
775  *      when a new frame (mbuf) has been received and is to be put on
776  *      the rx queue.
777  *---------------------------------------------------------------------------*/
778 static void
779 rbch_rx_data_rdy(int unit)
780 {
781         if(rbch_softc[unit].sc_bprot == BPROT_RHDLC)
782         {
783                 struct mbuf *m;
784                 
785                 if((m = *isdn_linktab[unit]->rx_mbuf) == NULL)
786                         return;
787
788                 m->m_pkthdr.len = m->m_len;
789
790                 if(IF_QFULL(&(rbch_softc[unit].sc_hdlcq)))
791                 {
792                         NDBGL4(L4_RBCHDBG, "unit %d: hdlc rx queue full!", unit);
793                         m_freem(m);
794                 }                       
795                 else
796                 {
797                         IF_ENQUEUE(&(rbch_softc[unit].sc_hdlcq), m);
798                 }
799         }
800
801         if(rbch_softc[unit].sc_devstate & ST_RDWAITDATA)
802         {
803                 NDBGL4(L4_RBCHDBG, "unit %d, wakeup", unit);
804                 rbch_softc[unit].sc_devstate &= ~ST_RDWAITDATA;
805                 wakeup((caddr_t) &isdn_linktab[unit]->rx_queue);
806         }
807         else
808         {
809                 NDBGL4(L4_RBCHDBG, "unit %d, NO wakeup", unit);
810         }
811         KNOTE(&rbch_softc[unit].kqp.ki_note, 0);
812 }
813
814 /*---------------------------------------------------------------------------*
815  *      this routine is called from the HSCX interrupt handler
816  *      when the last frame has been sent out and there is no
817  *      further frame (mbuf) in the tx queue.
818  *---------------------------------------------------------------------------*/
819 static void
820 rbch_tx_queue_empty(int unit)
821 {
822         if(rbch_softc[unit].sc_devstate & ST_WRWAITEMPTY)
823         {
824                 NDBGL4(L4_RBCHDBG, "unit %d, wakeup", unit);
825                 rbch_softc[unit].sc_devstate &= ~ST_WRWAITEMPTY;
826                 wakeup((caddr_t) &isdn_linktab[unit]->tx_queue);
827         }
828         else
829         {
830                 NDBGL4(L4_RBCHDBG, "unit %d, NO wakeup", unit);
831         }
832         KNOTE(&rbch_softc[unit].kqp.ki_note, 0);
833 }
834
835 /*---------------------------------------------------------------------------*
836  *      this routine is called from the HSCX interrupt handler
837  *      each time a packet is received or transmitted
838  *---------------------------------------------------------------------------*/
839 static void
840 rbch_activity(int unit, int rxtx)
841 {
842         if (rbch_softc[unit].sc_cd)
843                 rbch_softc[unit].sc_cd->last_active_time = SECOND;
844         KNOTE(&rbch_softc[unit].kqp.ki_note, 0);
845 }
846
847 /*---------------------------------------------------------------------------*
848  *      clear an hdlc rx queue for a rbch unit
849  *---------------------------------------------------------------------------*/
850 static void
851 rbch_clrq(int unit)
852 {
853         CRIT_VAR;
854
855         CRIT_BEG;
856         IF_DRAIN(&rbch_softc[unit].sc_hdlcq);
857         CRIT_END;
858 }
859                                 
860 /*---------------------------------------------------------------------------*
861  *      return this drivers linktab address
862  *---------------------------------------------------------------------------*/
863 drvr_link_t *
864 rbch_ret_linktab(int unit)
865 {
866         rbch_init_linktab(unit);
867         return(&rbch_drvr_linktab[unit]);
868 }
869
870 /*---------------------------------------------------------------------------*
871  *      setup the isdn_linktab for this driver
872  *---------------------------------------------------------------------------*/
873 void
874 rbch_set_linktab(int unit, isdn_link_t *ilt)
875 {
876         isdn_linktab[unit] = ilt;
877 }
878
879 /*---------------------------------------------------------------------------*
880  *      initialize this drivers linktab
881  *---------------------------------------------------------------------------*/
882 static void
883 rbch_init_linktab(int unit)
884 {
885         rbch_drvr_linktab[unit].unit = unit;
886         rbch_drvr_linktab[unit].bch_rx_data_ready = rbch_rx_data_rdy;
887         rbch_drvr_linktab[unit].bch_tx_queue_empty = rbch_tx_queue_empty;
888         rbch_drvr_linktab[unit].bch_activity = rbch_activity;   
889         rbch_drvr_linktab[unit].line_connected = rbch_connect;
890         rbch_drvr_linktab[unit].line_disconnected = rbch_disconnect;
891         rbch_drvr_linktab[unit].dial_response = rbch_dialresponse;
892         rbch_drvr_linktab[unit].updown_ind = rbch_updown;       
893 }
894
895 /*===========================================================================*/
896
897 #endif /* NI4BRBCH > 0 */