2 * Copyright (c) 1997, 2002 Hellmuth Michaelis. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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
26 /*---------------------------------------------------------------------------
28 * i4b_rbch.c - device driver for raw B channel data
29 * ---------------------------------------------------
30 * last edit-date: [Sun Mar 17 09:51:03 2002]
32 *---------------------------------------------------------------------------*/
34 #include <sys/cdefs.h>
35 __FBSDID("$FreeBSD$");
39 #include <sys/param.h>
40 #include <sys/systm.h>
43 #include <sys/kernel.h>
45 #include <sys/socket.h>
46 #include <sys/filio.h>
51 #include <i4b/include/i4b_ioctl.h>
52 #include <i4b/include/i4b_rbch_ioctl.h>
53 #include <i4b/include/i4b_debug.h>
55 #include <i4b/include/i4b_global.h>
56 #include <i4b/include/i4b_mbuf.h>
57 #include <i4b/include/i4b_l3l4.h>
59 #include <i4b/layer4/i4b_l4.h>
61 #include <sys/ioccom.h>
64 static drvr_link_t rbch_drvr_linktab[NI4BRBCH];
65 static isdn_link_t *isdn_linktab[NI4BRBCH];
67 #define I4BRBCHACCT 1 /* enable accounting messages */
68 #define I4BRBCHACCTINTVL 2 /* accounting msg interval in secs */
70 static struct rbch_softc {
71 int sc_unit; /* unit number */
73 int sc_devstate; /* state of driver */
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
81 int sc_bprot; /* B-ch protocol used */
82 call_desc_t *sc_cd; /* Call Descriptor */
85 struct ifqueue sc_hdlcq; /* hdlc read queue */
86 #define I4BRBCHMAXQLEN 10
88 struct selinfo selp; /* select / poll */
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 */
98 } rbch_softc[NI4BRBCH];
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);
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;
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,
127 static void i4brbchattach(void *);
128 PSEUDO_SET(i4brbchattach, i4b_rbch);
130 /*===========================================================================*
131 * DEVICE DRIVER ROUTINES
132 *===========================================================================*/
134 /*---------------------------------------------------------------------------*
135 * interface attach routine
136 *---------------------------------------------------------------------------*/
138 i4brbchattach(void *dummy)
142 printf("i4brbch: %d raw B channel access device(s) attached\n", NI4BRBCH);
144 for(i=0; i < NI4BRBCH; i++)
146 make_dev(&i4brbch_cdevsw, i,
147 UID_ROOT, GID_WHEEL, 0600, "i4brbch%d", i);
150 callout_handle_init(&rbch_softc[i].sc_callout);
151 rbch_softc[i].sc_fn = 1;
153 rbch_softc[i].sc_unit = i;
154 rbch_softc[i].sc_devstate = ST_IDLE;
155 rbch_softc[i].sc_hdlcq.ifq_maxlen = I4BRBCHMAXQLEN;
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);
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);
166 /*---------------------------------------------------------------------------*
168 *---------------------------------------------------------------------------*/
170 i4brbchopen(struct cdev *dev, int flag, int fmt, struct thread *td)
172 int unit = minor(dev);
177 if(rbch_softc[unit].sc_devstate & ST_ISOPEN)
184 rbch_softc[unit].sc_devstate |= ST_ISOPEN;
186 NDBGL4(L4_RBCHDBG, "unit %d, open", unit);
191 /*---------------------------------------------------------------------------*
193 *---------------------------------------------------------------------------*/
195 i4brbchclose(struct cdev *dev, int flag, int fmt, struct thread *td)
197 int unit = minor(dev);
198 struct rbch_softc *sc = &rbch_softc[unit];
200 if(sc->sc_devstate & ST_CONNECTED)
201 i4b_l4_drvrdisc(BDRV_RBCH, unit);
203 sc->sc_devstate &= ~ST_ISOPEN;
207 NDBGL4(L4_RBCHDBG, "unit %d, closed", unit);
212 /*---------------------------------------------------------------------------*
213 * read from rbch device
214 *---------------------------------------------------------------------------*/
216 i4brbchread(struct cdev *dev, struct uio *uio, int ioflag)
220 int unit = minor(dev);
222 struct rbch_softc *sc = &rbch_softc[unit];
226 NDBGL4(L4_RBCHDBG, "unit %d, enter read", unit);
229 if(!(sc->sc_devstate & ST_ISOPEN))
232 NDBGL4(L4_RBCHDBG, "unit %d, read while not open", unit);
236 if((sc->sc_devstate & ST_NOBLOCK))
238 if(!(sc->sc_devstate & ST_CONNECTED)) {
243 if(sc->sc_bprot == BPROT_RHDLC)
246 iqp = isdn_linktab[unit]->rx_queue;
248 if(IF_QEMPTY(iqp) && (sc->sc_devstate & ST_ISOPEN)) {
255 while(!(sc->sc_devstate & ST_CONNECTED))
257 NDBGL4(L4_RBCHDBG, "unit %d, wait read init", unit);
259 if((error = tsleep( &rbch_softc[unit],
264 NDBGL4(L4_RBCHDBG, "unit %d, error %d tsleep", unit, error);
269 if(sc->sc_bprot == BPROT_RHDLC)
272 iqp = isdn_linktab[unit]->rx_queue;
274 while(IF_QEMPTY(iqp) && (sc->sc_devstate & ST_ISOPEN))
276 sc->sc_devstate |= ST_RDWAITDATA;
278 NDBGL4(L4_RBCHDBG, "unit %d, wait read data", unit);
280 if((error = tsleep( &isdn_linktab[unit]->rx_queue,
285 NDBGL4(L4_RBCHDBG, "unit %d, error %d tsleep read", unit, error);
286 sc->sc_devstate &= ~ST_RDWAITDATA;
294 NDBGL4(L4_RBCHDBG, "unit %d, read %d bytes", unit, m->m_len);
298 error = uiomove(m->m_data, m->m_len, uio);
302 NDBGL4(L4_RBCHDBG, "unit %d, error %d uiomove", unit, error);
314 /*---------------------------------------------------------------------------*
315 * write to rbch device
316 *---------------------------------------------------------------------------*/
318 i4brbchwrite(struct cdev *dev, struct uio * uio, int ioflag)
322 int unit = minor(dev);
323 struct rbch_softc *sc = &rbch_softc[unit];
327 NDBGL4(L4_RBCHDBG, "unit %d, write", unit);
330 if(!(sc->sc_devstate & ST_ISOPEN))
332 NDBGL4(L4_RBCHDBG, "unit %d, write while not open", unit);
337 if((sc->sc_devstate & ST_NOBLOCK))
339 if(!(sc->sc_devstate & ST_CONNECTED)) {
343 if(_IF_QFULL(isdn_linktab[unit]->tx_queue) && (sc->sc_devstate & ST_ISOPEN)) {
350 while(!(sc->sc_devstate & ST_CONNECTED))
352 NDBGL4(L4_RBCHDBG, "unit %d, write wait init", unit);
354 error = tsleep( &rbch_softc[unit],
357 if(error == ERESTART) {
361 else if(error == EINTR)
364 NDBGL4(L4_RBCHDBG, "unit %d, EINTR during wait init", unit);
370 NDBGL4(L4_RBCHDBG, "unit %d, error %d tsleep init", unit, error);
373 tsleep( &rbch_softc[unit], I4BPRI | PCATCH, "xrbch", (hz*1));
376 while(_IF_QFULL(isdn_linktab[unit]->tx_queue) && (sc->sc_devstate & ST_ISOPEN))
378 sc->sc_devstate |= ST_WRWAITEMPTY;
380 NDBGL4(L4_RBCHDBG, "unit %d, write queue full", unit);
382 if ((error = tsleep( &isdn_linktab[unit]->tx_queue,
385 sc->sc_devstate &= ~ST_WRWAITEMPTY;
386 if(error == ERESTART)
391 else if(error == EINTR)
394 NDBGL4(L4_RBCHDBG, "unit %d, EINTR during wait write", unit);
400 NDBGL4(L4_RBCHDBG, "unit %d, error %d tsleep write", unit, error);
407 if(!(sc->sc_devstate & ST_ISOPEN))
409 NDBGL4(L4_RBCHDBG, "unit %d, not open anymore", unit);
414 if((m = i4b_Bgetmbuf(BCH_MAX_DATALEN)) != NULL)
416 m->m_len = min(BCH_MAX_DATALEN, uio->uio_resid);
418 NDBGL4(L4_RBCHDBG, "unit %d, write %d bytes", unit, m->m_len);
420 error = uiomove(m->m_data, m->m_len, uio);
422 (void) IF_HANDOFF(isdn_linktab[unit]->tx_queue, m, NULL);
424 (*isdn_linktab[unit]->bch_tx_start)(isdn_linktab[unit]->unit, isdn_linktab[unit]->channel);
432 /*---------------------------------------------------------------------------*
433 * rbch device ioctl handlibg
434 *---------------------------------------------------------------------------*/
436 i4brbchioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, struct thread *td)
439 int unit = minor(dev);
440 struct rbch_softc *sc = &rbch_softc[unit];
444 case FIOASYNC: /* Set async mode */
447 NDBGL4(L4_RBCHDBG, "unit %d, setting async mode", unit);
451 NDBGL4(L4_RBCHDBG, "unit %d, clearing async mode", unit);
458 NDBGL4(L4_RBCHDBG, "unit %d, setting non-blocking mode", unit);
459 sc->sc_devstate |= ST_NOBLOCK;
463 NDBGL4(L4_RBCHDBG, "unit %d, clearing non-blocking mode", unit);
464 sc->sc_devstate &= ~ST_NOBLOCK;
468 case TIOCCDTR: /* Clear DTR */
469 if(sc->sc_devstate & ST_CONNECTED)
471 NDBGL4(L4_RBCHDBG, "unit %d, disconnecting for DTR down", unit);
472 i4b_l4_drvrdisc(BDRV_RBCH, unit);
476 case I4B_RBCH_DIALOUT:
480 for (l = 0; l < TELNO_MAX && ((char *)data)[l]; l++)
484 NDBGL4(L4_RBCHDBG, "unit %d, attempting dialout to %s", unit, (char *)data);
485 i4b_l4_dialoutnumber(BDRV_RBCH, unit, l, (char *)data);
488 /* FALLTHROUGH to SDTR */
491 case TIOCSDTR: /* Set DTR */
492 NDBGL4(L4_RBCHDBG, "unit %d, attempting dialout (DTR)", unit);
493 i4b_l4_dialout(BDRV_RBCH, unit);
496 case TIOCSETA: /* Set termios struct */
499 case TIOCGETA: /* Get termios struct */
500 *(struct termios *)data = sc->it_in;
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;
509 case I4B_RBCH_VR_REQ:
513 mvr = (msg_vr_req_t *)data;
515 mvr->version = VERSION;
521 default: /* Unknown stuff */
522 NDBGL4(L4_RBCHDBG, "unit %d, ioctl, unknown cmd %lx", unit, (u_long)cmd);
529 /*---------------------------------------------------------------------------*
531 *---------------------------------------------------------------------------*/
533 i4brbchpoll(struct cdev *dev, int events, struct thread *td)
535 int revents = 0; /* Events we found */
537 int unit = minor(dev);
538 struct rbch_softc *sc = &rbch_softc[unit];
540 /* We can't check for anything but IN or OUT */
544 if(!(sc->sc_devstate & ST_ISOPEN))
551 * Writes are OK if we are connected and the
552 * transmit queue can take them
555 if((events & (POLLOUT|POLLWRNORM)) &&
556 (sc->sc_devstate & ST_CONNECTED) &&
557 !_IF_QFULL(isdn_linktab[unit]->tx_queue))
559 revents |= (events & (POLLOUT|POLLWRNORM));
562 /* ... while reads are OK if we have any data */
564 if((events & (POLLIN|POLLRDNORM)) &&
565 (sc->sc_devstate & ST_CONNECTED))
569 if(sc->sc_bprot == BPROT_RHDLC)
572 iqp = isdn_linktab[unit]->rx_queue;
575 revents |= (events & (POLLIN|POLLRDNORM));
579 selrecord(td, &sc->selp);
586 /*---------------------------------------------------------------------------*
588 *---------------------------------------------------------------------------*/
590 rbch_timeout(struct rbch_softc *sc)
592 bchan_statistics_t bs;
593 int unit = sc->sc_unit;
595 /* get # of bytes in and out from the HSCX driver */
597 (*isdn_linktab[unit]->bch_stat)
598 (isdn_linktab[unit]->unit, isdn_linktab[unit]->channel, &bs);
600 sc->sc_ioutb += bs.outbytes;
601 sc->sc_iinb += bs.inbytes;
603 if((sc->sc_iinb != sc->sc_linb) || (sc->sc_ioutb != sc->sc_loutb) || sc->sc_fn)
605 int ri = (sc->sc_iinb - sc->sc_linb)/I4BRBCHACCTINTVL;
606 int ro = (sc->sc_ioutb - sc->sc_loutb)/I4BRBCHACCTINTVL;
608 if((sc->sc_iinb == sc->sc_linb) && (sc->sc_ioutb == sc->sc_loutb))
613 sc->sc_linb = sc->sc_iinb;
614 sc->sc_loutb = sc->sc_ioutb;
616 i4b_l4_accounting(BDRV_RBCH, unit, ACCT_DURING,
617 sc->sc_ioutb, sc->sc_iinb, ro, ri, sc->sc_ioutb, sc->sc_iinb);
619 START_TIMER(sc->sc_callout, rbch_timeout, sc, I4BRBCHACCTINTVL*hz);
621 #endif /* I4BRBCHACCT */
623 /*===========================================================================*
624 * ISDN INTERFACE ROUTINES
625 *===========================================================================*/
627 /*---------------------------------------------------------------------------*
628 * this routine is called from L4 handler at connect time
629 *---------------------------------------------------------------------------*/
631 rbch_connect(int unit, void *cdp)
633 call_desc_t *cd = (call_desc_t *)cdp;
634 struct rbch_softc *sc = &rbch_softc[unit];
636 sc->sc_bprot = cd->bprot;
639 if(sc->sc_bprot == BPROT_RHDLC)
646 START_TIMER(sc->sc_callout, rbch_timeout, sc, I4BRBCHACCTINTVL*hz);
649 if(!(sc->sc_devstate & ST_CONNECTED))
651 NDBGL4(L4_RBCHDBG, "unit %d, wakeup", unit);
652 sc->sc_devstate |= ST_CONNECTED;
658 /*---------------------------------------------------------------------------*
659 * this routine is called from L4 handler at disconnect time
660 *---------------------------------------------------------------------------*/
662 rbch_disconnect(int unit, void *cdp)
664 call_desc_t *cd = (call_desc_t *)cdp;
665 struct rbch_softc *sc = &rbch_softc[unit];
671 NDBGL4(L4_RBCHDBG, "rbch%d: channel %d not active",
672 cd->driver_unit, cd->channelid);
678 NDBGL4(L4_RBCHDBG, "unit %d, disconnect", unit);
680 sc->sc_devstate &= ~ST_CONNECTED;
685 i4b_l4_accounting(BDRV_RBCH, unit, ACCT_FINAL,
686 sc->sc_ioutb, sc->sc_iinb, 0, 0, sc->sc_ioutb, sc->sc_iinb);
688 STOP_TIMER(sc->sc_callout, rbch_timeout, sc);
693 /*---------------------------------------------------------------------------*
694 * feedback from daemon in case of dial problems
695 *---------------------------------------------------------------------------*/
697 rbch_dialresponse(int unit, int status, cause_t cause)
701 /*---------------------------------------------------------------------------*
703 *---------------------------------------------------------------------------*/
705 rbch_updown(int unit, int updown)
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
713 *---------------------------------------------------------------------------*/
715 rbch_rx_data_rdy(int unit)
717 if(rbch_softc[unit].sc_bprot == BPROT_RHDLC)
719 register struct mbuf *m;
721 if((m = *isdn_linktab[unit]->rx_mbuf) == NULL)
724 m->m_pkthdr.len = m->m_len;
726 if (! IF_HANDOFF(&(rbch_softc[unit].sc_hdlcq), m, NULL))
728 NDBGL4(L4_RBCHDBG, "unit %d: hdlc rx queue full!", unit);
732 if(rbch_softc[unit].sc_devstate & ST_RDWAITDATA)
734 NDBGL4(L4_RBCHDBG, "unit %d, wakeup", unit);
735 rbch_softc[unit].sc_devstate &= ~ST_RDWAITDATA;
736 wakeup( &isdn_linktab[unit]->rx_queue);
740 NDBGL4(L4_RBCHDBG, "unit %d, NO wakeup", unit);
742 selwakeuppri(&rbch_softc[unit].selp, I4BPRI);
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 *---------------------------------------------------------------------------*/
751 rbch_tx_queue_empty(int unit)
753 if(rbch_softc[unit].sc_devstate & ST_WRWAITEMPTY)
755 NDBGL4(L4_RBCHDBG, "unit %d, wakeup", unit);
756 rbch_softc[unit].sc_devstate &= ~ST_WRWAITEMPTY;
757 wakeup( &isdn_linktab[unit]->tx_queue);
761 NDBGL4(L4_RBCHDBG, "unit %d, NO wakeup", unit);
763 selwakeuppri(&rbch_softc[unit].selp, TTOPRI);
766 /*---------------------------------------------------------------------------*
767 * this routine is called from the HSCX interrupt handler
768 * each time a packet is received or transmitted
769 *---------------------------------------------------------------------------*/
771 rbch_activity(int unit, int rxtx)
773 if (rbch_softc[unit].sc_cd)
774 rbch_softc[unit].sc_cd->last_active_time = SECOND;
775 selwakeuppri(&rbch_softc[unit].selp, I4BPRI);
778 /*---------------------------------------------------------------------------*
779 * clear an hdlc rx queue for a rbch unit
780 *---------------------------------------------------------------------------*/
787 IF_DRAIN(&rbch_softc[unit].sc_hdlcq);
791 /*---------------------------------------------------------------------------*
792 * return this drivers linktab address
793 *---------------------------------------------------------------------------*/
795 rbch_ret_linktab(int unit)
797 rbch_init_linktab(unit);
798 return(&rbch_drvr_linktab[unit]);
801 /*---------------------------------------------------------------------------*
802 * setup the isdn_linktab for this driver
803 *---------------------------------------------------------------------------*/
805 rbch_set_linktab(int unit, isdn_link_t *ilt)
807 isdn_linktab[unit] = ilt;
810 /*---------------------------------------------------------------------------*
811 * initialize this drivers linktab
812 *---------------------------------------------------------------------------*/
814 rbch_init_linktab(int unit)
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;
826 /*===========================================================================*/