]> CyberLeo.Net >> Repos - FreeBSD/releng/7.2.git/blob - sys/i4b/driver/i4b_rbch.c
Create releng/7.2 from stable/7 in preparation for 7.2-RELEASE.
[FreeBSD/releng/7.2.git] / sys / i4b / driver / i4b_rbch.c
1 /*-
2  * Copyright (c) 1997, 2002 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  *
28  *      i4b_rbch.c - device driver for raw B channel data
29  *      ---------------------------------------------------
30  *      last edit-date: [Sun Mar 17 09:51:03 2002]
31  *
32  *---------------------------------------------------------------------------*/
33
34 #include <sys/cdefs.h>
35 __FBSDID("$FreeBSD$");
36
37 #include "opt_i4b.h"
38
39 #include <sys/param.h>
40 #include <sys/systm.h>
41 #include <sys/conf.h>
42 #include <sys/uio.h>
43 #include <sys/kernel.h>
44 #include <sys/mbuf.h>
45 #include <sys/socket.h>
46 #include <sys/filio.h>
47 #include <sys/tty.h>
48
49 #include <net/if.h>
50
51 #include <i4b/include/i4b_ioctl.h>
52 #include <i4b/include/i4b_rbch_ioctl.h>
53 #include <i4b/include/i4b_debug.h>
54
55 #include <i4b/include/i4b_global.h>
56 #include <i4b/include/i4b_mbuf.h>
57 #include <i4b/include/i4b_l3l4.h>
58
59 #include <i4b/layer4/i4b_l4.h>
60
61 #include <sys/ioccom.h>
62 #include <sys/poll.h>
63
64 static drvr_link_t rbch_drvr_linktab[NI4BRBCH];
65 static isdn_link_t *isdn_linktab[NI4BRBCH];
66
67 #define I4BRBCHACCT             1       /* enable accounting messages */
68 #define I4BRBCHACCTINTVL        2       /* accounting msg interval in secs */
69
70 static struct rbch_softc {
71         int sc_unit;                    /* unit number          */
72
73         int sc_devstate;                /* state of driver      */
74 #define ST_IDLE         0x00
75 #define ST_CONNECTED    0x01
76 #define ST_ISOPEN       0x02
77 #define ST_RDWAITDATA   0x04
78 #define ST_WRWAITEMPTY  0x08
79 #define ST_NOBLOCK      0x10
80
81         int sc_bprot;                   /* B-ch protocol used   */
82         call_desc_t *sc_cd;             /* Call Descriptor */
83         struct termios it_in;
84
85         struct ifqueue sc_hdlcq;        /* hdlc read queue      */
86 #define I4BRBCHMAXQLEN  10
87
88         struct selinfo selp;            /* select / poll        */
89
90 #if I4BRBCHACCT
91         struct callout_handle sc_callout;
92         int             sc_iinb;        /* isdn driver # of inbytes     */
93         int             sc_ioutb;       /* isdn driver # of outbytes    */
94         int             sc_linb;        /* last # of bytes rx'd         */
95         int             sc_loutb;       /* last # of bytes tx'd         */
96         int             sc_fn;          /* flag, first null acct        */
97 #endif  
98 } rbch_softc[NI4BRBCH];
99
100 static void rbch_rx_data_rdy(int unit);
101 static void rbch_tx_queue_empty(int unit);
102 static void rbch_connect(int unit, void *cdp);
103 static void rbch_disconnect(int unit, void *cdp);
104 static void rbch_init_linktab(int unit);
105 static void rbch_clrq(int unit);
106
107 static  d_open_t        i4brbchopen;
108 static  d_close_t       i4brbchclose;
109 static  d_read_t        i4brbchread;
110 static  d_read_t        i4brbchwrite;
111 static  d_ioctl_t       i4brbchioctl;
112 static  d_poll_t        i4brbchpoll;
113
114
115 static struct cdevsw i4brbch_cdevsw = {
116         .d_version =    D_VERSION,
117         .d_flags =      D_NEEDGIANT,
118         .d_open =       i4brbchopen,
119         .d_close =      i4brbchclose,
120         .d_read =       i4brbchread,
121         .d_write =      i4brbchwrite,
122         .d_ioctl =      i4brbchioctl,
123         .d_poll =       i4brbchpoll,
124         .d_name =       "i4brbch",
125 };
126
127 static void i4brbchattach(void *);
128 PSEUDO_SET(i4brbchattach, i4b_rbch);
129
130 /*===========================================================================*
131  *                      DEVICE DRIVER ROUTINES
132  *===========================================================================*/
133
134 /*---------------------------------------------------------------------------*
135  *      interface attach routine
136  *---------------------------------------------------------------------------*/
137 static void
138 i4brbchattach(void *dummy)
139 {
140         int i;
141
142         printf("i4brbch: %d raw B channel access device(s) attached\n", NI4BRBCH);
143         
144         for(i=0; i < NI4BRBCH; i++)
145         {
146                 make_dev(&i4brbch_cdevsw, i,
147                         UID_ROOT, GID_WHEEL, 0600, "i4brbch%d", i);
148
149 #if I4BRBCHACCT
150                 callout_handle_init(&rbch_softc[i].sc_callout);
151                 rbch_softc[i].sc_fn = 1;
152 #endif
153                 rbch_softc[i].sc_unit = i;
154                 rbch_softc[i].sc_devstate = ST_IDLE;
155                 rbch_softc[i].sc_hdlcq.ifq_maxlen = I4BRBCHMAXQLEN;
156
157                 if(!mtx_initialized(&rbch_softc[i].sc_hdlcq.ifq_mtx))
158                         mtx_init(&rbch_softc[i].sc_hdlcq.ifq_mtx, "i4b_rbch", NULL, MTX_DEF);
159
160                 rbch_softc[i].it_in.c_ispeed = rbch_softc[i].it_in.c_ospeed = 64000;
161                 termioschars(&rbch_softc[i].it_in);
162                 rbch_init_linktab(i);
163         }
164 }
165
166 /*---------------------------------------------------------------------------*
167  *      open rbch device
168  *---------------------------------------------------------------------------*/
169 static int
170 i4brbchopen(struct cdev *dev, int flag, int fmt, struct thread *td)
171 {
172         int unit = minor(dev);
173         
174         if(unit >= NI4BRBCH)
175                 return(ENXIO);
176
177         if(rbch_softc[unit].sc_devstate & ST_ISOPEN)
178                 return(EBUSY);
179
180 #if 0
181         rbch_clrq(unit);
182 #endif
183         
184         rbch_softc[unit].sc_devstate |= ST_ISOPEN;              
185
186         NDBGL4(L4_RBCHDBG, "unit %d, open", unit);      
187
188         return(0);
189 }
190
191 /*---------------------------------------------------------------------------*
192  *      close rbch device
193  *---------------------------------------------------------------------------*/
194 static int
195 i4brbchclose(struct cdev *dev, int flag, int fmt, struct thread *td)
196 {
197         int unit = minor(dev);
198         struct rbch_softc *sc = &rbch_softc[unit];
199         
200         if(sc->sc_devstate & ST_CONNECTED)
201                 i4b_l4_drvrdisc(BDRV_RBCH, unit);
202
203         sc->sc_devstate &= ~ST_ISOPEN;          
204
205         rbch_clrq(unit);
206         
207         NDBGL4(L4_RBCHDBG, "unit %d, closed", unit);
208         
209         return(0);
210 }
211
212 /*---------------------------------------------------------------------------*
213  *      read from rbch device
214  *---------------------------------------------------------------------------*/
215 static int
216 i4brbchread(struct cdev *dev, struct uio *uio, int ioflag)
217 {
218         struct mbuf *m;
219         int error = 0;
220         int unit = minor(dev);
221         struct ifqueue *iqp;
222         struct rbch_softc *sc = &rbch_softc[unit];
223
224         CRIT_VAR;
225         
226         NDBGL4(L4_RBCHDBG, "unit %d, enter read", unit);
227         
228         CRIT_BEG;
229         if(!(sc->sc_devstate & ST_ISOPEN))
230         {
231                 CRIT_END;
232                 NDBGL4(L4_RBCHDBG, "unit %d, read while not open", unit);
233                 return(EIO);
234         }
235
236         if((sc->sc_devstate & ST_NOBLOCK))
237         {
238                 if(!(sc->sc_devstate & ST_CONNECTED)) {
239                         CRIT_END;
240                         return(EWOULDBLOCK);
241                 }
242
243                 if(sc->sc_bprot == BPROT_RHDLC)
244                         iqp = &sc->sc_hdlcq;
245                 else
246                         iqp = isdn_linktab[unit]->rx_queue;     
247
248                 if(IF_QEMPTY(iqp) && (sc->sc_devstate & ST_ISOPEN)) {
249                         CRIT_END;
250                         return(EWOULDBLOCK);
251         }
252         }
253         else
254         {
255                 while(!(sc->sc_devstate & ST_CONNECTED))
256                 {
257                         NDBGL4(L4_RBCHDBG, "unit %d, wait read init", unit);
258                 
259                         if((error = tsleep( &rbch_softc[unit],
260                                            I4BPRI | PCATCH,
261                                            "rrrbch", 0 )) != 0)
262                         {
263                                 CRIT_END;
264                                 NDBGL4(L4_RBCHDBG, "unit %d, error %d tsleep", unit, error);
265                                 return(error);
266                         }
267                 }
268
269                 if(sc->sc_bprot == BPROT_RHDLC)
270                         iqp = &sc->sc_hdlcq;
271                 else
272                         iqp = isdn_linktab[unit]->rx_queue;     
273
274                 while(IF_QEMPTY(iqp) && (sc->sc_devstate & ST_ISOPEN))
275                 {
276                         sc->sc_devstate |= ST_RDWAITDATA;
277                 
278                         NDBGL4(L4_RBCHDBG, "unit %d, wait read data", unit);
279                 
280                         if((error = tsleep( &isdn_linktab[unit]->rx_queue,
281                                            I4BPRI | PCATCH,
282                                            "rrbch", 0 )) != 0)
283                         {
284                                 CRIT_END;
285                                 NDBGL4(L4_RBCHDBG, "unit %d, error %d tsleep read", unit, error);
286                                 sc->sc_devstate &= ~ST_RDWAITDATA;
287                                 return(error);
288                         }
289                 }
290         }
291
292         IF_DEQUEUE(iqp, m);
293
294         NDBGL4(L4_RBCHDBG, "unit %d, read %d bytes", unit, m->m_len);
295         
296         if(m && m->m_len)
297         {
298                 error = uiomove(m->m_data, m->m_len, uio);
299         }
300         else
301         {
302                 NDBGL4(L4_RBCHDBG, "unit %d, error %d uiomove", unit, error);
303                 error = EIO;
304         }
305                 
306         if(m)
307                 i4b_Bfreembuf(m);
308
309         CRIT_END;
310
311         return(error);
312 }
313
314 /*---------------------------------------------------------------------------*
315  *      write to rbch device
316  *---------------------------------------------------------------------------*/
317 static int
318 i4brbchwrite(struct cdev *dev, struct uio * uio, int ioflag)
319 {
320         struct mbuf *m;
321         int error = 0;
322         int unit = minor(dev);
323         struct rbch_softc *sc = &rbch_softc[unit];
324
325         CRIT_VAR;
326         
327         NDBGL4(L4_RBCHDBG, "unit %d, write", unit);     
328
329         CRIT_BEG;
330         if(!(sc->sc_devstate & ST_ISOPEN))
331         {
332                 NDBGL4(L4_RBCHDBG, "unit %d, write while not open", unit);
333                 CRIT_END;
334                 return(EIO);
335         }
336
337         if((sc->sc_devstate & ST_NOBLOCK))
338         {
339                 if(!(sc->sc_devstate & ST_CONNECTED)) {
340                         CRIT_END;
341                         return(EWOULDBLOCK);
342                 }
343                 if(_IF_QFULL(isdn_linktab[unit]->tx_queue) && (sc->sc_devstate & ST_ISOPEN)) {
344                         CRIT_END;
345                         return(EWOULDBLOCK);
346         }
347         }
348         else
349         {
350                 while(!(sc->sc_devstate & ST_CONNECTED))
351                 {
352                         NDBGL4(L4_RBCHDBG, "unit %d, write wait init", unit);
353                 
354                         error = tsleep( &rbch_softc[unit],
355                                                    I4BPRI | PCATCH,
356                                                    "wrrbch", 0 );
357                         if(error == ERESTART) {
358                                 CRIT_END;
359                                 return (ERESTART);
360                         }
361                         else if(error == EINTR)
362                         {
363                                 CRIT_END;
364                                 NDBGL4(L4_RBCHDBG, "unit %d, EINTR during wait init", unit);
365                                 return(EINTR);
366                         }
367                         else if(error)
368                         {
369                                 CRIT_END;
370                                 NDBGL4(L4_RBCHDBG, "unit %d, error %d tsleep init", unit, error);
371                                 return(error);
372                         }
373                         tsleep( &rbch_softc[unit], I4BPRI | PCATCH, "xrbch", (hz*1));
374                 }
375
376                 while(_IF_QFULL(isdn_linktab[unit]->tx_queue) && (sc->sc_devstate & ST_ISOPEN))
377                 {
378                         sc->sc_devstate |= ST_WRWAITEMPTY;
379
380                         NDBGL4(L4_RBCHDBG, "unit %d, write queue full", unit);
381                 
382                         if ((error = tsleep( &isdn_linktab[unit]->tx_queue,
383                                             I4BPRI | PCATCH,
384                                             "wrbch", 0)) != 0) {
385                                 sc->sc_devstate &= ~ST_WRWAITEMPTY;
386                                 if(error == ERESTART)
387                                 {
388                                         CRIT_END;
389                                         return(ERESTART);
390                                 }
391                                 else if(error == EINTR)
392                                 {
393                                         CRIT_END;
394                                         NDBGL4(L4_RBCHDBG, "unit %d, EINTR during wait write", unit);
395                                         return(error);
396                                 }
397                                 else if(error)
398                                 {
399                                         CRIT_END;
400                                         NDBGL4(L4_RBCHDBG, "unit %d, error %d tsleep write", unit, error);
401                                         return(error);
402                                 }
403                         }
404                 }
405         }
406
407         if(!(sc->sc_devstate & ST_ISOPEN))
408         {
409                 NDBGL4(L4_RBCHDBG, "unit %d, not open anymore", unit);
410                 CRIT_END;
411                 return(EIO);
412         }
413
414         if((m = i4b_Bgetmbuf(BCH_MAX_DATALEN)) != NULL)
415         {
416                 m->m_len = min(BCH_MAX_DATALEN, uio->uio_resid);
417
418                 NDBGL4(L4_RBCHDBG, "unit %d, write %d bytes", unit, m->m_len);
419                 
420                 error = uiomove(m->m_data, m->m_len, uio);
421
422                 (void) IF_HANDOFF(isdn_linktab[unit]->tx_queue, m, NULL);
423
424                 (*isdn_linktab[unit]->bch_tx_start)(isdn_linktab[unit]->unit, isdn_linktab[unit]->channel);
425         }
426
427         CRIT_END;
428         
429         return(error);
430 }
431
432 /*---------------------------------------------------------------------------*
433  *      rbch device ioctl handlibg
434  *---------------------------------------------------------------------------*/
435 static int
436 i4brbchioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, struct thread *td)
437 {
438         int error = 0;
439         int unit = minor(dev);
440         struct rbch_softc *sc = &rbch_softc[unit];
441         
442         switch(cmd)
443         {
444                 case FIOASYNC:  /* Set async mode */
445                         if (*(int *)data)
446                         {
447                                 NDBGL4(L4_RBCHDBG, "unit %d, setting async mode", unit);
448                         }
449                         else
450                         {
451                                 NDBGL4(L4_RBCHDBG, "unit %d, clearing async mode", unit);
452                         }
453                         break;
454
455                 case FIONBIO:
456                         if (*(int *)data)
457                         {
458                                 NDBGL4(L4_RBCHDBG, "unit %d, setting non-blocking mode", unit);
459                                 sc->sc_devstate |= ST_NOBLOCK;
460                         }
461                         else
462                         {
463                                 NDBGL4(L4_RBCHDBG, "unit %d, clearing non-blocking mode", unit);
464                                 sc->sc_devstate &= ~ST_NOBLOCK;
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 *)data)[l]; l++)
481                                 ;
482                         if (l)
483                         {
484                                 NDBGL4(L4_RBCHDBG, "unit %d, attempting dialout to %s", unit, (char *)data);
485                                 i4b_l4_dialoutnumber(BDRV_RBCH, unit, l, (char *)data);
486                                 break;
487                         }
488                         /* FALLTHROUGH 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 *)data = sc->it_in;
501                         break;
502
503                 case TIOCMGET:
504                         *(int *)data = TIOCM_LE|TIOCM_DTR|TIOCM_RTS|TIOCM_CTS|TIOCM_DSR;
505                         if (sc->sc_devstate & ST_CONNECTED)
506                                 *(int *)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 *)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, (u_long)cmd);
523                         error = EINVAL;
524                         break;
525         }
526         return(error);
527 }
528
529 /*---------------------------------------------------------------------------*
530  *      device driver poll
531  *---------------------------------------------------------------------------*/
532 static int
533 i4brbchpoll(struct cdev *dev, int events, struct thread *td)
534 {
535         int revents = 0;        /* Events we found */
536         int s;
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
542         s = splhigh();
543
544         if(!(sc->sc_devstate & ST_ISOPEN))
545         {
546                 splx(s);
547                 return(POLLNVAL);
548         }
549
550         /*
551          * Writes are OK if we are connected and the
552          * transmit queue can take them
553          */
554          
555         if((events & (POLLOUT|POLLWRNORM)) &&
556            (sc->sc_devstate & ST_CONNECTED) &&
557            !_IF_QFULL(isdn_linktab[unit]->tx_queue))
558         {
559                 revents |= (events & (POLLOUT|POLLWRNORM));
560         }
561         
562         /* ... while reads are OK if we have any data */
563
564         if((events & (POLLIN|POLLRDNORM)) &&
565            (sc->sc_devstate & ST_CONNECTED))
566         {
567                 struct ifqueue *iqp;
568
569                 if(sc->sc_bprot == BPROT_RHDLC)
570                         iqp = &sc->sc_hdlcq;
571                 else
572                         iqp = isdn_linktab[unit]->rx_queue;     
573
574                 if(!IF_QEMPTY(iqp))
575                         revents |= (events & (POLLIN|POLLRDNORM));
576         }
577                 
578         if(revents == 0)
579                 selrecord(td, &sc->selp);
580
581         splx(s);
582         return(revents);
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         START_TIMER(sc->sc_callout, rbch_timeout, sc, I4BRBCHACCTINTVL*hz);
620 }
621 #endif /* I4BRBCHACCT */
622
623 /*===========================================================================*
624  *                      ISDN INTERFACE ROUTINES
625  *===========================================================================*/
626
627 /*---------------------------------------------------------------------------*
628  *      this routine is called from L4 handler at connect time
629  *---------------------------------------------------------------------------*/
630 static void
631 rbch_connect(int unit, void *cdp)
632 {
633         call_desc_t *cd = (call_desc_t *)cdp;
634         struct rbch_softc *sc = &rbch_softc[unit];
635
636         sc->sc_bprot = cd->bprot;
637
638 #if I4BRBCHACCT
639         if(sc->sc_bprot == BPROT_RHDLC)
640         {       
641                 sc->sc_iinb = 0;
642                 sc->sc_ioutb = 0;
643                 sc->sc_linb = 0;
644                 sc->sc_loutb = 0;
645
646                 START_TIMER(sc->sc_callout, rbch_timeout, sc, I4BRBCHACCTINTVL*hz);
647         }
648 #endif          
649         if(!(sc->sc_devstate & ST_CONNECTED))
650         {
651                 NDBGL4(L4_RBCHDBG, "unit %d, wakeup", unit);
652                 sc->sc_devstate |= ST_CONNECTED;
653                 sc->sc_cd = cdp;
654                 wakeup(sc);
655         }
656 }
657
658 /*---------------------------------------------------------------------------*
659  *      this routine is called from L4 handler at disconnect time
660  *---------------------------------------------------------------------------*/
661 static void
662 rbch_disconnect(int unit, void *cdp)
663 {
664         call_desc_t *cd = (call_desc_t *)cdp;
665         struct rbch_softc *sc = &rbch_softc[unit];
666
667         CRIT_VAR;
668         
669         if(cd != sc->sc_cd)
670         {
671                 NDBGL4(L4_RBCHDBG, "rbch%d: channel %d not active",
672                         cd->driver_unit, cd->channelid);
673                 return;
674         }
675
676         CRIT_BEG;
677         
678         NDBGL4(L4_RBCHDBG, "unit %d, disconnect", unit);
679
680         sc->sc_devstate &= ~ST_CONNECTED;
681
682         sc->sc_cd = NULL;
683         
684 #if I4BRBCHACCT
685         i4b_l4_accounting(BDRV_RBCH, unit, ACCT_FINAL,
686                  sc->sc_ioutb, sc->sc_iinb, 0, 0, sc->sc_ioutb, sc->sc_iinb);
687
688         STOP_TIMER(sc->sc_callout, rbch_timeout, sc);
689 #endif          
690         CRIT_END;
691 }
692         
693 /*---------------------------------------------------------------------------*
694  *      feedback from daemon in case of dial problems
695  *---------------------------------------------------------------------------*/
696 static void
697 rbch_dialresponse(int unit, int status, cause_t cause)
698 {
699 }
700         
701 /*---------------------------------------------------------------------------*
702  *      interface up/down
703  *---------------------------------------------------------------------------*/
704 static void
705 rbch_updown(int unit, int updown)
706 {
707 }
708         
709 /*---------------------------------------------------------------------------*
710  *      this routine is called from the HSCX interrupt handler
711  *      when a new frame (mbuf) has been received and is to be put on
712  *      the rx queue.
713  *---------------------------------------------------------------------------*/
714 static void
715 rbch_rx_data_rdy(int unit)
716 {
717         if(rbch_softc[unit].sc_bprot == BPROT_RHDLC)
718         {
719                 register struct mbuf *m;
720                 
721                 if((m = *isdn_linktab[unit]->rx_mbuf) == NULL)
722                         return;
723
724                 m->m_pkthdr.len = m->m_len;
725
726                 if (! IF_HANDOFF(&(rbch_softc[unit].sc_hdlcq), m, NULL))
727                 {
728                         NDBGL4(L4_RBCHDBG, "unit %d: hdlc rx queue full!", unit);
729                 }
730         }
731
732         if(rbch_softc[unit].sc_devstate & ST_RDWAITDATA)
733         {
734                 NDBGL4(L4_RBCHDBG, "unit %d, wakeup", unit);
735                 rbch_softc[unit].sc_devstate &= ~ST_RDWAITDATA;
736                 wakeup( &isdn_linktab[unit]->rx_queue);
737         }
738         else
739         {
740                 NDBGL4(L4_RBCHDBG, "unit %d, NO wakeup", unit);
741         }
742         selwakeuppri(&rbch_softc[unit].selp, I4BPRI);
743 }
744
745 /*---------------------------------------------------------------------------*
746  *      this routine is called from the HSCX interrupt handler
747  *      when the last frame has been sent out and there is no
748  *      further frame (mbuf) in the tx queue.
749  *---------------------------------------------------------------------------*/
750 static void
751 rbch_tx_queue_empty(int unit)
752 {
753         if(rbch_softc[unit].sc_devstate & ST_WRWAITEMPTY)
754         {
755                 NDBGL4(L4_RBCHDBG, "unit %d, wakeup", unit);
756                 rbch_softc[unit].sc_devstate &= ~ST_WRWAITEMPTY;
757                 wakeup( &isdn_linktab[unit]->tx_queue);
758         }
759         else
760         {
761                 NDBGL4(L4_RBCHDBG, "unit %d, NO wakeup", unit);
762         }
763         selwakeuppri(&rbch_softc[unit].selp, TTOPRI);
764 }
765
766 /*---------------------------------------------------------------------------*
767  *      this routine is called from the HSCX interrupt handler
768  *      each time a packet is received or transmitted
769  *---------------------------------------------------------------------------*/
770 static void
771 rbch_activity(int unit, int rxtx)
772 {
773         if (rbch_softc[unit].sc_cd)
774                 rbch_softc[unit].sc_cd->last_active_time = SECOND;
775         selwakeuppri(&rbch_softc[unit].selp, I4BPRI);
776 }
777
778 /*---------------------------------------------------------------------------*
779  *      clear an hdlc rx queue for a rbch unit
780  *---------------------------------------------------------------------------*/
781 static void
782 rbch_clrq(int unit)
783 {
784         CRIT_VAR;
785
786         CRIT_BEG;
787         IF_DRAIN(&rbch_softc[unit].sc_hdlcq);
788         CRIT_END;
789 }
790                                 
791 /*---------------------------------------------------------------------------*
792  *      return this drivers linktab address
793  *---------------------------------------------------------------------------*/
794 drvr_link_t *
795 rbch_ret_linktab(int unit)
796 {
797         rbch_init_linktab(unit);
798         return(&rbch_drvr_linktab[unit]);
799 }
800
801 /*---------------------------------------------------------------------------*
802  *      setup the isdn_linktab for this driver
803  *---------------------------------------------------------------------------*/
804 void
805 rbch_set_linktab(int unit, isdn_link_t *ilt)
806 {
807         isdn_linktab[unit] = ilt;
808 }
809
810 /*---------------------------------------------------------------------------*
811  *      initialize this drivers linktab
812  *---------------------------------------------------------------------------*/
813 static void
814 rbch_init_linktab(int unit)
815 {
816         rbch_drvr_linktab[unit].unit = unit;
817         rbch_drvr_linktab[unit].bch_rx_data_ready = rbch_rx_data_rdy;
818         rbch_drvr_linktab[unit].bch_tx_queue_empty = rbch_tx_queue_empty;
819         rbch_drvr_linktab[unit].bch_activity = rbch_activity;   
820         rbch_drvr_linktab[unit].line_connected = rbch_connect;
821         rbch_drvr_linktab[unit].line_disconnected = rbch_disconnect;
822         rbch_drvr_linktab[unit].dial_response = rbch_dialresponse;
823         rbch_drvr_linktab[unit].updown_ind = rbch_updown;       
824 }
825
826 /*===========================================================================*/