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