Merge branch 'vendor/OPENSSL'
[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/poll.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
122 PDEVSTATIC d_poll_t i4brbchpoll;
123 #define POLLFIELD       i4brbchpoll
124
125 #define CDEV_MAJOR 57
126
127 static struct dev_ops i4brbch_ops = {
128         { "i4brbch", CDEV_MAJOR, 0 },
129         .d_open =       i4brbchopen,
130         .d_close =      i4brbchclose,
131         .d_read =       i4brbchread,
132         .d_write =      i4brbchwrite,
133         .d_ioctl =      i4brbchioctl,
134         .d_poll =       POLLFIELD,
135 };
136
137 static void i4brbchattach(void *);
138 PSEUDO_SET(i4brbchattach, i4b_rbch);
139
140 /*===========================================================================*
141  *                      DEVICE DRIVER ROUTINES
142  *===========================================================================*/
143
144 /*---------------------------------------------------------------------------*
145  *      interface attach routine
146  *---------------------------------------------------------------------------*/
147 PDEVSTATIC void
148 i4brbchattach(void *dummy)
149 {
150         int i;
151
152 #ifndef HACK_NO_PSEUDO_ATTACH_MSG
153         kprintf("i4brbch: %d raw B channel access device(s) attached\n", NI4BRBCH);
154 #endif
155         
156         for(i=0; i < NI4BRBCH; i++)
157         {
158                 make_dev(&i4brbch_ops, i,
159                         UID_ROOT, GID_WHEEL, 0600, "i4brbch%d", i);
160
161 #if I4BRBCHACCT
162                 callout_init(&rbch_softc[i].sc_timeout);
163                 rbch_softc[i].sc_fn = 1;
164 #endif
165                 rbch_softc[i].sc_unit = i;
166                 rbch_softc[i].sc_devstate = ST_IDLE;
167                 rbch_softc[i].sc_hdlcq.ifq_maxlen = I4BRBCHMAXQLEN;
168                 rbch_softc[i].it_in.c_ispeed = rbch_softc[i].it_in.c_ospeed = 64000;
169                 termioschars(&rbch_softc[i].it_in);
170                 rbch_init_linktab(i);
171         }
172 }
173
174 /*---------------------------------------------------------------------------*
175  *      open rbch device
176  *---------------------------------------------------------------------------*/
177 PDEVSTATIC int
178 i4brbchopen(struct dev_open_args *ap)
179 {
180         cdev_t dev = ap->a_head.a_dev;
181         int unit = minor(dev);
182         
183         if(unit >= NI4BRBCH)
184                 return(ENXIO);
185
186         if(rbch_softc[unit].sc_devstate & ST_ISOPEN)
187                 return(EBUSY);
188
189 #if 0
190         rbch_clrq(unit);
191 #endif
192         
193         rbch_softc[unit].sc_devstate |= ST_ISOPEN;              
194
195         NDBGL4(L4_RBCHDBG, "unit %d, open", unit);      
196
197         return(0);
198 }
199
200 /*---------------------------------------------------------------------------*
201  *      close rbch device
202  *---------------------------------------------------------------------------*/
203 PDEVSTATIC int
204 i4brbchclose(struct dev_close_args *ap)
205 {
206         cdev_t dev = ap->a_head.a_dev;
207         int unit = minor(dev);
208         struct rbch_softc *sc = &rbch_softc[unit];
209         
210         if(sc->sc_devstate & ST_CONNECTED)
211                 i4b_l4_drvrdisc(BDRV_RBCH, unit);
212
213         sc->sc_devstate &= ~ST_ISOPEN;          
214
215         rbch_clrq(unit);
216         
217         NDBGL4(L4_RBCHDBG, "unit %d, closed", unit);
218         
219         return(0);
220 }
221
222 /*---------------------------------------------------------------------------*
223  *      read from rbch device
224  *---------------------------------------------------------------------------*/
225 PDEVSTATIC int
226 i4brbchread(struct dev_read_args *ap)
227 {
228         cdev_t dev = ap->a_head.a_dev;
229         struct uio *uio = ap->a_uio;
230         struct mbuf *m;
231         int error = 0;
232         int unit = minor(dev);
233         struct ifqueue *iqp;
234         struct rbch_softc *sc = &rbch_softc[unit];
235
236         CRIT_VAR;
237         
238         NDBGL4(L4_RBCHDBG, "unit %d, enter read", unit);
239         
240         CRIT_BEG;
241         if(!(sc->sc_devstate & ST_ISOPEN))
242         {
243                 CRIT_END;
244                 NDBGL4(L4_RBCHDBG, "unit %d, read while not open", unit);
245                 return(EIO);
246         }
247
248         if((sc->sc_devstate & ST_NOBLOCK) || (ap->a_ioflag & IO_NDELAY))
249         {
250                 if(!(sc->sc_devstate & ST_CONNECTED)) {
251                         CRIT_END;
252                         return(EWOULDBLOCK);
253                 }
254
255                 if(sc->sc_bprot == BPROT_RHDLC)
256                         iqp = &sc->sc_hdlcq;
257                 else
258                         iqp = isdn_linktab[unit]->rx_queue;     
259
260                 if(IF_QEMPTY(iqp) && (sc->sc_devstate & ST_ISOPEN)) {
261                         CRIT_END;
262                         return(EWOULDBLOCK);
263         }
264         }
265         else
266         {
267                 while(!(sc->sc_devstate & ST_CONNECTED))
268                 {
269                         NDBGL4(L4_RBCHDBG, "unit %d, wait read init", unit);
270                 
271                         if((error = tsleep((caddr_t) &rbch_softc[unit],
272                                                PCATCH, "rrrbch", 0 )) != 0)
273                         {
274                                 CRIT_END;
275                                 NDBGL4(L4_RBCHDBG, "unit %d, error %d tsleep", unit, error);
276                                 return(error);
277                         }
278                 }
279
280                 if(sc->sc_bprot == BPROT_RHDLC)
281                         iqp = &sc->sc_hdlcq;
282                 else
283                         iqp = isdn_linktab[unit]->rx_queue;     
284
285                 while(IF_QEMPTY(iqp) && (sc->sc_devstate & ST_ISOPEN))
286                 {
287                         sc->sc_devstate |= ST_RDWAITDATA;
288                 
289                         NDBGL4(L4_RBCHDBG, "unit %d, wait read data", unit);
290                 
291                         if((error = tsleep((caddr_t) &isdn_linktab[unit]->rx_queue,
292                                            PCATCH, "rrbch", 0 )) != 0)
293                         {
294                                 CRIT_END;
295                                 NDBGL4(L4_RBCHDBG, "unit %d, error %d tsleep read", unit, error);
296                                 sc->sc_devstate &= ~ST_RDWAITDATA;
297                                 return(error);
298                         }
299                 }
300         }
301
302         IF_DEQUEUE(iqp, m);
303
304         NDBGL4(L4_RBCHDBG, "unit %d, read %d bytes", unit, m->m_len);
305         
306         if(m && m->m_len)
307         {
308                 error = uiomove(m->m_data, m->m_len, uio);
309         }
310         else
311         {
312                 NDBGL4(L4_RBCHDBG, "unit %d, error %d uiomove", unit, error);
313                 error = EIO;
314         }
315                 
316         if(m)
317                 i4b_Bfreembuf(m);
318
319         CRIT_END;
320
321         return(error);
322 }
323
324 /*---------------------------------------------------------------------------*
325  *      write to rbch device
326  *---------------------------------------------------------------------------*/
327 PDEVSTATIC int
328 i4brbchwrite(struct dev_write_args *ap)
329 {
330         cdev_t dev = ap->a_head.a_dev;
331         struct uio *uio = ap->a_uio;
332         struct mbuf *m;
333         int error = 0;
334         int unit = minor(dev);
335         struct rbch_softc *sc = &rbch_softc[unit];
336
337         CRIT_VAR;
338         
339         NDBGL4(L4_RBCHDBG, "unit %d, write", unit);     
340
341         CRIT_BEG;
342         if(!(sc->sc_devstate & ST_ISOPEN))
343         {
344                 NDBGL4(L4_RBCHDBG, "unit %d, write while not open", unit);
345                 CRIT_END;
346                 return(EIO);
347         }
348
349         if((sc->sc_devstate & ST_NOBLOCK) || (ap->a_ioflag & IO_NDELAY))
350         {
351                 if(!(sc->sc_devstate & ST_CONNECTED)) {
352                         CRIT_END;
353                         return(EWOULDBLOCK);
354                 }
355                 if(IF_QFULL(isdn_linktab[unit]->tx_queue) && (sc->sc_devstate & ST_ISOPEN)) {
356                         CRIT_END;
357                         return(EWOULDBLOCK);
358         }
359         }
360         else
361         {
362                 while(!(sc->sc_devstate & ST_CONNECTED))
363                 {
364                         NDBGL4(L4_RBCHDBG, "unit %d, write wait init", unit);
365                 
366                         error = tsleep((caddr_t) &rbch_softc[unit],
367                                                    PCATCH, "wrrbch", 0 );
368                         if(error == ERESTART) {
369                                 CRIT_END;
370                                 return (ERESTART);
371                         }
372                         else if(error == EINTR)
373                         {
374                                 CRIT_END;
375                                 NDBGL4(L4_RBCHDBG, "unit %d, EINTR during wait init", unit);
376                                 return(EINTR);
377                         }
378                         else if(error)
379                         {
380                                 CRIT_END;
381                                 NDBGL4(L4_RBCHDBG, "unit %d, error %d tsleep init", unit, error);
382                                 return(error);
383                         }
384                         tsleep((caddr_t) &rbch_softc[unit], PCATCH, "xrbch", (hz*1));
385                 }
386
387                 while(IF_QFULL(isdn_linktab[unit]->tx_queue) && (sc->sc_devstate & ST_ISOPEN))
388                 {
389                         sc->sc_devstate |= ST_WRWAITEMPTY;
390
391                         NDBGL4(L4_RBCHDBG, "unit %d, write queue full", unit);
392                 
393                         if ((error = tsleep((caddr_t) &isdn_linktab[unit]->tx_queue,
394                                             PCATCH, "wrbch", 0)) != 0) {
395                                 sc->sc_devstate &= ~ST_WRWAITEMPTY;
396                                 if(error == ERESTART)
397                                 {
398                                         CRIT_END;
399                                         return(ERESTART);
400                                 }
401                                 else if(error == EINTR)
402                                 {
403                                         CRIT_END;
404                                         NDBGL4(L4_RBCHDBG, "unit %d, EINTR during wait write", unit);
405                                         return(error);
406                                 }
407                                 else if(error)
408                                 {
409                                         CRIT_END;
410                                         NDBGL4(L4_RBCHDBG, "unit %d, error %d tsleep write", unit, error);
411                                         return(error);
412                                 }
413                         }
414                 }
415         }
416
417         if(!(sc->sc_devstate & ST_ISOPEN))
418         {
419                 NDBGL4(L4_RBCHDBG, "unit %d, not open anymore", unit);
420                 CRIT_END;
421                 return(EIO);
422         }
423
424         if((m = i4b_Bgetmbuf(BCH_MAX_DATALEN)) != NULL)
425         {
426                 m->m_len = (int)szmin(BCH_MAX_DATALEN, uio->uio_resid);
427
428                 NDBGL4(L4_RBCHDBG, "unit %d, write %d bytes", unit, m->m_len);
429                 
430                 error = uiomove(m->m_data, (size_t)m->m_len, uio);
431
432                 if(IF_QFULL(isdn_linktab[unit]->tx_queue))
433                         m_freem(m);
434                 else
435                         IF_ENQUEUE(isdn_linktab[unit]->tx_queue, m);
436                 (*isdn_linktab[unit]->bch_tx_start)(isdn_linktab[unit]->unit, isdn_linktab[unit]->channel);
437         }
438
439         CRIT_END;
440         
441         return(error);
442 }
443
444 /*---------------------------------------------------------------------------*
445  *      rbch device ioctl handlibg
446  *---------------------------------------------------------------------------*/
447 PDEVSTATIC int
448 i4brbchioctl(struct dev_ioctl_args *ap)
449 {
450         cdev_t dev = ap->a_head.a_dev;
451         int error = 0;
452         int unit = minor(dev);
453         struct rbch_softc *sc = &rbch_softc[unit];
454         
455         switch(ap->a_cmd)
456         {
457                 case FIOASYNC:  /* Set async mode */
458                         if (*(int *)ap->a_data)
459                         {
460                                 NDBGL4(L4_RBCHDBG, "unit %d, setting async mode", unit);
461                         }
462                         else
463                         {
464                                 NDBGL4(L4_RBCHDBG, "unit %d, clearing async mode", unit);
465                         }
466                         break;
467
468                 case TIOCCDTR:  /* Clear DTR */
469                         if(sc->sc_devstate & ST_CONNECTED)
470                         {
471                                 NDBGL4(L4_RBCHDBG, "unit %d, disconnecting for DTR down", unit);
472                                 i4b_l4_drvrdisc(BDRV_RBCH, unit);
473                         }
474                         break;
475
476                 case I4B_RBCH_DIALOUT:
477                 {
478                         size_t l;
479
480                         for (l = 0; l < TELNO_MAX && ((char *)ap->a_data)[l]; l++)
481                                 ;
482                         if (l)
483                         {
484                                 NDBGL4(L4_RBCHDBG, "unit %d, attempting dialout to %s", unit, (char *)ap->a_data);
485                                 i4b_l4_dialoutnumber(BDRV_RBCH, unit, l, (char *)ap->a_data);
486                                 break;
487                         }
488                         /* fall through to SDTR */
489                 }
490
491                 case TIOCSDTR:  /* Set DTR */
492                         NDBGL4(L4_RBCHDBG, "unit %d, attempting dialout (DTR)", unit);
493                         i4b_l4_dialout(BDRV_RBCH, unit);
494                         break;
495
496                 case TIOCSETA:  /* Set termios struct */
497                         break;
498
499                 case TIOCGETA:  /* Get termios struct */
500                         *(struct termios *)ap->a_data = sc->it_in;
501                         break;
502
503                 case TIOCMGET:
504                         *(int *)ap->a_data = TIOCM_LE|TIOCM_DTR|TIOCM_RTS|TIOCM_CTS|TIOCM_DSR;
505                         if (sc->sc_devstate & ST_CONNECTED)
506                                 *(int *)ap->a_data |= TIOCM_CD;
507                         break;
508
509                 case I4B_RBCH_VR_REQ:
510                 {
511                         msg_vr_req_t *mvr;
512
513                         mvr = (msg_vr_req_t *)ap->a_data;
514
515                         mvr->version = VERSION;
516                         mvr->release = REL;
517                         mvr->step = STEP;                       
518                         break;
519                 }
520
521                 default:        /* Unknown stuff */
522                         NDBGL4(L4_RBCHDBG, "unit %d, ioctl, unknown cmd %lx", unit, ap->a_cmd);
523                         error = EINVAL;
524                         break;
525         }
526         return(error);
527 }
528
529 /*---------------------------------------------------------------------------*
530  *      device driver poll
531  *---------------------------------------------------------------------------*/
532 PDEVSTATIC int
533 i4brbchpoll(struct dev_poll_args *ap)
534 {
535         cdev_t dev = ap->a_head.a_dev;
536         int revents = 0;        /* Events we found */
537         int unit = minor(dev);
538         struct rbch_softc *sc = &rbch_softc[unit];
539         
540         /* We can't check for anything but IN or OUT */
541         crit_enter();
542
543         if(!(sc->sc_devstate & ST_ISOPEN))
544         {
545                 crit_exit();
546                 return(POLLNVAL);
547         }
548
549         /*
550          * Writes are OK if we are connected and the
551          * transmit queue can take them
552          */
553          
554         if((ap->a_events & (POLLOUT|POLLWRNORM)) &&
555            (sc->sc_devstate & ST_CONNECTED) &&
556            !IF_QFULL(isdn_linktab[unit]->tx_queue))
557         {
558                 revents |= (ap->a_events & (POLLOUT|POLLWRNORM));
559         }
560         
561         /* ... while reads are OK if we have any data */
562
563         if((ap->a_events & (POLLIN|POLLRDNORM)) &&
564            (sc->sc_devstate & ST_CONNECTED))
565         {
566                 struct ifqueue *iqp;
567
568                 if(sc->sc_bprot == BPROT_RHDLC)
569                         iqp = &sc->sc_hdlcq;
570                 else
571                         iqp = isdn_linktab[unit]->rx_queue;     
572
573                 if(!IF_QEMPTY(iqp))
574                         revents |= (ap->a_events & (POLLIN|POLLRDNORM));
575         }
576                 
577         if(revents == 0)
578                 selrecord(curthread, &sc->selp);
579
580         crit_exit();
581         ap->a_events = revents;
582         return (0);
583 }
584
585 #if I4BRBCHACCT
586 /*---------------------------------------------------------------------------*
587  *      watchdog routine
588  *---------------------------------------------------------------------------*/
589 static void
590 rbch_timeout(struct rbch_softc *sc)
591 {
592         bchan_statistics_t bs;
593         int unit = sc->sc_unit;
594
595         /* get # of bytes in and out from the HSCX driver */ 
596         
597         (*isdn_linktab[unit]->bch_stat)
598                 (isdn_linktab[unit]->unit, isdn_linktab[unit]->channel, &bs);
599
600         sc->sc_ioutb += bs.outbytes;
601         sc->sc_iinb += bs.inbytes;
602         
603         if((sc->sc_iinb != sc->sc_linb) || (sc->sc_ioutb != sc->sc_loutb) || sc->sc_fn) 
604         {
605                 int ri = (sc->sc_iinb - sc->sc_linb)/I4BRBCHACCTINTVL;
606                 int ro = (sc->sc_ioutb - sc->sc_loutb)/I4BRBCHACCTINTVL;
607
608                 if((sc->sc_iinb == sc->sc_linb) && (sc->sc_ioutb == sc->sc_loutb))
609                         sc->sc_fn = 0;
610                 else
611                         sc->sc_fn = 1;
612                         
613                 sc->sc_linb = sc->sc_iinb;
614                 sc->sc_loutb = sc->sc_ioutb;
615
616                 i4b_l4_accounting(BDRV_RBCH, unit, ACCT_DURING,
617                          sc->sc_ioutb, sc->sc_iinb, ro, ri, sc->sc_ioutb, sc->sc_iinb);
618         }
619         callout_reset(&sc->sc_timeout, I4BRBCHACCTINTVL * hz,
620                         (void *)rbch_timeout, sc);
621 }
622 #endif /* I4BRBCHACCT */
623
624 /*===========================================================================*
625  *                      ISDN INTERFACE ROUTINES
626  *===========================================================================*/
627
628 /*---------------------------------------------------------------------------*
629  *      this routine is called from L4 handler at connect time
630  *---------------------------------------------------------------------------*/
631 static void
632 rbch_connect(int unit, void *cdp)
633 {
634         call_desc_t *cd = (call_desc_t *)cdp;
635         struct rbch_softc *sc = &rbch_softc[unit];
636
637         sc->sc_bprot = cd->bprot;
638
639 #if I4BRBCHACCT
640         if(sc->sc_bprot == BPROT_RHDLC)
641         {       
642                 sc->sc_iinb = 0;
643                 sc->sc_ioutb = 0;
644                 sc->sc_linb = 0;
645                 sc->sc_loutb = 0;
646
647                 callout_reset(&sc->sc_timeout, I4BRBCHACCTINTVL * hz,
648                                 (void *)rbch_timeout, sc);
649         }
650 #endif          
651         if(!(sc->sc_devstate & ST_CONNECTED))
652         {
653                 NDBGL4(L4_RBCHDBG, "unit %d, wakeup", unit);
654                 sc->sc_devstate |= ST_CONNECTED;
655                 sc->sc_cd = cdp;
656                 wakeup((caddr_t)sc);
657         }
658 }
659
660 /*---------------------------------------------------------------------------*
661  *      this routine is called from L4 handler at disconnect time
662  *---------------------------------------------------------------------------*/
663 static void
664 rbch_disconnect(int unit, void *cdp)
665 {
666         call_desc_t *cd = (call_desc_t *)cdp;
667         struct rbch_softc *sc = &rbch_softc[unit];
668
669         CRIT_VAR;
670         
671         if(cd != sc->sc_cd)
672         {
673                 NDBGL4(L4_RBCHDBG, "rbch%d: channel %d not active",
674                         cd->driver_unit, cd->channelid);
675                 return;
676         }
677
678         CRIT_BEG;
679         
680         NDBGL4(L4_RBCHDBG, "unit %d, disconnect", unit);
681
682         sc->sc_devstate &= ~ST_CONNECTED;
683
684         sc->sc_cd = NULL;
685         
686 #if I4BRBCHACCT
687         i4b_l4_accounting(BDRV_RBCH, unit, ACCT_FINAL,
688                  sc->sc_ioutb, sc->sc_iinb, 0, 0, sc->sc_ioutb, sc->sc_iinb);
689         callout_stop(&sc->sc_timeout);
690 #endif          
691         CRIT_END;
692 }
693         
694 /*---------------------------------------------------------------------------*
695  *      feedback from daemon in case of dial problems
696  *---------------------------------------------------------------------------*/
697 static void
698 rbch_dialresponse(int unit, int status, cause_t cause)
699 {
700 }
701         
702 /*---------------------------------------------------------------------------*
703  *      interface up/down
704  *---------------------------------------------------------------------------*/
705 static void
706 rbch_updown(int unit, int updown)
707 {
708 }
709         
710 /*---------------------------------------------------------------------------*
711  *      this routine is called from the HSCX interrupt handler
712  *      when a new frame (mbuf) has been received and is to be put on
713  *      the rx queue.
714  *---------------------------------------------------------------------------*/
715 static void
716 rbch_rx_data_rdy(int unit)
717 {
718         if(rbch_softc[unit].sc_bprot == BPROT_RHDLC)
719         {
720                 struct mbuf *m;
721                 
722                 if((m = *isdn_linktab[unit]->rx_mbuf) == NULL)
723                         return;
724
725                 m->m_pkthdr.len = m->m_len;
726
727                 if(IF_QFULL(&(rbch_softc[unit].sc_hdlcq)))
728                 {
729                         NDBGL4(L4_RBCHDBG, "unit %d: hdlc rx queue full!", unit);
730                         m_freem(m);
731                 }                       
732                 else
733                 {
734                         IF_ENQUEUE(&(rbch_softc[unit].sc_hdlcq), m);
735                 }
736         }
737
738         if(rbch_softc[unit].sc_devstate & ST_RDWAITDATA)
739         {
740                 NDBGL4(L4_RBCHDBG, "unit %d, wakeup", unit);
741                 rbch_softc[unit].sc_devstate &= ~ST_RDWAITDATA;
742                 wakeup((caddr_t) &isdn_linktab[unit]->rx_queue);
743         }
744         else
745         {
746                 NDBGL4(L4_RBCHDBG, "unit %d, NO wakeup", unit);
747         }
748         selwakeup(&rbch_softc[unit].selp);
749 }
750
751 /*---------------------------------------------------------------------------*
752  *      this routine is called from the HSCX interrupt handler
753  *      when the last frame has been sent out and there is no
754  *      further frame (mbuf) in the tx queue.
755  *---------------------------------------------------------------------------*/
756 static void
757 rbch_tx_queue_empty(int unit)
758 {
759         if(rbch_softc[unit].sc_devstate & ST_WRWAITEMPTY)
760         {
761                 NDBGL4(L4_RBCHDBG, "unit %d, wakeup", unit);
762                 rbch_softc[unit].sc_devstate &= ~ST_WRWAITEMPTY;
763                 wakeup((caddr_t) &isdn_linktab[unit]->tx_queue);
764         }
765         else
766         {
767                 NDBGL4(L4_RBCHDBG, "unit %d, NO wakeup", unit);
768         }
769         selwakeup(&rbch_softc[unit].selp);
770 }
771
772 /*---------------------------------------------------------------------------*
773  *      this routine is called from the HSCX interrupt handler
774  *      each time a packet is received or transmitted
775  *---------------------------------------------------------------------------*/
776 static void
777 rbch_activity(int unit, int rxtx)
778 {
779         if (rbch_softc[unit].sc_cd)
780                 rbch_softc[unit].sc_cd->last_active_time = SECOND;
781         selwakeup(&rbch_softc[unit].selp);
782 }
783
784 /*---------------------------------------------------------------------------*
785  *      clear an hdlc rx queue for a rbch unit
786  *---------------------------------------------------------------------------*/
787 static void
788 rbch_clrq(int unit)
789 {
790         CRIT_VAR;
791
792         CRIT_BEG;
793         IF_DRAIN(&rbch_softc[unit].sc_hdlcq);
794         CRIT_END;
795 }
796                                 
797 /*---------------------------------------------------------------------------*
798  *      return this drivers linktab address
799  *---------------------------------------------------------------------------*/
800 drvr_link_t *
801 rbch_ret_linktab(int unit)
802 {
803         rbch_init_linktab(unit);
804         return(&rbch_drvr_linktab[unit]);
805 }
806
807 /*---------------------------------------------------------------------------*
808  *      setup the isdn_linktab for this driver
809  *---------------------------------------------------------------------------*/
810 void
811 rbch_set_linktab(int unit, isdn_link_t *ilt)
812 {
813         isdn_linktab[unit] = ilt;
814 }
815
816 /*---------------------------------------------------------------------------*
817  *      initialize this drivers linktab
818  *---------------------------------------------------------------------------*/
819 static void
820 rbch_init_linktab(int unit)
821 {
822         rbch_drvr_linktab[unit].unit = unit;
823         rbch_drvr_linktab[unit].bch_rx_data_ready = rbch_rx_data_rdy;
824         rbch_drvr_linktab[unit].bch_tx_queue_empty = rbch_tx_queue_empty;
825         rbch_drvr_linktab[unit].bch_activity = rbch_activity;   
826         rbch_drvr_linktab[unit].line_connected = rbch_connect;
827         rbch_drvr_linktab[unit].line_disconnected = rbch_disconnect;
828         rbch_drvr_linktab[unit].dial_response = rbch_dialresponse;
829         rbch_drvr_linktab[unit].updown_ind = rbch_updown;       
830 }
831
832 /*===========================================================================*/
833
834 #endif /* NI4BRBCH > 0 */