Merge from vendor branch NTPD:
[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.12 2004/09/16 04:36:30 dillon 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
52 #if defined(__NetBSD__) && __NetBSD_Version__ >= 104230000
53 #include <sys/callout.h>
54 #endif
55
56 #if defined (__NetBSD__) || defined (__OpenBSD__)
57 extern cc_t ttydefchars;
58 #define termioschars(t) memcpy((t)->c_cc, &ttydefchars, sizeof((t)->c_cc))
59 #endif
60
61 #if defined(__DragonFly__) || defined(__FreeBSD__)
62
63 #ifdef DEVFS
64 #include <sys/devfsext.h>
65 #endif
66
67 #endif /* __FreeBSD__ */
68
69 #ifdef __NetBSD__
70 #include <sys/filio.h>
71 #endif
72
73 #if defined(__DragonFly__) || defined(__FreeBSD__)
74 #include <net/i4b/include/machine/i4b_ioctl.h>
75 #include <net/i4b/include/machine/i4b_rbch_ioctl.h>
76 #include <net/i4b/include/machine/i4b_debug.h>
77 #else
78 #include <i4b/i4b_ioctl.h>
79 #include <i4b/i4b_rbch_ioctl.h>
80 #include <i4b/i4b_debug.h>
81 #endif
82
83 #include "../include/i4b_global.h"
84 #include "../include/i4b_mbuf.h"
85 #include "../include/i4b_l3l4.h"
86 #include "../layer4/i4b_l4.h"
87
88 #ifdef __bsdi__
89 #include <sys/device.h>
90 #endif
91
92 #ifdef OS_USES_POLL
93 #include <sys/ioccom.h>
94 #include <sys/poll.h>
95 #else
96 #include <sys/fcntl.h>
97 #include <sys/ioctl.h>
98 #endif
99
100 #if defined(__DragonFly__) || defined(__FreeBSD__)
101 #include <sys/filio.h>
102 #endif
103
104 static drvr_link_t rbch_drvr_linktab[NI4BRBCH];
105 static isdn_link_t *isdn_linktab[NI4BRBCH];
106
107 #define I4BRBCHACCT             1       /* enable accounting messages */
108 #define I4BRBCHACCTINTVL        2       /* accounting msg interval in secs */
109
110 static struct rbch_softc {
111
112         int sc_unit;                    /* unit number          */
113
114         int sc_devstate;                /* state of driver      */
115 #define ST_IDLE         0x00
116 #define ST_CONNECTED    0x01
117 #define ST_ISOPEN       0x02
118 #define ST_RDWAITDATA   0x04
119 #define ST_WRWAITEMPTY  0x08
120 #define ST_NOBLOCK      0x10
121
122         int sc_bprot;                   /* B-ch protocol used   */
123
124         call_desc_t *sc_cd;             /* Call Descriptor */
125
126         struct termios it_in;
127
128         struct ifqueue sc_hdlcq;        /* hdlc read queue      */
129 #define I4BRBCHMAXQLEN  10
130
131         struct selinfo selp;            /* select / poll        */
132
133 #if I4BRBCHACCT
134         struct callout  sc_timeout;
135
136         int             sc_iinb;        /* isdn driver # of inbytes     */
137         int             sc_ioutb;       /* isdn driver # of outbytes    */
138         int             sc_linb;        /* last # of bytes rx'd         */
139         int             sc_loutb;       /* last # of bytes tx'd         */
140         int             sc_fn;          /* flag, first null acct        */
141 #endif  
142 } rbch_softc[NI4BRBCH];
143
144 static void rbch_rx_data_rdy(int unit);
145 static void rbch_tx_queue_empty(int unit);
146 static void rbch_connect(int unit, void *cdp);
147 static void rbch_disconnect(int unit, void *cdp);
148 static void rbch_init_linktab(int unit);
149 static void rbch_clrq(int unit);
150
151 #if !defined(__DragonFly__) && !defined(__FreeBSD__)
152 #define PDEVSTATIC      /* - not static - */
153 #define IOCTL_CMD_T     u_long
154 void i4brbchattach (void);
155 int i4brbchopen (dev_t dev, int flag, int fmt, struct proc *p);
156 int i4brbchclose (dev_t dev, int flag, int fmt, struct proc *p);
157 int i4brbchread (dev_t dev, struct uio *uio, int ioflag);
158 int i4brbchwrite (dev_t dev, struct uio *uio, int ioflag);
159 int i4brbchioctl (dev_t dev, IOCTL_CMD_T cmd, caddr_t arg, int flag, struct proc* pr);
160 #ifdef OS_USES_POLL
161 int i4brbchpoll (dev_t dev, int events, struct proc *p);
162 #else
163 PDEVSTATIC int i4brbchselect (dev_t dev, int rw, struct proc *p);
164 #endif
165 #endif
166
167 #if defined(__DragonFly__) || (BSD > 199306 && defined(__FreeBSD__))
168 #define PDEVSTATIC      static
169 #define IOCTL_CMD_T     u_long
170
171 PDEVSTATIC d_open_t i4brbchopen;
172 PDEVSTATIC d_close_t i4brbchclose;
173 PDEVSTATIC d_read_t i4brbchread;
174 PDEVSTATIC d_read_t i4brbchwrite;
175 PDEVSTATIC d_ioctl_t i4brbchioctl;
176
177 #ifdef OS_USES_POLL
178 PDEVSTATIC d_poll_t i4brbchpoll;
179 #define POLLFIELD       i4brbchpoll
180 #else
181 PDEVSTATIC d_select_t i4brbchselect;
182 #define POLLFIELD       i4brbchselect
183 #endif
184
185 #define CDEV_MAJOR 57
186
187 static struct cdevsw i4brbch_cdevsw = {
188         /* name */      "i4brbch",
189         /* maj */       CDEV_MAJOR,
190         /* flags */     0,
191         /* port */      NULL,
192         /* clone */     NULL,
193
194         /* open */      i4brbchopen,
195         /* close */     i4brbchclose,
196         /* read */      i4brbchread,
197         /* write */     i4brbchwrite,
198         /* ioctl */     i4brbchioctl,
199         /* poll */      POLLFIELD,
200         /* mmap */      nommap,
201         /* strategy */  nostrategy,
202         /* dump */      nodump,
203         /* psize */     nopsize
204 };
205
206 static void i4brbchattach(void *);
207 PSEUDO_SET(i4brbchattach, i4b_rbch);
208
209 /*===========================================================================*
210  *                      DEVICE DRIVER ROUTINES
211  *===========================================================================*/
212
213 /*---------------------------------------------------------------------------*
214  *      initialization at kernel load time
215  *---------------------------------------------------------------------------*/
216 static void
217 i4brbchinit(void *unused)
218 {
219         cdevsw_add(&i4brbch_cdevsw, 0, 0);
220 }
221
222 SYSINIT(i4brbchdev, SI_SUB_DRIVERS,
223         SI_ORDER_MIDDLE+CDEV_MAJOR, &i4brbchinit, NULL);
224
225 #endif /* BSD > 199306 && defined(__FreeBSD__) */
226
227 #ifdef __bsdi__
228 int i4brbchmatch(struct device *parent, struct cfdata *cf, void *aux);
229 void dummy_i4brbchattach(struct device*, struct device *, void *);
230
231 #define CDEV_MAJOR 61
232
233 static struct cfdriver i4brbchcd =
234         { NULL, "i4brbch", i4brbchmatch, dummy_i4brbchattach, DV_DULL,
235           sizeof(struct cfdriver) };
236 struct devsw i4brbchsw = 
237         { &i4brbchcd,
238           i4brbchopen,  i4brbchclose,   i4brbchread,    i4brbchwrite,
239           i4brbchioctl, seltrue,        nommap,         nostrat,
240           nodump,       nopsize,        0,              nostop
241 };
242
243 int
244 i4brbchmatch(struct device *parent, struct cfdata *cf, void *aux)
245 {
246         printf("i4brbchmatch: aux=0x%x\n", aux);
247         return 1;
248 }
249 void
250 dummy_i4brbchattach(struct device *parent, struct device *self, void *aux)
251 {
252         printf("dummy_i4brbchattach: aux=0x%x\n", aux);
253 }
254 #endif /* __bsdi__ */
255
256 /*---------------------------------------------------------------------------*
257  *      interface attach routine
258  *---------------------------------------------------------------------------*/
259 PDEVSTATIC void
260 #if defined(__DragonFly__) || defined(__FreeBSD__)
261 i4brbchattach(void *dummy)
262 #else
263 i4brbchattach()
264 #endif
265 {
266         int i;
267
268 #ifndef HACK_NO_PSEUDO_ATTACH_MSG
269         printf("i4brbch: %d raw B channel access device(s) attached\n", NI4BRBCH);
270 #endif
271         
272         for(i=0; i < NI4BRBCH; i++)
273         {
274 #if defined(__DragonFly__) || defined(__FreeBSD__)
275                 make_dev(&i4brbch_cdevsw, i,
276                         UID_ROOT, GID_WHEEL, 0600, "i4brbch%d", i);
277 #endif
278
279 #if I4BRBCHACCT
280                 callout_init(&rbch_softc[i].sc_timeout);
281                 rbch_softc[i].sc_fn = 1;
282 #endif
283                 rbch_softc[i].sc_unit = i;
284                 rbch_softc[i].sc_devstate = ST_IDLE;
285                 rbch_softc[i].sc_hdlcq.ifq_maxlen = I4BRBCHMAXQLEN;
286 #if defined(__FreeBSD__) && __FreeBSD__ > 4
287                 mtx_init(&rbch_softc[i].sc_hdlcq.ifq_mtx, "i4b_rbch", MTX_DEF);
288 #endif          
289                 rbch_softc[i].it_in.c_ispeed = rbch_softc[i].it_in.c_ospeed = 64000;
290                 termioschars(&rbch_softc[i].it_in);
291                 rbch_init_linktab(i);
292         }
293 }
294
295 /*---------------------------------------------------------------------------*
296  *      open rbch device
297  *---------------------------------------------------------------------------*/
298 PDEVSTATIC int
299 i4brbchopen(dev_t dev, int flag, int fmt, struct thread *td)
300 {
301         int unit = minor(dev);
302         
303         if(unit >= NI4BRBCH)
304                 return(ENXIO);
305
306         if(rbch_softc[unit].sc_devstate & ST_ISOPEN)
307                 return(EBUSY);
308
309 #if 0
310         rbch_clrq(unit);
311 #endif
312         
313         rbch_softc[unit].sc_devstate |= ST_ISOPEN;              
314
315         NDBGL4(L4_RBCHDBG, "unit %d, open", unit);      
316
317         return(0);
318 }
319
320 /*---------------------------------------------------------------------------*
321  *      close rbch device
322  *---------------------------------------------------------------------------*/
323 PDEVSTATIC int
324 i4brbchclose(dev_t dev, int flag, int fmt, struct thread *td)
325 {
326         int unit = minor(dev);
327         struct rbch_softc *sc = &rbch_softc[unit];
328         
329         if(sc->sc_devstate & ST_CONNECTED)
330                 i4b_l4_drvrdisc(BDRV_RBCH, unit);
331
332         sc->sc_devstate &= ~ST_ISOPEN;          
333
334         rbch_clrq(unit);
335         
336         NDBGL4(L4_RBCHDBG, "unit %d, closed", unit);
337         
338         return(0);
339 }
340
341 /*---------------------------------------------------------------------------*
342  *      read from rbch device
343  *---------------------------------------------------------------------------*/
344 PDEVSTATIC int
345 i4brbchread(dev_t dev, struct uio *uio, int ioflag)
346 {
347         struct mbuf *m;
348         int error = 0;
349         int unit = minor(dev);
350         struct ifqueue *iqp;
351         struct rbch_softc *sc = &rbch_softc[unit];
352
353         CRIT_VAR;
354         
355         NDBGL4(L4_RBCHDBG, "unit %d, enter read", unit);
356         
357         CRIT_BEG;
358         if(!(sc->sc_devstate & ST_ISOPEN))
359         {
360                 CRIT_END;
361                 NDBGL4(L4_RBCHDBG, "unit %d, read while not open", unit);
362                 return(EIO);
363         }
364
365         if((sc->sc_devstate & ST_NOBLOCK))
366         {
367                 if(!(sc->sc_devstate & ST_CONNECTED)) {
368                         CRIT_END;
369                         return(EWOULDBLOCK);
370                 }
371
372                 if(sc->sc_bprot == BPROT_RHDLC)
373                         iqp = &sc->sc_hdlcq;
374                 else
375                         iqp = isdn_linktab[unit]->rx_queue;     
376
377                 if(IF_QEMPTY(iqp) && (sc->sc_devstate & ST_ISOPEN)) {
378                         CRIT_END;
379                         return(EWOULDBLOCK);
380         }
381         }
382         else
383         {
384                 while(!(sc->sc_devstate & ST_CONNECTED))
385                 {
386                         NDBGL4(L4_RBCHDBG, "unit %d, wait read init", unit);
387                 
388                         if((error = tsleep((caddr_t) &rbch_softc[unit],
389                                                PCATCH, "rrrbch", 0 )) != 0)
390                         {
391                                 CRIT_END;
392                                 NDBGL4(L4_RBCHDBG, "unit %d, error %d tsleep", unit, error);
393                                 return(error);
394                         }
395                 }
396
397                 if(sc->sc_bprot == BPROT_RHDLC)
398                         iqp = &sc->sc_hdlcq;
399                 else
400                         iqp = isdn_linktab[unit]->rx_queue;     
401
402                 while(IF_QEMPTY(iqp) && (sc->sc_devstate & ST_ISOPEN))
403                 {
404                         sc->sc_devstate |= ST_RDWAITDATA;
405                 
406                         NDBGL4(L4_RBCHDBG, "unit %d, wait read data", unit);
407                 
408                         if((error = tsleep((caddr_t) &isdn_linktab[unit]->rx_queue,
409                                            PCATCH, "rrbch", 0 )) != 0)
410                         {
411                                 CRIT_END;
412                                 NDBGL4(L4_RBCHDBG, "unit %d, error %d tsleep read", unit, error);
413                                 sc->sc_devstate &= ~ST_RDWAITDATA;
414                                 return(error);
415                         }
416                 }
417         }
418
419         IF_DEQUEUE(iqp, m);
420
421         NDBGL4(L4_RBCHDBG, "unit %d, read %d bytes", unit, m->m_len);
422         
423         if(m && m->m_len)
424         {
425                 error = uiomove(m->m_data, m->m_len, uio);
426         }
427         else
428         {
429                 NDBGL4(L4_RBCHDBG, "unit %d, error %d uiomove", unit, error);
430                 error = EIO;
431         }
432                 
433         if(m)
434                 i4b_Bfreembuf(m);
435
436         CRIT_END;
437
438         return(error);
439 }
440
441 /*---------------------------------------------------------------------------*
442  *      write to rbch device
443  *---------------------------------------------------------------------------*/
444 PDEVSTATIC int
445 i4brbchwrite(dev_t dev, struct uio * uio, int ioflag)
446 {
447         struct mbuf *m;
448         int error = 0;
449         int unit = minor(dev);
450         struct rbch_softc *sc = &rbch_softc[unit];
451
452         CRIT_VAR;
453         
454         NDBGL4(L4_RBCHDBG, "unit %d, write", unit);     
455
456         CRIT_BEG;
457         if(!(sc->sc_devstate & ST_ISOPEN))
458         {
459                 NDBGL4(L4_RBCHDBG, "unit %d, write while not open", unit);
460                 CRIT_END;
461                 return(EIO);
462         }
463
464         if((sc->sc_devstate & ST_NOBLOCK))
465         {
466                 if(!(sc->sc_devstate & ST_CONNECTED)) {
467                         CRIT_END;
468                         return(EWOULDBLOCK);
469                 }
470                 if(_IF_QFULL(isdn_linktab[unit]->tx_queue) && (sc->sc_devstate & ST_ISOPEN)) {
471                         CRIT_END;
472                         return(EWOULDBLOCK);
473         }
474         }
475         else
476         {
477                 while(!(sc->sc_devstate & ST_CONNECTED))
478                 {
479                         NDBGL4(L4_RBCHDBG, "unit %d, write wait init", unit);
480                 
481                         error = tsleep((caddr_t) &rbch_softc[unit],
482                                                    PCATCH, "wrrbch", 0 );
483                         if(error == ERESTART) {
484                                 CRIT_END;
485                                 return (ERESTART);
486                         }
487                         else if(error == EINTR)
488                         {
489                                 CRIT_END;
490                                 NDBGL4(L4_RBCHDBG, "unit %d, EINTR during wait init", unit);
491                                 return(EINTR);
492                         }
493                         else if(error)
494                         {
495                                 CRIT_END;
496                                 NDBGL4(L4_RBCHDBG, "unit %d, error %d tsleep init", unit, error);
497                                 return(error);
498                         }
499                         tsleep((caddr_t) &rbch_softc[unit], PCATCH, "xrbch", (hz*1));
500                 }
501
502                 while(_IF_QFULL(isdn_linktab[unit]->tx_queue) && (sc->sc_devstate & ST_ISOPEN))
503                 {
504                         sc->sc_devstate |= ST_WRWAITEMPTY;
505
506                         NDBGL4(L4_RBCHDBG, "unit %d, write queue full", unit);
507                 
508                         if ((error = tsleep((caddr_t) &isdn_linktab[unit]->tx_queue,
509                                             PCATCH, "wrbch", 0)) != 0) {
510                                 sc->sc_devstate &= ~ST_WRWAITEMPTY;
511                                 if(error == ERESTART)
512                                 {
513                                         CRIT_END;
514                                         return(ERESTART);
515                                 }
516                                 else if(error == EINTR)
517                                 {
518                                         CRIT_END;
519                                         NDBGL4(L4_RBCHDBG, "unit %d, EINTR during wait write", unit);
520                                         return(error);
521                                 }
522                                 else if(error)
523                                 {
524                                         CRIT_END;
525                                         NDBGL4(L4_RBCHDBG, "unit %d, error %d tsleep write", unit, error);
526                                         return(error);
527                                 }
528                         }
529                 }
530         }
531
532         if(!(sc->sc_devstate & ST_ISOPEN))
533         {
534                 NDBGL4(L4_RBCHDBG, "unit %d, not open anymore", unit);
535                 CRIT_END;
536                 return(EIO);
537         }
538
539         if((m = i4b_Bgetmbuf(BCH_MAX_DATALEN)) != NULL)
540         {
541                 m->m_len = min(BCH_MAX_DATALEN, uio->uio_resid);
542
543                 NDBGL4(L4_RBCHDBG, "unit %d, write %d bytes", unit, m->m_len);
544                 
545                 error = uiomove(m->m_data, m->m_len, uio);
546
547 #if defined (__FreeBSD__) && __FreeBSD__ > 4            
548                 (void) IF_HANDOFF(isdn_linktab[unit]->tx_queue, m, NULL);
549 #else
550                 if(IF_QFULL(isdn_linktab[unit]->tx_queue))
551                         m_freem(m);
552                 else
553                         IF_ENQUEUE(isdn_linktab[unit]->tx_queue, m);
554 #endif
555                 (*isdn_linktab[unit]->bch_tx_start)(isdn_linktab[unit]->unit, isdn_linktab[unit]->channel);
556         }
557
558         CRIT_END;
559         
560         return(error);
561 }
562
563 /*---------------------------------------------------------------------------*
564  *      rbch device ioctl handlibg
565  *---------------------------------------------------------------------------*/
566 PDEVSTATIC int
567 i4brbchioctl(dev_t dev, IOCTL_CMD_T cmd, caddr_t data, int flag, struct thread *td)
568 {
569         int error = 0;
570         int unit = minor(dev);
571         struct rbch_softc *sc = &rbch_softc[unit];
572         
573         switch(cmd)
574         {
575                 case FIOASYNC:  /* Set async mode */
576                         if (*(int *)data)
577                         {
578                                 NDBGL4(L4_RBCHDBG, "unit %d, setting async mode", unit);
579                         }
580                         else
581                         {
582                                 NDBGL4(L4_RBCHDBG, "unit %d, clearing async mode", unit);
583                         }
584                         break;
585
586                 case FIONBIO:
587                         if (*(int *)data)
588                         {
589                                 NDBGL4(L4_RBCHDBG, "unit %d, setting non-blocking mode", unit);
590                                 sc->sc_devstate |= ST_NOBLOCK;
591                         }
592                         else
593                         {
594                                 NDBGL4(L4_RBCHDBG, "unit %d, clearing non-blocking mode", unit);
595                                 sc->sc_devstate &= ~ST_NOBLOCK;
596                         }
597                         break;
598
599                 case TIOCCDTR:  /* Clear DTR */
600                         if(sc->sc_devstate & ST_CONNECTED)
601                         {
602                                 NDBGL4(L4_RBCHDBG, "unit %d, disconnecting for DTR down", unit);
603                                 i4b_l4_drvrdisc(BDRV_RBCH, unit);
604                         }
605                         break;
606
607                 case I4B_RBCH_DIALOUT:
608                 {
609                         size_t l;
610
611                         for (l = 0; l < TELNO_MAX && ((char *)data)[l]; l++)
612                                 ;
613                         if (l)
614                         {
615                                 NDBGL4(L4_RBCHDBG, "unit %d, attempting dialout to %s", unit, (char *)data);
616                                 i4b_l4_dialoutnumber(BDRV_RBCH, unit, l, (char *)data);
617                                 break;
618                         }
619                         /* fall through to SDTR */
620                 }
621
622                 case TIOCSDTR:  /* Set DTR */
623                         NDBGL4(L4_RBCHDBG, "unit %d, attempting dialout (DTR)", unit);
624                         i4b_l4_dialout(BDRV_RBCH, unit);
625                         break;
626
627                 case TIOCSETA:  /* Set termios struct */
628                         break;
629
630                 case TIOCGETA:  /* Get termios struct */
631                         *(struct termios *)data = sc->it_in;
632                         break;
633
634                 case TIOCMGET:
635                         *(int *)data = TIOCM_LE|TIOCM_DTR|TIOCM_RTS|TIOCM_CTS|TIOCM_DSR;
636                         if (sc->sc_devstate & ST_CONNECTED)
637                                 *(int *)data |= TIOCM_CD;
638                         break;
639
640                 case I4B_RBCH_VR_REQ:
641                 {
642                         msg_vr_req_t *mvr;
643
644                         mvr = (msg_vr_req_t *)data;
645
646                         mvr->version = VERSION;
647                         mvr->release = REL;
648                         mvr->step = STEP;                       
649                         break;
650                 }
651
652                 default:        /* Unknown stuff */
653                         NDBGL4(L4_RBCHDBG, "unit %d, ioctl, unknown cmd %lx", unit, (u_long)cmd);
654                         error = EINVAL;
655                         break;
656         }
657         return(error);
658 }
659
660 #ifdef OS_USES_POLL
661
662 /*---------------------------------------------------------------------------*
663  *      device driver poll
664  *---------------------------------------------------------------------------*/
665 PDEVSTATIC int
666 i4brbchpoll(dev_t dev, int events, struct thread *td)
667 {
668         int revents = 0;        /* Events we found */
669         int s;
670         int unit = minor(dev);
671         struct rbch_softc *sc = &rbch_softc[unit];
672         
673         /* We can't check for anything but IN or OUT */
674
675         s = splhigh();
676
677         if(!(sc->sc_devstate & ST_ISOPEN))
678         {
679                 splx(s);
680                 return(POLLNVAL);
681         }
682
683         /*
684          * Writes are OK if we are connected and the
685          * transmit queue can take them
686          */
687          
688         if((events & (POLLOUT|POLLWRNORM)) &&
689            (sc->sc_devstate & ST_CONNECTED) &&
690            !_IF_QFULL(isdn_linktab[unit]->tx_queue))
691         {
692                 revents |= (events & (POLLOUT|POLLWRNORM));
693         }
694         
695         /* ... while reads are OK if we have any data */
696
697         if((events & (POLLIN|POLLRDNORM)) &&
698            (sc->sc_devstate & ST_CONNECTED))
699         {
700                 struct ifqueue *iqp;
701
702                 if(sc->sc_bprot == BPROT_RHDLC)
703                         iqp = &sc->sc_hdlcq;
704                 else
705                         iqp = isdn_linktab[unit]->rx_queue;     
706
707                 if(!IF_QEMPTY(iqp))
708                         revents |= (events & (POLLIN|POLLRDNORM));
709         }
710                 
711         if(revents == 0)
712                 selrecord(td, &sc->selp);
713
714         splx(s);
715         return(revents);
716 }
717
718 #else /* OS_USES_POLL */
719
720 /*---------------------------------------------------------------------------*
721  *      device driver select
722  *---------------------------------------------------------------------------*/
723 PDEVSTATIC int
724 i4brbchselect(dev_t dev, int rw, struct thread *td)
725 {
726         int unit = minor(dev);
727         struct rbch_softc *sc = &rbch_softc[unit];
728         int s;
729
730         s = splhigh();
731
732         if(!(sc->sc_devstate & ST_ISOPEN))
733         {
734                 splx(s);
735                 NDBGL4(L4_RBCHDBG, "unit %d, not open anymore", unit);
736                 return(1);
737         }
738         
739         if(sc->sc_devstate & ST_CONNECTED)
740         {
741                 struct ifqueue *iqp;
742
743                 switch(rw)
744                 {
745                         case FREAD:
746                                 if(sc->sc_bprot == BPROT_RHDLC)
747                                         iqp = &sc->sc_hdlcq;
748                                 else
749                                         iqp = isdn_linktab[unit]->rx_queue;     
750
751                                 if(!IF_QEMPTY(iqp))
752                                 {
753                                         splx(s);
754                                         return(1);
755                                 }
756                                 break;
757
758                         case FWRITE:
759                                 if(!_IF_QFULL(isdn_linktab[unit]->rx_queue))
760                                 {
761                                         splx(s);
762                                         return(1);
763                                 }
764                                 break;
765
766                         default:
767                                 splx(s);
768                                 return 0;
769                 }
770         }
771         selrecord(p, &sc->selp);
772         splx(s);
773         return(0);
774 }
775
776 #endif /* OS_USES_POLL */
777
778 #if I4BRBCHACCT
779 /*---------------------------------------------------------------------------*
780  *      watchdog routine
781  *---------------------------------------------------------------------------*/
782 static void
783 rbch_timeout(struct rbch_softc *sc)
784 {
785         bchan_statistics_t bs;
786         int unit = sc->sc_unit;
787
788         /* get # of bytes in and out from the HSCX driver */ 
789         
790         (*isdn_linktab[unit]->bch_stat)
791                 (isdn_linktab[unit]->unit, isdn_linktab[unit]->channel, &bs);
792
793         sc->sc_ioutb += bs.outbytes;
794         sc->sc_iinb += bs.inbytes;
795         
796         if((sc->sc_iinb != sc->sc_linb) || (sc->sc_ioutb != sc->sc_loutb) || sc->sc_fn) 
797         {
798                 int ri = (sc->sc_iinb - sc->sc_linb)/I4BRBCHACCTINTVL;
799                 int ro = (sc->sc_ioutb - sc->sc_loutb)/I4BRBCHACCTINTVL;
800
801                 if((sc->sc_iinb == sc->sc_linb) && (sc->sc_ioutb == sc->sc_loutb))
802                         sc->sc_fn = 0;
803                 else
804                         sc->sc_fn = 1;
805                         
806                 sc->sc_linb = sc->sc_iinb;
807                 sc->sc_loutb = sc->sc_ioutb;
808
809                 i4b_l4_accounting(BDRV_RBCH, unit, ACCT_DURING,
810                          sc->sc_ioutb, sc->sc_iinb, ro, ri, sc->sc_ioutb, sc->sc_iinb);
811         }
812         callout_reset(&sc->sc_timeout, I4BRBCHACCTINTVL * hz,
813                         (void *)rbch_timeout, sc);
814 }
815 #endif /* I4BRBCHACCT */
816
817 /*===========================================================================*
818  *                      ISDN INTERFACE ROUTINES
819  *===========================================================================*/
820
821 /*---------------------------------------------------------------------------*
822  *      this routine is called from L4 handler at connect time
823  *---------------------------------------------------------------------------*/
824 static void
825 rbch_connect(int unit, void *cdp)
826 {
827         call_desc_t *cd = (call_desc_t *)cdp;
828         struct rbch_softc *sc = &rbch_softc[unit];
829
830         sc->sc_bprot = cd->bprot;
831
832 #if I4BRBCHACCT
833         if(sc->sc_bprot == BPROT_RHDLC)
834         {       
835                 sc->sc_iinb = 0;
836                 sc->sc_ioutb = 0;
837                 sc->sc_linb = 0;
838                 sc->sc_loutb = 0;
839
840                 callout_reset(&sc->sc_timeout, I4BRBCHACCTINTVL * hz,
841                                 (void *)rbch_timeout, sc);
842         }
843 #endif          
844         if(!(sc->sc_devstate & ST_CONNECTED))
845         {
846                 NDBGL4(L4_RBCHDBG, "unit %d, wakeup", unit);
847                 sc->sc_devstate |= ST_CONNECTED;
848                 sc->sc_cd = cdp;
849                 wakeup((caddr_t)sc);
850         }
851 }
852
853 /*---------------------------------------------------------------------------*
854  *      this routine is called from L4 handler at disconnect time
855  *---------------------------------------------------------------------------*/
856 static void
857 rbch_disconnect(int unit, void *cdp)
858 {
859         call_desc_t *cd = (call_desc_t *)cdp;
860         struct rbch_softc *sc = &rbch_softc[unit];
861
862         CRIT_VAR;
863         
864         if(cd != sc->sc_cd)
865         {
866                 NDBGL4(L4_RBCHDBG, "rbch%d: channel %d not active",
867                         cd->driver_unit, cd->channelid);
868                 return;
869         }
870
871         CRIT_BEG;
872         
873         NDBGL4(L4_RBCHDBG, "unit %d, disconnect", unit);
874
875         sc->sc_devstate &= ~ST_CONNECTED;
876
877         sc->sc_cd = NULL;
878         
879 #if I4BRBCHACCT
880         i4b_l4_accounting(BDRV_RBCH, unit, ACCT_FINAL,
881                  sc->sc_ioutb, sc->sc_iinb, 0, 0, sc->sc_ioutb, sc->sc_iinb);
882         callout_stop(&sc->sc_timeout);
883 #endif          
884         CRIT_END;
885 }
886         
887 /*---------------------------------------------------------------------------*
888  *      feedback from daemon in case of dial problems
889  *---------------------------------------------------------------------------*/
890 static void
891 rbch_dialresponse(int unit, int status, cause_t cause)
892 {
893 }
894         
895 /*---------------------------------------------------------------------------*
896  *      interface up/down
897  *---------------------------------------------------------------------------*/
898 static void
899 rbch_updown(int unit, int updown)
900 {
901 }
902         
903 /*---------------------------------------------------------------------------*
904  *      this routine is called from the HSCX interrupt handler
905  *      when a new frame (mbuf) has been received and is to be put on
906  *      the rx queue.
907  *---------------------------------------------------------------------------*/
908 static void
909 rbch_rx_data_rdy(int unit)
910 {
911         if(rbch_softc[unit].sc_bprot == BPROT_RHDLC)
912         {
913                 struct mbuf *m;
914                 
915                 if((m = *isdn_linktab[unit]->rx_mbuf) == NULL)
916                         return;
917
918                 m->m_pkthdr.len = m->m_len;
919
920 #if defined (__FreeBSD__) && __FreeBSD__ > 4
921                 if (! IF_HANDOFF(&(rbch_softc[unit].sc_hdlcq), m, NULL))
922                 {
923                         NDBGL4(L4_RBCHDBG, "unit %d: hdlc rx queue full!", unit);
924                 }
925 #else
926                 if(IF_QFULL(&(rbch_softc[unit].sc_hdlcq)))
927                 {
928                         NDBGL4(L4_RBCHDBG, "unit %d: hdlc rx queue full!", unit);
929                         m_freem(m);
930                 }                       
931                 else
932                 {
933                         IF_ENQUEUE(&(rbch_softc[unit].sc_hdlcq), m);
934                 }
935 #endif          
936         }
937
938         if(rbch_softc[unit].sc_devstate & ST_RDWAITDATA)
939         {
940                 NDBGL4(L4_RBCHDBG, "unit %d, wakeup", unit);
941                 rbch_softc[unit].sc_devstate &= ~ST_RDWAITDATA;
942                 wakeup((caddr_t) &isdn_linktab[unit]->rx_queue);
943         }
944         else
945         {
946                 NDBGL4(L4_RBCHDBG, "unit %d, NO wakeup", unit);
947         }
948         selwakeup(&rbch_softc[unit].selp);
949 }
950
951 /*---------------------------------------------------------------------------*
952  *      this routine is called from the HSCX interrupt handler
953  *      when the last frame has been sent out and there is no
954  *      further frame (mbuf) in the tx queue.
955  *---------------------------------------------------------------------------*/
956 static void
957 rbch_tx_queue_empty(int unit)
958 {
959         if(rbch_softc[unit].sc_devstate & ST_WRWAITEMPTY)
960         {
961                 NDBGL4(L4_RBCHDBG, "unit %d, wakeup", unit);
962                 rbch_softc[unit].sc_devstate &= ~ST_WRWAITEMPTY;
963                 wakeup((caddr_t) &isdn_linktab[unit]->tx_queue);
964         }
965         else
966         {
967                 NDBGL4(L4_RBCHDBG, "unit %d, NO wakeup", unit);
968         }
969         selwakeup(&rbch_softc[unit].selp);
970 }
971
972 /*---------------------------------------------------------------------------*
973  *      this routine is called from the HSCX interrupt handler
974  *      each time a packet is received or transmitted
975  *---------------------------------------------------------------------------*/
976 static void
977 rbch_activity(int unit, int rxtx)
978 {
979         if (rbch_softc[unit].sc_cd)
980                 rbch_softc[unit].sc_cd->last_active_time = SECOND;
981         selwakeup(&rbch_softc[unit].selp);
982 }
983
984 /*---------------------------------------------------------------------------*
985  *      clear an hdlc rx queue for a rbch unit
986  *---------------------------------------------------------------------------*/
987 static void
988 rbch_clrq(int unit)
989 {
990         CRIT_VAR;
991
992 #if defined (__FreeBSD__) && __FreeBSD__ > 4
993         CRIT_BEG;
994         IF_DRAIN(&rbch_softc[unit].sc_hdlcq);
995         CRIT_END;
996 #else
997         struct mbuf *m;
998         for(;;)
999         {
1000                 CRIT_BEG;
1001                 IF_DEQUEUE(&rbch_softc[unit].sc_hdlcq, m);
1002                 CRIT_END;
1003
1004                 if(m)
1005                         m_freem(m);
1006                 else
1007                         break;
1008         }
1009 #endif  
1010 }
1011                                 
1012 /*---------------------------------------------------------------------------*
1013  *      return this drivers linktab address
1014  *---------------------------------------------------------------------------*/
1015 drvr_link_t *
1016 rbch_ret_linktab(int unit)
1017 {
1018         rbch_init_linktab(unit);
1019         return(&rbch_drvr_linktab[unit]);
1020 }
1021
1022 /*---------------------------------------------------------------------------*
1023  *      setup the isdn_linktab for this driver
1024  *---------------------------------------------------------------------------*/
1025 void
1026 rbch_set_linktab(int unit, isdn_link_t *ilt)
1027 {
1028         isdn_linktab[unit] = ilt;
1029 }
1030
1031 /*---------------------------------------------------------------------------*
1032  *      initialize this drivers linktab
1033  *---------------------------------------------------------------------------*/
1034 static void
1035 rbch_init_linktab(int unit)
1036 {
1037         rbch_drvr_linktab[unit].unit = unit;
1038         rbch_drvr_linktab[unit].bch_rx_data_ready = rbch_rx_data_rdy;
1039         rbch_drvr_linktab[unit].bch_tx_queue_empty = rbch_tx_queue_empty;
1040         rbch_drvr_linktab[unit].bch_activity = rbch_activity;   
1041         rbch_drvr_linktab[unit].line_connected = rbch_connect;
1042         rbch_drvr_linktab[unit].line_disconnected = rbch_disconnect;
1043         rbch_drvr_linktab[unit].dial_response = rbch_dialresponse;
1044         rbch_drvr_linktab[unit].updown_ind = rbch_updown;       
1045 }
1046
1047 /*===========================================================================*/
1048
1049 #endif /* NI4BRBCH > 0 */