/* * Copyright (c) 1997, 1999 Hellmuth Michaelis. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * *--------------------------------------------------------------------------- * * i4btrc - device driver for trace data read device * --------------------------------------------------- * * $FreeBSD$ * * last edit-date: [Tue Jun 1 12:15:40 1999] * * NOTE: the code assumes that SPLI4B >= splimp ! * *---------------------------------------------------------------------------*/ #include "i4btrc.h" #if NI4BTRC > 0 #ifdef __FreeBSD__ #include "isic.h" /* 'isic' is no pseudo-device on non FreeBSD - * so we just can't count it at compile time, * we're doing an attach-time check instead. */ #if NI4BTRC < NISIC #error "number of trace devices != number of passive ISDN controllers !" #error "change number of i4btrc to be equal to number of isic devices !" #endif #endif /* __FreeBSD__ */ #include #include #if defined(__FreeBSD__) && __FreeBSD__ >= 3 #include #else #include #endif #include #include #include #include #include #include #include #include #ifdef __FreeBSD__ #include #include #else #include #include #endif #ifndef __FreeBSD__ #define memcpy(d,s,l) bcopy(s,d,l) #endif #include #include #include static struct ifqueue trace_queue[NI4BTRC]; static int device_state[NI4BTRC]; #define ST_IDLE 0x00 #define ST_ISOPEN 0x01 #define ST_WAITDATA 0x02 static int analyzemode = 0; static int rxunit = -1; static int txunit = -1; static int outunit = -1; #ifndef __FreeBSD__ #define PDEVSTATIC /* - not static - */ void i4btrcattach __P((void)); int i4btrcopen __P((dev_t dev, int flag, int fmt, struct proc *p)); int i4btrcclose __P((dev_t dev, int flag, int fmt, struct proc *p)); int i4btrcread __P((dev_t dev, struct uio * uio, int ioflag)); #ifdef __bsdi__ int i4btrcioctl __P((dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p)); #else int i4btrcioctl __P((dev_t dev, int cmd, caddr_t data, int flag, struct proc *p)); #endif #endif #if BSD > 199306 && defined(__FreeBSD__) #define PDEVSTATIC static static d_open_t i4btrcopen; static d_close_t i4btrcclose; static d_read_t i4btrcread; static d_ioctl_t i4btrcioctl; #ifdef OS_USES_POLL static d_poll_t i4btrcpoll; #define POLLFIELD i4btrcpoll #else #define POLLFIELD noselect #endif #define CDEV_MAJOR 59 #if defined (__FreeBSD_version) && __FreeBSD_version >= 400006 static struct cdevsw i4btrc_cdevsw = { /* open */ i4btrcopen, /* close */ i4btrcclose, /* read */ i4btrcread, /* write */ nowrite, /* ioctl */ i4btrcioctl, /* stop */ nostop, /* reset */ noreset, /* devtotty */ nodevtotty, /* poll */ POLLFIELD, /* mmap */ nommap, /* strategy */ nostrategy, /* name */ "i4btrc", /* parms */ noparms, /* maj */ CDEV_MAJOR, /* dump */ nodump, /* psize */ nopsize, /* flags */ 0, /* maxio */ 0, /* bmaj */ -1 }; #else static struct cdevsw i4btrc_cdevsw = { i4btrcopen, i4btrcclose, i4btrcread, nowrite, i4btrcioctl, nostop, noreset, nodevtotty, POLLFIELD, nommap, NULL, "i4btrc", NULL, -1 }; #endif /*---------------------------------------------------------------------------* * interface init routine *---------------------------------------------------------------------------*/ static void i4btrcinit(void *unused) { #if defined (__FreeBSD_version) && __FreeBSD_version >= 400006 cdevsw_add(&i4btrc_cdevsw); #else dev_t dev = makedev(CDEV_MAJOR, 0); cdevsw_add(&dev, &i4btrc_cdevsw, NULL); #endif } SYSINIT(i4btrcdev, SI_SUB_DRIVERS, SI_ORDER_MIDDLE+CDEV_MAJOR, &i4btrcinit, NULL); static void i4btrcattach(void *); PSEUDO_SET(i4btrcattach, i4b_trace); #endif /* BSD > 199306 && defined(__FreeBSD__) */ #ifdef __bsdi__ #include int i4btrcmatch(struct device *parent, struct cfdata *cf, void *aux); void dummy_i4btrcattach(struct device*, struct device *, void *); #define CDEV_MAJOR 60 static struct cfdriver i4btrccd = { NULL, "i4btrc", i4btrcmatch, dummy_i4btrcattach, DV_DULL, sizeof(struct cfdriver) }; struct devsw i4btrcsw = { &i4btrccd, i4btrcopen, i4btrcclose, i4btrcread, nowrite, i4btrcioctl, seltrue, nommap, nostrat, nodump, nopsize, 0, nostop }; int i4btrcmatch(struct device *parent, struct cfdata *cf, void *aux) { printf("i4btrcmatch: aux=0x%x\n", aux); return 1; } void dummy_i4btrcattach(struct device *parent, struct device *self, void *aux) { printf("dummy_i4btrcattach: aux=0x%x\n", aux); } #endif /* __bsdi__ */ int get_trace_data_from_l1(i4b_trace_hdr_t *hdr, int len, char *buf); /*---------------------------------------------------------------------------* * interface attach routine *---------------------------------------------------------------------------*/ PDEVSTATIC void #ifdef __FreeBSD__ i4btrcattach(void *dummy) #else i4btrcattach() #endif { int i; #ifndef HACK_NO_PSEUDO_ATTACH_MSG printf("i4btrc: %d ISDN trace device(s) attached\n", NI4BTRC); #endif for(i=0; i < NI4BTRC; i++) { make_dev(&i4btrc_cdevsw, i, UID_ROOT, GID_WHEEL, 0600, "i4btrc%d", i); trace_queue[i].ifq_maxlen = IFQ_MAXLEN; device_state[i] = ST_IDLE; } } /*---------------------------------------------------------------------------* * get_trace_data_from_l1() * ------------------------ * is called from layer 1, adds timestamp to trace data and puts * it into a queue, from which it can be read from the i4btrc * device. The unit number in the trace header selects the minor * device's queue the data is put into. *---------------------------------------------------------------------------*/ int get_trace_data_from_l1(i4b_trace_hdr_t *hdr, int len, char *buf) { struct mbuf *m; int x; int unit; int trunc = 0; int totlen = len + sizeof(i4b_trace_hdr_t); /* * for telephony (or better non-HDLC HSCX mode) we get * (MCLBYTE + sizeof(i4b_trace_hdr_t)) length packets * to put into the queue to userland. because of this * we detect this situation, strip the length to MCLBYTES * max size, and infor the userland program of this fact * by putting the no of truncated bytes into hdr->trunc. */ if(totlen > MCLBYTES) { trunc = 1; hdr->trunc = totlen - MCLBYTES; totlen = MCLBYTES; } else { hdr->trunc = 0; } /* set length of trace record */ hdr->length = totlen; /* check valid unit no */ if((unit = hdr->unit) > NI4BTRC) { printf("i4b_trace: get_trace_data_from_l1 - unit > NI4BTRC!\n"); return(0); } /* get mbuf */ if(!(m = i4b_Bgetmbuf(totlen))) { printf("i4b_trace: get_trace_data_from_l1 - i4b_getmbuf() failed!\n"); return(0); } /* check if we are in analyzemode */ if(analyzemode && (unit == rxunit || unit == txunit)) { if(unit == rxunit) hdr->dir = FROM_NT; else hdr->dir = FROM_TE; unit = outunit; } if(IF_QFULL(&trace_queue[unit])) { struct mbuf *m1; x = SPLI4B(); IF_DEQUEUE(&trace_queue[unit], m1); splx(x); i4b_Bfreembuf(m1); } /* copy trace header */ memcpy(m->m_data, hdr, sizeof(i4b_trace_hdr_t)); /* copy trace data */ if(trunc) memcpy(&m->m_data[sizeof(i4b_trace_hdr_t)], buf, totlen-sizeof(i4b_trace_hdr_t)); else memcpy(&m->m_data[sizeof(i4b_trace_hdr_t)], buf, len); x = SPLI4B(); IF_ENQUEUE(&trace_queue[unit], m); if(device_state[unit] & ST_WAITDATA) { device_state[unit] &= ~ST_WAITDATA; wakeup((caddr_t) &trace_queue[unit]); } splx(x); return(1); } /*---------------------------------------------------------------------------* * open trace device *---------------------------------------------------------------------------*/ PDEVSTATIC int i4btrcopen(dev_t dev, int flag, int fmt, struct proc *p) { int x; int unit = minor(dev); if(unit > NI4BTRC) return(ENXIO); if(device_state[unit] & ST_ISOPEN) return(EBUSY); if(analyzemode && (unit == outunit || unit == rxunit || unit == txunit)) return(EBUSY); x = SPLI4B(); device_state[unit] = ST_ISOPEN; splx(x); return(0); } /*---------------------------------------------------------------------------* * close trace device *---------------------------------------------------------------------------*/ PDEVSTATIC int i4btrcclose(dev_t dev, int flag, int fmt, struct proc *p) { int unit = minor(dev); int i, x, cno = -1; for(i = 0; i < nctrl; i++) { if(ctrl_desc[i].N_SET_TRACE) { cno = i; break; } } if(analyzemode && (unit == outunit)) { analyzemode = 0; outunit = -1; if(cno >= 0) { ctrl_desc[cno].N_SET_TRACE(rxunit, TRACE_OFF); ctrl_desc[cno].N_SET_TRACE(txunit, TRACE_OFF); } rxunit = -1; txunit = -1; } if(cno >= 0) { ctrl_desc[cno].N_SET_TRACE(unit, TRACE_OFF); } x = SPLI4B(); device_state[unit] = ST_IDLE; splx(x); return(0); } /*---------------------------------------------------------------------------* * read from trace device *---------------------------------------------------------------------------*/ PDEVSTATIC int i4btrcread(dev_t dev, struct uio * uio, int ioflag) { struct mbuf *m; int x; int error = 0; int unit = minor(dev); if(!(device_state[unit] & ST_ISOPEN)) return(EIO); x = SPLI4B(); while(IF_QEMPTY(&trace_queue[unit]) && (device_state[unit] & ST_ISOPEN)) { device_state[unit] |= ST_WAITDATA; if((error = tsleep((caddr_t) &trace_queue[unit], TTIPRI | PCATCH, "bitrc", 0 )) != 0) { device_state[unit] &= ~ST_WAITDATA; splx(x); return(error); } } IF_DEQUEUE(&trace_queue[unit], m); if(m && m->m_len) error = uiomove(m->m_data, m->m_len, uio); else error = EIO; if(m) i4b_Bfreembuf(m); splx(x); return(error); } #if defined(__FreeBSD__) && defined(OS_USES_POLL) /*---------------------------------------------------------------------------* * poll device *---------------------------------------------------------------------------*/ PDEVSTATIC int i4btrcpoll(dev_t dev, int events, struct proc *p) { return(ENODEV); } #endif /*---------------------------------------------------------------------------* * device driver ioctl routine *---------------------------------------------------------------------------*/ PDEVSTATIC int #if defined (__FreeBSD_version) && __FreeBSD_version >= 300003 i4btrcioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p) #elif defined(__bsdi__) i4btrcioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p) #else i4btrcioctl(dev_t dev, int cmd, caddr_t data, int flag, struct proc *p) #endif { int error = 0; int unit = minor(dev); i4b_trace_setupa_t *tsa; int i, cno = -1; /* find the first passive controller to get at the set/get function pointers. Would be better if we had the controller class virtual function table separate from the port registry... */ for(i=0; i < nctrl; i++) { if(ctrl_desc[i].N_SET_TRACE) { cno = i; /* one suitable controller, might not */ break; /* be related to the trace unit at all, but */ } /* has the right function pointers */ } switch(cmd) { case I4B_TRC_GET: if(cno < 0) return ENOTTY; *(int *)data = ctrl_desc[cno].N_GET_TRACE(unit); break; case I4B_TRC_SET: if(cno < 0) return ENOTTY; ctrl_desc[cno].N_SET_TRACE(unit, *(int *)data); break; case I4B_TRC_SETA: tsa = (i4b_trace_setupa_t *)data; if(tsa->rxunit >= 0 && tsa->rxunit < NI4BTRC) rxunit = tsa->rxunit; else error = EINVAL; if(tsa->txunit >= 0 && tsa->txunit < NI4BTRC) txunit = tsa->txunit; else error = EINVAL; if(error) { outunit = -1; rxunit = -1; txunit = -1; } else { if(cno < 0) return ENOTTY; outunit = unit; analyzemode = 1; ctrl_desc[cno].N_SET_TRACE(rxunit, tsa->rxflags & (TRACE_I | TRACE_D_RX | TRACE_B_RX)); ctrl_desc[cno].N_SET_TRACE(txunit, tsa->txflags & (TRACE_I | TRACE_D_RX | TRACE_B_RX)); } break; case I4B_TRC_RESETA: analyzemode = 0; outunit = -1; rxunit = -1; txunit = -1; break; default: error = ENOTTY; break; } return(error); } #endif /* NI4BTRC > 0 */