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