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