/* * /src/NTP/REPOSITORY/v3/parse/parsestreams.c,v 3.22 1994/06/01 10:41:16 kardel Exp * * parsestreams.c,v 3.22 1994/06/01 10:41:16 kardel Exp * * STREAMS module for reference clocks * (SunOS4.x) * * Copyright (c) 1989,1990,1991,1992,1993,1994 * Frank Kardel Friedrich-Alexander Universitaet Erlangen-Nuernberg * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * */ #ifndef lint static char rcsid[] = "parsestreams.c,v 3.22 1994/06/01 10:41:16 kardel Exp"; #endif #include "sys/types.h" #include "sys/conf.h" #include "sys/buf.h" #include "sys/param.h" #include "sys/sysmacros.h" #include "sys/errno.h" #include "sys/time.h" #include "sundev/mbvar.h" #include "sun/autoconf.h" #include "sys/stream.h" #include "sys/stropts.h" #include "sys/dir.h" #include "sys/signal.h" #include "sys/termios.h" #include "sys/termio.h" #include "sys/ttold.h" #include "sys/user.h" #include "sys/errno.h" #include "sys/tty.h" #include "machine/cpu.h" #ifdef VDDRV #include "sun/vddrv.h" #endif /* * no protypes here ! */ #define P(x) () /* * use microtime instead of uniqtime if advised to */ #ifdef MICROTIME #define uniqtime microtime #endif #define HAVE_NO_NICE /* for the NTP headerfiles */ #include "ntp_fp.h" #include "parse.h" #include "sys/parsestreams.h" #ifdef VDDRV static unsigned int parsebusy = 0; /*--------------- loadable driver section -----------------------------*/ extern struct streamtab parseinfo; struct vdldrv parsesync_vd = { VDMAGIC_PSEUDO, /* nothing like a real driver - a STREAMS module */ "PARSE ", /* name this baby - keep room for revision number */ }; /* * strings support usually not in kernel */ static int strlen(s) register char *s; { register int c; c = 0; if (s) { while (*s++) { c++; } } return c; } static void strncpy(t, s, c) register char *t; register char *s; register int c; { if (s && t) { while ((c-- > 0) && (*t++ = *s++)) ; } } static int strcmp(s, t) register char *s; register char *t; { register int c = 0; if (!s || !t || (s == t)) { return 0; } while (!(c = *s++ - *t++) && *s && *t) /* empty loop */; return c; } static int strncmp(s, t, n) register char *s; register char *t; register int n; { register int c = 0; if (!s || !t || (s == t)) { return 0; } while (n-- && !(c = *s++ - *t++) && *s && *t) /* empty loop */; return c; } /* * driver init routine * since no mechanism gets us into and out of the fmodsw, we have to * do it ourselves */ /*ARGSUSED*/ int xxxinit(fc, vdp, vdi, vds) unsigned int fc; struct vddrv *vdp; addr_t vdi; struct vdstat *vds; { extern struct fmodsw fmodsw[]; extern int fmodcnt; struct fmodsw *fm = fmodsw; struct fmodsw *fmend = &fmodsw[fmodcnt]; struct fmodsw *ifm = (struct fmodsw *)0; char *mname = parseinfo.st_rdinit->qi_minfo->mi_idname; switch (fc) { case VDLOAD: vdp->vdd_vdtab = (struct vdlinkage *)&parsesync_vd; /* * now, jog along fmodsw scanning for an empty slot * and deposit our name there */ while (fm <= fmend) { if (!strncmp(fm->f_name, mname, FMNAMESZ)) { printf("vddrinit[%s]: STREAMS module already loaded.\n", mname); return(EBUSY); } else if ((ifm == (struct fmodsw *)0) && (fm->f_name[0] == '\0') && (fm->f_str == (struct streamtab *)0)) { /* * got one - so move in */ ifm = fm; break; } fm++; } if (ifm == (struct fmodsw *)0) { printf("vddrinit[%s]: no slot free for STREAMS module\n", mname); return (ENOSPC); } else { static char revision[] = "3.22"; char *s, *S, *t; strncpy(ifm->f_name, mname, FMNAMESZ); ifm->f_name[FMNAMESZ] = '\0'; ifm->f_str = &parseinfo; /* * copy RCS revision into Drv_name * * are we forcing RCS here to do things it was not built for ? */ s = revision; if (*s == '$') { /* * skip "$Revision: " * if present. - not necessary on a -kv co (cvs export) */ while (*s && (*s != ' ')) { s++; } if (*s == ' ') s++; } t = parsesync_vd.Drv_name; while (*t && (*t != ' ')) { t++; } if (*t == ' ') t++; S = s; while (*S && (((*S >= '0') && (*S <= '9')) || (*S == '.'))) { S++; } if (*s && *t && (S > s)) { if (strlen(t) >= (S - s)) { (void) strncpy(t, s, S - s); } } return (0); } break; case VDUNLOAD: if (parsebusy > 0) { printf("vddrinit[%s]: STREAMS module has still %d instances active.\n", mname, parsebusy); return (EBUSY); } else { while (fm <= fmend) { if (!strncmp(fm->f_name, mname, FMNAMESZ)) { /* * got it - kill entry */ fm->f_name[0] = '\0'; fm->f_str = (struct streamtab *)0; fm++; break; } fm++; } if (fm > fmend) { printf("vddrinit[%s]: cannot find entry for STREAMS module\n", mname); return (ENXIO); } else return (0); } case VDSTAT: return (0); default: return (EIO); } return EIO; } #endif /*--------------- stream module definition ----------------------------*/ static int parseopen(), parseclose(), parsewput(), parserput(), parsersvc(); static struct module_info driverinfo = { 0, /* module ID number */ "parse", /* module name */ 0, /* minimum accepted packet size */ INFPSZ, /* maximum accepted packet size */ 1, /* high water mark - flow control */ 0 /* low water mark - flow control */ }; static struct qinit rinit = /* read queue definition */ { parserput, /* put procedure */ parsersvc, /* service procedure */ parseopen, /* open procedure */ parseclose, /* close procedure */ NULL, /* admin procedure - NOT USED FOR NOW */ &driverinfo, /* information structure */ NULL /* statistics */ }; static struct qinit winit = /* write queue definition */ { parsewput, /* put procedure */ NULL, /* service procedure */ NULL, /* open procedure */ NULL, /* close procedure */ NULL, /* admin procedure - NOT USED FOR NOW */ &driverinfo, /* information structure */ NULL /* statistics */ }; struct streamtab parseinfo = /* stream info element for dpr driver */ { &rinit, /* read queue */ &winit, /* write queue */ NULL, /* read mux */ NULL, /* write mux */ NULL /* module auto push */ }; /*--------------- driver data structures ----------------------------*/ /* * we usually have an inverted signal - but you * can change this to suit your needs */ int cd_invert = 1; /* invert status of CD line - PPS support via CD input */ int parsedebug = ~0; extern void uniqtime(); /*--------------- module implementation -----------------------------*/ #define TIMEVAL_USADD(_X_, _US_) {\ (_X_)->tv_usec += (_US_);\ if ((_X_)->tv_usec >= 1000000)\ {\ (_X_)->tv_sec++;\ (_X_)->tv_usec -= 1000000;\ }\ } while (0) #if defined(sun4c) && defined(DEBUG_CD) #include #include #define SET_LED(_X_) (((cpu & CPU_ARCH) == SUN4C_ARCH) ? *(u_char *)AUXIO_REG = AUX_MBO|AUX_EJECT|((_X_)?AUX_LED:0) : 0) #else #define SET_LED(_X_) #endif static int init_linemon(); static void close_linemon(); /* * keep here MACHINE AND OS AND ENVIRONMENT DEPENDENT * timing constants * * FOR ABSOLUTE PRECISION YOU NEED TO MEASURE THE TIMING * SKEW BETWEEN THE HW-PPS SIGNAL AND KERNEL uniqtime() * YOURSELF. * * YOU MUST BE QUALIFIED APPROPRIATELY FOR THESE TYPE * OF HW MANIPULATION ! * * you need an oscilloscope and the permission for HW work * in order to figure out these timing constants/variables */ #ifdef sun static unsigned long xsdelay = 10; /* assume an SS2 */ static unsigned long stdelay = 350; struct delays { unsigned char mask; /* what to check for */ unsigned char type; /* what to match */ unsigned long xsdelay; /* external status direct delay in us */ unsigned long stdelay; /* STREAMS message delay (M_[UN]HANGUP) */ } isr_delays[] = { /* * WARNING: must still be measured - currently taken from Craig Leres ppsdev */ #ifdef sun4c {CPU_ARCH|CPU_MACH, CPU_SUN4C_50, 10, 350}, {CPU_ARCH|CPU_MACH, CPU_SUN4C_65, 15, 700}, {CPU_ARCH|CPU_MACH, CPU_SUN4C_75, 10, 350}, #endif #ifdef sun4m {CPU_ARCH|CPU_MACH, CPU_SUN4M_50, 8, 250}, {CPU_ARCH|CPU_MACH, CPU_SUN4M_690, 8, 250}, #endif {0,} }; void setup_delays() { register int i; for (i = 0; isr_delays[i].mask; i++) { if ((cpu & isr_delays[i].mask) == isr_delays[i].type) { xsdelay = isr_delays[i].xsdelay; stdelay = isr_delays[i].stdelay; return; } } printf("parse: WARNING: PPS kernel fudge factors unknown for this machine (Type 0x%x) - assuming SS2 (Sun4/75)\n", cpu); } #else #define setup_delays() /* empty - no need for clobbering kernel with this */ static unsigned long xsdelay = 0; /* assume nothing */ static unsigned long stdelay = 0; #endif #define M_PARSE 0x0001 #define M_NOPARSE 0x0002 static int setup_stream(q, mode) queue_t *q; int mode; { mblk_t *mp; mp = allocb(sizeof(struct stroptions), BPRI_MED); if (mp) { struct stroptions *str = (struct stroptions *)mp->b_rptr; str->so_flags = SO_READOPT|SO_HIWAT|SO_LOWAT; str->so_readopt = (mode == M_PARSE) ? RMSGD : RNORM; str->so_hiwat = (mode == M_PARSE) ? sizeof(parsetime_t) : 256; str->so_lowat = 0; mp->b_datap->db_type = M_SETOPTS; mp->b_wptr += sizeof(struct stroptions); putnext(q, mp); return putctl1(WR(q)->q_next, M_CTL, (mode == M_PARSE) ? MC_SERVICEIMM : MC_SERVICEDEF); } else { parseprintf(DD_OPEN,("parse: setup_stream - FAILED - no MEMORY for allocb\n")); return 0; } } /*ARGSUSED*/ static int parseopen(q, dev, flag, sflag) queue_t *q; dev_t dev; int flag; int sflag; { register mblk_t *mp; register parsestream_t *parse; static int notice = 0; parseprintf(DD_OPEN,("parse: OPEN\n")); if (sflag != MODOPEN) { /* open only for modules */ parseprintf(DD_OPEN,("parse: OPEN - FAILED - not MODOPEN\n")); return OPENFAIL; } if (q->q_ptr != (caddr_t)NULL) { u.u_error = EBUSY; parseprintf(DD_OPEN,("parse: OPEN - FAILED - EXCLUSIVE ONLY\n")); return OPENFAIL; } #ifdef VDDRV parsebusy++; #endif q->q_ptr = (caddr_t)kmem_alloc(sizeof(parsestream_t)); if (q->q_ptr == (caddr_t)0) { parseprintf(DD_OPEN,("parse: OPEN - FAILED - no memory\n")); return OPENFAIL; } WR(q)->q_ptr = q->q_ptr; parse = (parsestream_t *) q->q_ptr; bzero((caddr_t)parse, sizeof(*parse)); parse->parse_queue = q; parse->parse_status = PARSE_ENABLE; parse->parse_ppsclockev.tv.tv_sec = 0; parse->parse_ppsclockev.tv.tv_usec = 0; parse->parse_ppsclockev.serial = 0; if (!parse_ioinit(&parse->parse_io)) { /* * ok guys - beat it */ kmem_free((caddr_t)parse, sizeof(parsestream_t)); #ifdef VDDRV parsebusy--; #endif return OPENFAIL; } if (setup_stream(q, M_PARSE)) { (void) init_linemon(q); /* hook up PPS ISR routines if possible */ setup_delays(); parseprintf(DD_OPEN,("parse: OPEN - SUCCEEDED\n")); /* * I know that you know the delete key, but you didn't write this * code, did you ? - So, keep the message in here. */ if (!notice) { printf("%s: Copyright (c) 1991-1994, Frank Kardel\n", parsesync_vd.Drv_name); notice = 1; } return 1; } else { kmem_free((caddr_t)parse, sizeof(parsestream_t)); #ifdef VDDRV parsebusy--; #endif return OPENFAIL; } } /*ARGSUSED*/ static int parseclose(q, flags) queue_t *q; int flags; { register parsestream_t *parse = (parsestream_t *)q->q_ptr; register unsigned long s; parseprintf(DD_CLOSE,("parse: CLOSE\n")); s = splhigh(); if (parse->parse_dqueue) close_linemon(parse->parse_dqueue, q); parse->parse_dqueue = (queue_t *)0; (void) splx(s); parse_ioend(&parse->parse_io); kmem_free((caddr_t)parse, sizeof(parsestream_t)); q->q_ptr = (caddr_t)NULL; WR(q)->q_ptr = (caddr_t)NULL; #ifdef VDDRV parsebusy--; #endif } /* * move unrecognized stuff upward */ static parsersvc(q) queue_t *q; { mblk_t *mp; while (mp = getq(q)) { if (canput(q->q_next) || (mp->b_datap->db_type > QPCTL)) { putnext(q, mp); parseprintf(DD_RSVC,("parse: RSVC - putnext\n")); } else { putbq(q, mp); parseprintf(DD_RSVC,("parse: RSVC - flow control wait\n")); break; } } } /* * do ioctls and * send stuff down - dont care about * flow control */ static int parsewput(q, mp) queue_t *q; register mblk_t *mp; { register int ok = 1; register mblk_t *datap; register struct iocblk *iocp; parsestream_t *parse = (parsestream_t *)q->q_ptr; parseprintf(DD_WPUT,("parse: parsewput\n")); switch (mp->b_datap->db_type) { default: putnext(q, mp); break; case M_IOCTL: iocp = (struct iocblk *)mp->b_rptr; switch (iocp->ioc_cmd) { default: parseprintf(DD_WPUT,("parse: parsewput - forward M_IOCTL\n")); putnext(q, mp); break; case CIOGETEV: /* * taken from Craig Leres ppsclock module (and modified) */ datap = allocb(sizeof(struct ppsclockev), BPRI_MED); if (datap == NULL || mp->b_cont) { mp->b_datap->db_type = M_IOCNAK; iocp->ioc_error = (datap == NULL) ? ENOMEM : EINVAL; if (datap != NULL) freeb(datap); qreply(q, mp); break; } mp->b_cont = datap; *(struct ppsclockev *)datap->b_wptr = parse->parse_ppsclockev; datap->b_wptr += sizeof(struct ppsclockev) / sizeof(*datap->b_wptr); mp->b_datap->db_type = M_IOCACK; iocp->ioc_count = sizeof(struct ppsclockev); qreply(q, mp); break; case PARSEIOC_ENABLE: case PARSEIOC_DISABLE: { parse->parse_status = (parse->parse_status & ~PARSE_ENABLE) | (iocp->ioc_cmd == PARSEIOC_ENABLE) ? PARSE_ENABLE : 0; if (!setup_stream(RD(q), (parse->parse_status & PARSE_ENABLE) ? M_PARSE : M_NOPARSE)) { mp->b_datap->db_type = M_IOCNAK; } else { mp->b_datap->db_type = M_IOCACK; } qreply(q, mp); break; } case PARSEIOC_SETSTAT: case PARSEIOC_GETSTAT: case PARSEIOC_TIMECODE: case PARSEIOC_SETFMT: case PARSEIOC_GETFMT: case PARSEIOC_SETCS: if (iocp->ioc_count == sizeof(parsectl_t)) { parsectl_t *dct = (parsectl_t *)mp->b_cont->b_rptr; switch (iocp->ioc_cmd) { case PARSEIOC_GETSTAT: parseprintf(DD_WPUT,("parse: parsewput - PARSEIOC_GETSTAT\n")); ok = parse_getstat(dct, &parse->parse_io); break; case PARSEIOC_SETSTAT: parseprintf(DD_WPUT,("parse: parsewput - PARSEIOC_SETSTAT\n")); ok = parse_setstat(dct, &parse->parse_io); break; case PARSEIOC_TIMECODE: parseprintf(DD_WPUT,("parse: parsewput - PARSEIOC_TIMECODE\n")); ok = parse_timecode(dct, &parse->parse_io); break; case PARSEIOC_SETFMT: parseprintf(DD_WPUT,("parse: parsewput - PARSEIOC_SETFMT\n")); ok = parse_setfmt(dct, &parse->parse_io); break; case PARSEIOC_GETFMT: parseprintf(DD_WPUT,("parse: parsewput - PARSEIOC_GETFMT\n")); ok = parse_getfmt(dct, &parse->parse_io); break; case PARSEIOC_SETCS: parseprintf(DD_WPUT,("parse: parsewput - PARSEIOC_SETCS\n")); ok = parse_setcs(dct, &parse->parse_io); break; } mp->b_datap->db_type = ok ? M_IOCACK : M_IOCNAK; } else { mp->b_datap->db_type = M_IOCNAK; } parseprintf(DD_WPUT,("parse: parsewput qreply - %s\n", (mp->b_datap->db_type == M_IOCNAK) ? "M_IOCNAK" : "M_IOCACK")); qreply(q, mp); break; } } } /* * read characters from streams buffers */ static unsigned long rdchar(mp) register mblk_t **mp; { while (*mp != (mblk_t *)NULL) { if ((*mp)->b_wptr - (*mp)->b_rptr) { return (unsigned long)(*(unsigned char *)((*mp)->b_rptr++)); } else { register mblk_t *mmp = *mp; *mp = (*mp)->b_cont; freeb(mmp); } } return ~0; } /* * convert incoming data */ static int parserput(q, mp) queue_t *q; mblk_t *mp; { unsigned char type; switch (type = mp->b_datap->db_type) { default: /* * anything we don't know will be put on queue * the service routine will move it to the next one */ parseprintf(DD_RPUT,("parse: parserput - forward type 0x%x\n", type)); if (canput(q->q_next) || (mp->b_datap->db_type > QPCTL)) { putnext(q, mp); } else putq(q, mp); break; case M_BREAK: case M_DATA: { register parsestream_t * parse = (parsestream_t *)q->q_ptr; register mblk_t *nmp; register unsigned long ch; timestamp_t ctime; /* * get time on packet delivery */ uniqtime(&ctime.tv); if (!(parse->parse_status & PARSE_ENABLE)) { parseprintf(DD_RPUT,("parse: parserput - parser disabled - forward type 0x%x\n", type)); if (canput(q->q_next) || (mp->b_datap->db_type > QPCTL)) { putnext(q, mp); } else putq(q, mp); } else { parseprintf(DD_RPUT,("parse: parserput - M_%s\n", (type == M_DATA) ? "DATA" : "BREAK")); if (type == M_DATA) { /* * parse packet looking for start an end characters */ while (mp != (mblk_t *)NULL) { ch = rdchar(&mp); if (ch != ~0 && parse_ioread(&parse->parse_io, (char)ch, &ctime)) { /* * up up and away (hopefully ...) * don't press it if resources are tight or nobody wants it */ nmp = (mblk_t *)NULL; if (canput(parse->parse_queue->q_next) && (nmp = allocb(sizeof(parsetime_t), BPRI_MED))) { bcopy((caddr_t)&parse->parse_io.parse_dtime, (caddr_t)nmp->b_rptr, sizeof(parsetime_t)); nmp->b_wptr += sizeof(parsetime_t); putnext(parse->parse_queue, nmp); } else if (nmp) freemsg(nmp); parse_iodone(&parse->parse_io); } } } else { if (parse_ioread(&parse->parse_io, (char)0, &ctime)) { /* * up up and away (hopefully ...) * don't press it if resources are tight or nobody wants it */ nmp = (mblk_t *)NULL; if (canput(parse->parse_queue->q_next) && (nmp = allocb(sizeof(parsetime_t), BPRI_MED))) { bcopy((caddr_t)&parse->parse_io.parse_dtime, (caddr_t)nmp->b_rptr, sizeof(parsetime_t)); nmp->b_wptr += sizeof(parsetime_t); putnext(parse->parse_queue, nmp); } else if (nmp) freemsg(nmp); parse_iodone(&parse->parse_io); } freemsg(mp); } break; } } /* * CD PPS support for non direct ISR hack */ case M_HANGUP: case M_UNHANGUP: { register parsestream_t * parse = (parsestream_t *)q->q_ptr; timestamp_t ctime; register mblk_t *nmp; register int status = cd_invert ^ (type == M_HANGUP); SET_LED(status); uniqtime(&ctime.tv); TIMEVAL_USADD(&ctime.tv, stdelay); parseprintf(DD_RPUT,("parse: parserput - M_%sHANGUP\n", (type == M_HANGUP) ? "" : "UN")); if ((parse->parse_status & PARSE_ENABLE) && parse_iopps(&parse->parse_io, status ? SYNC_ONE : SYNC_ZERO, &ctime)) { nmp = (mblk_t *)NULL; if (canput(parse->parse_queue->q_next) && (nmp = allocb(sizeof(parsetime_t), BPRI_MED))) { bcopy((caddr_t)&parse->parse_io.parse_dtime, (caddr_t)nmp->b_rptr, sizeof(parsetime_t)); nmp->b_wptr += sizeof(parsetime_t); putnext(parse->parse_queue, nmp); } else if (nmp) freemsg(nmp); parse_iodone(&parse->parse_io); freemsg(mp); } else if (canput(q->q_next) || (mp->b_datap->db_type > QPCTL)) { putnext(q, mp); } else putq(q, mp); if (status) { parse->parse_ppsclockev.tv = ctime.tv; ++(parse->parse_ppsclockev.serial); } } } } static int init_zs_linemon(); /* handle line monitor for "zs" driver */ static void close_zs_linemon(); static void zs_xsisr(); /* zs external status interupt handler */ /*-------------------- CD isr status monitor ---------------*/ static int init_linemon(q) register queue_t *q; { register queue_t *dq; dq = WR(q); /* * we ARE doing very bad things down here (basically stealing ISR * hooks) * * so we chase down the STREAMS stack searching for the driver * and if this is a known driver we insert our ISR routine for * status changes in to the ExternalStatus handling hook */ while (dq->q_next) { dq = dq->q_next; /* skip down to driver */ } /* * find appropriate driver dependent routine */ if (dq->q_qinfo && dq->q_qinfo->qi_minfo) { register char *dname = dq->q_qinfo->qi_minfo->mi_idname; parseprintf(DD_INSTALL, ("init_linemon: driver is \"%s\"\n", dname)); #ifdef sun if (dname && !strcmp(dname, "zs")) { return init_zs_linemon(dq, q); } else #endif { parseprintf(DD_INSTALL, ("init_linemon: driver \"%s\" not suitable for CD monitoring\n", dname)); return 0; } } parseprintf(DD_INSTALL, ("init_linemon: cannot find driver\n")); return 0; } static void close_linemon(q, my_q) register queue_t *q; register queue_t *my_q; { /* * find appropriate driver dependent routine */ if (q->q_qinfo && q->q_qinfo->qi_minfo) { register char *dname = q->q_qinfo->qi_minfo->mi_idname; #ifdef sun if (dname && !strcmp(dname, "zs")) { close_zs_linemon(q, my_q); return; } parseprintf(DD_INSTALL, ("close_linemon: cannot find driver close routine for \"%s\"\n", dname)); #endif } parseprintf(DD_INSTALL, ("close_linemon: cannot find driver name\n")); } #ifdef sun #include #include #include struct savedzsops { struct zsops zsops; struct zsops *oldzsops; }; struct zsops *emergencyzs; static int init_zs_linemon(q, my_q) register queue_t *q; register queue_t *my_q; { register struct zscom *zs; register struct savedzsops *szs; register parsestream_t *parsestream = (parsestream_t *)my_q->q_ptr; /* * we expect the zsaline pointer in the q_data pointer * from there on we insert our on EXTERNAL/STATUS ISR routine * into the interrupt path, before the standard handler */ zs = ((struct zsaline *)q->q_ptr)->za_common; if (!zs) { /* * well - not found on startup - just say no (shouldn't happen though) */ return 0; } else { unsigned long s; /* * we do a direct replacement, in case others fiddle also * if somebody else grabs our hook and we disconnect * we are in DEEP trouble - panic is likely to be next, sorry */ szs = (struct savedzsops *) kmem_alloc(sizeof(struct savedzsops)); if (szs == (struct savedzsops *)0) { parseprintf(DD_INSTALL, ("init_zs_linemon: CD monitor NOT installed - no memory\n")); return 0; } else { parsestream->parse_data = (void *)szs; s = splhigh(); parsestream->parse_dqueue = q; /* remember driver */ szs->zsops = *zs->zs_ops; szs->zsops.zsop_xsint = (int (*)())zs_xsisr; /* place our bastard */ szs->oldzsops = zs->zs_ops; emergencyzs = zs->zs_ops; zsopinit(zs, &szs->zsops); /* hook it up */ (void) splx(s); parseprintf(DD_INSTALL, ("init_zs_linemon: CD monitor installed\n")); return 1; } } } /* * unregister our ISR routine - must call under splhigh() */ static void close_zs_linemon(q, my_q) register queue_t *q; register queue_t *my_q; { register struct zscom *zs; register parsestream_t *parsestream = (parsestream_t *)my_q->q_ptr; zs = ((struct zsaline *)q->q_ptr)->za_common; if (!zs) { /* * well - not found on startup - just say no (shouldn't happen though) */ return; } else { register struct savedzsops *szs = (struct savedzsops *)parsestream->parse_data; zsopinit(zs, szs->oldzsops); /* reset to previous handler functions */ kmem_free((caddr_t)szs, sizeof (struct savedzsops)); parseprintf(DD_INSTALL, ("close_zs_linemon: CD monitor deleted\n")); return; } } #define MAXDEPTH 50 /* maximum allowed stream crawl */ #ifdef PPS_SYNC extern hardpps(); extern struct timeval time; #endif /* * take external status interrupt (only CD interests us) */ static void zs_xsisr(zs) register struct zscom *zs; { register struct zsaline *za = (struct zsaline *)zs->zs_priv; register struct zscc_device *zsaddr = zs->zs_addr; register queue_t *q; register unsigned char zsstatus; register int loopcheck; register char *dname; #ifdef PPS_SYNC register int s; register long usec; #endif /* * pick up current state */ zsstatus = zsaddr->zscc_control; if ((za->za_rr0 ^ zsstatus) & (ZSRR0_CD|ZSRR0_SYNC)) { timestamp_t cdevent; register int status; /* * CONDITIONAL external measurement support */ SET_LED(zsstatus & (ZSRR0_CD|ZSRR0_SYNC)); /* * inconsistent with upper SET_LED, but this * is for oscilloscope business anyway and we * are just interested in edge delays in the * lower us range */ #ifdef PPS_SYNC s = splclock(); usec = time.tv_usec; #endif /* * time stamp */ uniqtime(&cdevent.tv); #ifdef PPS_SYNC splx(s); #endif /* * logical state */ status = cd_invert ? (zsstatus & ZSRR0_SYNC) == 0 : (zsstatus & ZSRR0_SYNC) != 0; #ifdef PPS_SYNC if (status) { usec = cdevent.tv.tv_usec - usec; if (usec < 0) usec += 1000000; hardpps(&cdevent.tv, usec); } #endif TIMEVAL_USADD(&cdevent.tv, xsdelay); q = za->za_ttycommon.t_readq; /* * ok - now the hard part - find ourself */ loopcheck = MAXDEPTH; while (q) { if (q->q_qinfo && q->q_qinfo->qi_minfo) { dname = q->q_qinfo->qi_minfo->mi_idname; if (!strcmp(dname, parseinfo.st_rdinit->qi_minfo->mi_idname)) { /* * back home - phew (hopping along stream queues might * prove dangerous to your health) */ if ((((parsestream_t *)q->q_ptr)->parse_status & PARSE_ENABLE) && parse_iopps(&((parsestream_t *)q->q_ptr)->parse_io, status ? SYNC_ONE : SYNC_ZERO, &cdevent)) { /* * XXX - currently we do not pass up the message, as * we should. * for a correct behaviour wee need to block out * processing until parse_iodone has been posted via * a softcall-ed routine which does the message pass-up * right now PPS information relies on input being * received */ parse_iodone(&((parsestream_t *)q->q_ptr)->parse_io); } if (status) { ((parsestream_t *)q->q_ptr)->parse_ppsclockev.tv = cdevent.tv; ++(((parsestream_t *)q->q_ptr)->parse_ppsclockev.serial); } parseprintf(DD_ISR, ("zs_xsisr: CD event %s has been posted for \"%s\"\n", status ? "ONE" : "ZERO", dname)); break; } } q = q->q_next; if (!loopcheck--) { panic("zs_xsisr: STREAMS Queue corrupted - CD event"); } } /* * only pretend that CD has been handled */ za->za_rr0 = za->za_rr0 & ~(ZSRR0_CD|ZSRR0_SYNC) | zsstatus & (ZSRR0_CD|ZSRR0_SYNC); ZSDELAY(2); if (!((za->za_rr0 ^ zsstatus) & ~(ZSRR0_CD|ZSRR0_SYNC))) { /* * all done - kill status indication and return */ zsaddr->zscc_control = ZSWR0_RESET_STATUS; /* might kill other conditions here */ return; } } /* * we are now gathered here to process some unusual external status * interrupts. * any CD events have also been handled and shouldn't be processed * by the original routine (unless we have a VERY busy port pin) * some initializations are done here, which could have been done before for * both code paths but have been avioded for minimum path length to * the uniq_time routine */ dname = (char *) 0; q = za->za_ttycommon.t_readq; loopcheck = MAXDEPTH; /* * the real thing for everything else ... */ while (q) { if (q->q_qinfo && q->q_qinfo->qi_minfo) { dname = q->q_qinfo->qi_minfo->mi_idname; if (!strcmp(dname, parseinfo.st_rdinit->qi_minfo->mi_idname)) { register int (*zsisr)(); /* * back home - phew (hopping along stream queues might * prove dangerous to your health) */ if (zsisr = ((struct savedzsops *)((parsestream_t *)q->q_ptr)->parse_data)->oldzsops->zsop_xsint) (void)zsisr(zs); else panic("zs_xsisr: unable to locate original ISR"); parseprintf(DD_ISR, ("zs_xsisr: non CD event was processed for \"%s\"\n", dname)); /* * now back to our program ... */ return; } } q = q->q_next; if (!loopcheck--) { panic("zs_xsisr: STREAMS Queue corrupted - non CD event"); } } /* * last resort - shouldn't even come here as it indicates * corrupted TTY structures */ printf("zs_zsisr: looking for \"%s\" - found \"%s\" - taking EMERGENCY path\n", parseinfo.st_rdinit->qi_minfo->mi_idname, dname ? dname : "-NIL-"); if (emergencyzs && emergencyzs->zsop_xsint) emergencyzs->zsop_xsint(zs); else panic("zs_xsisr: no emergency ISR handler"); } #endif /* sun */ /* * History: * * parsestreams.c,v * Revision 3.22 1994/06/01 10:41:16 kardel * CD seems to happen on ZSRR0_SYNC * * Revision 3.21 1994/06/01 08:18:57 kardel * look at CD only * * Revision 3.20 1994/05/30 09:57:43 kardel * kmem_alloc checking * * Revision 3.19 1994/02/24 16:33:54 kardel * CD events can olso be posted on sync flag * * Revision 3.18 1994/02/24 14:12:58 kardel * initial PPS_SYNC support version * * Revision 3.17 1994/02/20 15:18:02 kardel * rcs id cleanup * * Revision 3.16 1994/02/15 22:39:50 kardel * memory leak on open failure closed * * Revision 3.15 1994/02/13 19:16:50 kardel * updated verbose Copyright message * * Revision 3.14 1994/02/02 17:45:38 kardel * rcs ids fixed * * Revision 3.12 1994/01/25 19:05:30 kardel * 94/01/23 reconcilation * * Revision 3.11 1994/01/23 17:22:07 kardel * 1994 reconcilation * * Revision 3.10 1993/12/15 12:48:58 kardel * fixed message loss on M_*HANHUP messages * * Revision 3.9 1993/11/05 15:34:55 kardel * shut up nice feature detection * * Revision 3.8 1993/10/22 14:27:56 kardel * Oct. 22nd 1993 reconcilation * * Revision 3.7 1993/10/10 18:13:53 kardel * Makefile reorganisation, file relocation * * Revision 3.6 1993/10/09 15:01:18 kardel * file structure unified * * Revision 3.5 1993/10/04 07:59:31 kardel * Well, at least we should know that a the tv_usec field should be in the range 0..999999 * * Revision 3.4 1993/09/26 23:41:33 kardel * new parse driver logic * * Revision 3.3 1993/09/11 00:38:34 kardel * LINEMON must also cover M_[UN]HANGUP handling * * Revision 3.2 1993/07/06 10:02:56 kardel * DCF77 driver goes generic... * */