diff options
Diffstat (limited to 'libparse/parsesolaris.c')
-rw-r--r-- | libparse/parsesolaris.c | 1202 |
1 files changed, 1202 insertions, 0 deletions
diff --git a/libparse/parsesolaris.c b/libparse/parsesolaris.c new file mode 100644 index 0000000..d3756af --- /dev/null +++ b/libparse/parsesolaris.c @@ -0,0 +1,1202 @@ +/* + * /src/NTP/ntp4-dev/libparse/parsesolaris.c,v 4.11 2005/04/16 17:32:10 kardel RELEASE_20050508_A + * + * parsesolaris.c,v 4.11 2005/04/16 17:32:10 kardel RELEASE_20050508_A + * + * STREAMS module for reference clocks + * + * Copyright (c) 1995-2005 by Frank Kardel <kardel <AT> ntp.org> + * Copyright (c) 1989-1994 by Frank Kardel, Friedrich-Alexander Universität Erlangen-Nürnberg, Germany + * + * 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. + * 3. Neither the name of the author nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * 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. + * + */ + +#define _KERNEL /* it is a _KERNEL module */ + +#ifndef lint +static char rcsid[] = "parsesolaris.c,v 4.11 2005/04/16 17:32:10 kardel RELEASE_20050508_A"; +#endif + +#include <config.h> +#include <sys/types.h> +#include <sys/conf.h> +#include <sys/errno.h> +#include <sys/time.h> +#include <sys/termios.h> +#include <sys/stream.h> +#include <sys/strtty.h> +#include <sys/stropts.h> +#include <sys/modctl.h> +#include <sys/ddi.h> +#include <sys/sunddi.h> +#ifdef __GNUC__ /* makes it compile on Solaris 2.6 - acc doesn't like it -- GREAT! */ +#include <stdarg.h> +#endif + +#include "ntp_fp.h" +#include "parse.h" +#include <sys/parsestreams.h> + +/*--------------- loadable driver section -----------------------------*/ + +static struct streamtab parseinfo; + +static struct fmodsw fmod_templ = +{ + "parse", /* module name */ + &parseinfo, /* module information */ + D_NEW|D_MP|D_MTQPAIR, /* exclusive for q pair */ + /* lock ptr */ +}; + +extern struct mod_ops mod_strmodops; + +static struct modlstrmod modlstrmod = +{ + &mod_strmodops, /* a STREAMS module */ + "PARSE - NTP reference", /* name this baby - keep room for revision number */ + &fmod_templ +}; + +static struct modlinkage modlinkage = +{ + MODREV_1, + { + &modlstrmod, + NULL + } +}; + +/* + * module management routines + */ +/*ARGSUSED*/ +int +_init( + void + ) +{ + static char revision[] = "4.6"; + char *s, *S; + char *t; + +#ifndef lint + t = rcsid; +#endif + + /* + * 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 = modlstrmod.strmod_linkinfo; + 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)) + { + strlcpy(t, s, (unsigned)(S - s)); + } + } + return (mod_install(&modlinkage)); +} + +/*ARGSUSED*/ +int +_info( + struct modinfo *modinfop + ) +{ + return (mod_info(&modlinkage, modinfop)); +} + +/*ARGSUSED*/ +int +_fini( + void + ) +{ + if (mod_remove(&modlinkage) != DDI_SUCCESS) + { + return EBUSY; + } + else + return DDI_SUCCESS; +} + +/*--------------- stream module definition ----------------------------*/ + +static int parseopen (queue_t *, dev_t *, int, int, cred_t *); +static int parseclose (queue_t *, int); +static int parsewput (queue_t *, mblk_t *); +static int parserput (queue_t *, mblk_t *); +static int parsersvc (queue_t *); + +static struct module_info driverinfo = +{ + 0, /* module ID number */ + fmod_templ.f_name, /* module name - why repeated here ? compat ?*/ + 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 */ +}; + +static struct streamtab parseinfo = /* stream info element for parse driver */ +{ + &rinit, /* read queue */ + &winit, /* write queue */ + NULL, /* read mux */ + NULL /* write mux */ +}; + +/*--------------- 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 */ + +#ifdef PARSEDEBUG +int parsedebug = ~0; +#else +int parsedebug = 0; +#endif + +/*--------------- module implementation -----------------------------*/ + +#define TIMEVAL_USADD(_X_, _US_) do {\ + (_X_)->tv_usec += (_US_);\ + if ((_X_)->tv_usec >= 1000000)\ + {\ + (_X_)->tv_sec++;\ + (_X_)->tv_usec -= 1000000;\ + }\ + } while (0) + +static int init_linemon (queue_t *); +static void close_linemon (queue_t *, queue_t *); + +#define M_PARSE 0x0001 +#define M_NOPARSE 0x0002 + +void +ntp_memset( + char *a, + int x, + int c + ) +{ + while (c-- > 0) + *a++ = x; +} + +static void +pprintf( + int lev, + char *form, + ... + ) +{ + va_list ap; + + va_start(ap, form); + + if (lev & parsedebug) + vcmn_err(CE_CONT, form, ap); + + va_end(ap); +} + +static int +setup_stream( + queue_t *q, + int mode + ) +{ + register mblk_t *mp; + + pprintf(DD_OPEN,"parse: SETUP_STREAM - setting up stream for q=%x\n", q); + + mp = allocb(sizeof(struct stroptions), BPRI_MED); + if (mp) + { + struct stroptions *str = (void *)mp->b_wptr; + + str->so_flags = SO_READOPT|SO_HIWAT|SO_LOWAT|SO_ISNTTY; + 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); + if (!q) + panic("NULL q - strange"); + putnext(q, mp); + return putctl1(WR(q)->q_next, M_CTL, (mode == M_PARSE) ? MC_SERVICEIMM : + MC_SERVICEDEF); + } + else + { + pprintf(DD_OPEN, "parse: setup_stream - FAILED - no MEMORY for allocb\n"); + return 0; + } +} + +/*ARGSUSED*/ +static int +parseopen( + queue_t *q, + dev_t *dev, + int flag, + int sflag, + cred_t *credp + ) +{ + register parsestream_t *parse; + static int notice = 0; + + pprintf(DD_OPEN, "parse: OPEN - q=%x\n", q); + + if (sflag != MODOPEN) + { /* open only for modules */ + pprintf(DD_OPEN, "parse: OPEN - FAILED - not MODOPEN\n"); + return EIO; + } + + if (q->q_ptr != (caddr_t)NULL) + { + pprintf(DD_OPEN, "parse: OPEN - FAILED - EXCLUSIVE ONLY\n"); + return EBUSY; + } + + q->q_ptr = (caddr_t)kmem_alloc(sizeof(parsestream_t), KM_SLEEP); + if (q->q_ptr == (caddr_t)0) + { + return ENOMEM; + } + + pprintf(DD_OPEN, "parse: OPEN - parse area q=%x, q->q_ptr=%x\n", q, q->q_ptr); + WR(q)->q_ptr = q->q_ptr; + pprintf(DD_OPEN, "parse: OPEN - WQ parse area q=%x, q->q_ptr=%x\n", WR(q), WR(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; + + qprocson(q); + + pprintf(DD_OPEN, "parse: OPEN - initializing io subsystem q=%x\n", q); + + if (!parse_ioinit(&parse->parse_io)) + { + /* + * ok guys - beat it + */ + qprocsoff(q); + + kmem_free((caddr_t)parse, sizeof(parsestream_t)); + + return EIO; + } + + pprintf(DD_OPEN, "parse: OPEN - initializing stream q=%x\n", q); + + if (setup_stream(q, M_PARSE)) + { + (void) init_linemon(q); /* hook up PPS ISR routines if possible */ + pprintf(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) + { + cmn_err(CE_CONT, "?%s: Copyright (c) 1993-2005, Frank Kardel\n", modlstrmod.strmod_linkinfo); + notice = 1; + } + + return 0; + } + else + { + qprocsoff(q); + + kmem_free((caddr_t)parse, sizeof(parsestream_t)); + + return EIO; + } +} + +/*ARGSUSED*/ +static int +parseclose( + queue_t *q, + int flags + ) +{ + register parsestream_t *parse = (parsestream_t *)q->q_ptr; + register unsigned long s; + + pprintf(DD_CLOSE, "parse: CLOSE\n"); + + qprocsoff(q); + + 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; + + return 0; +} + +/* + * move unrecognized stuff upward + */ +static int +parsersvc( + queue_t *q + ) +{ + mblk_t *mp; + + while ((mp = getq(q))) + { + if (canputnext(q) || (mp->b_datap->db_type > QPCTL)) + { + putnext(q, mp); + pprintf(DD_RSVC, "parse: RSVC - putnext\n"); + } + else + { + putbq(q, mp); + pprintf(DD_RSVC, "parse: RSVC - flow control wait\n"); + break; + } + } + return 0; +} + +/* + * do ioctls and + * send stuff down - dont care about + * flow control + */ +static int +parsewput( + queue_t *q, + mblk_t *mp + ) +{ + register int ok = 1; + register mblk_t *datap; + register struct iocblk *iocp; + parsestream_t *parse = (parsestream_t *)q->q_ptr; + + pprintf(DD_WPUT, "parse: parsewput\n"); + + switch (mp->b_datap->db_type) + { + default: + putnext(q, mp); + break; + + case M_IOCTL: + iocp = (void *)mp->b_rptr; + switch (iocp->ioc_cmd) + { + default: + pprintf(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; + /* (void *) quiets cast alignment warning */ + *(struct ppsclockev *)(void *)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 & (unsigned)~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_TIMECODE: + case PARSEIOC_SETFMT: + case PARSEIOC_GETFMT: + case PARSEIOC_SETCS: + if (iocp->ioc_count == sizeof(parsectl_t)) + { + parsectl_t *dct = (void *)mp->b_cont->b_rptr; + + switch (iocp->ioc_cmd) + { + case PARSEIOC_TIMECODE: + pprintf(DD_WPUT, "parse: parsewput - PARSEIOC_TIMECODE\n"); + ok = parse_timecode(dct, &parse->parse_io); + break; + + case PARSEIOC_SETFMT: + pprintf(DD_WPUT, "parse: parsewput - PARSEIOC_SETFMT\n"); + ok = parse_setfmt(dct, &parse->parse_io); + break; + + case PARSEIOC_GETFMT: + pprintf(DD_WPUT, "parse: parsewput - PARSEIOC_GETFMT\n"); + ok = parse_getfmt(dct, &parse->parse_io); + break; + + case PARSEIOC_SETCS: + pprintf(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; + } + pprintf(DD_WPUT, "parse: parsewput qreply - %s\n", (mp->b_datap->db_type == M_IOCNAK) ? "M_IOCNAK" : "M_IOCACK"); + qreply(q, mp); + break; + } + } + return 0; +} + +/* + * read characters from streams buffers + */ +static unsigned long +rdchar( + 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 (unsigned long)~0; +} + +/* + * convert incoming data + */ +static int +parserput( + queue_t *q, + mblk_t *imp + ) +{ + register unsigned char type; + mblk_t *mp = imp; + + 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 + */ + pprintf(DD_RPUT, "parse: parserput - forward type 0x%x\n", type); + + if (canputnext(q) || (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 c_time; + timespec_t hres_time; + + /* + * get time on packet delivery + */ + gethrestime(&hres_time); + c_time.tv.tv_sec = hres_time.tv_sec; + c_time.tv.tv_usec = hres_time.tv_nsec / 1000; + + if (!(parse->parse_status & PARSE_ENABLE)) + { + pprintf(DD_RPUT, "parse: parserput - parser disabled - forward type 0x%x\n", type); + if (canputnext(q) || (mp->b_datap->db_type > QPCTL)) + { + putnext(q, mp); + } + else + putq(q, mp); + } + else + { + pprintf(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, (unsigned int)ch, &c_time)) + { + /* + * up up and away (hopefully ...) + * don't press it if resources are tight or nobody wants it + */ + nmp = (mblk_t *)NULL; + if (canputnext(parse->parse_queue) && (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, (unsigned int)0, &c_time)) + { + /* + * up up and away (hopefully ...) + * don't press it if resources are tight or nobody wants it + */ + nmp = (mblk_t *)NULL; + if (canputnext(parse->parse_queue) && (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 c_time; + timespec_t hres_time; + register mblk_t *nmp; + register int status = cd_invert ^ (type == M_UNHANGUP); + + gethrestime(&hres_time); + c_time.tv.tv_sec = hres_time.tv_sec; + c_time.tv.tv_usec = hres_time.tv_nsec / 1000; + + pprintf(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, &c_time)) + { + nmp = (mblk_t *)NULL; + if (canputnext(parse->parse_queue) && (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 (canputnext(q) || (mp->b_datap->db_type > QPCTL)) + { + putnext(q, mp); + } + else + putq(q, mp); + + if (status) + { + parse->parse_ppsclockev.tv = c_time.tv; + ++(parse->parse_ppsclockev.serial); + } + } + } + return 0; +} + +static int init_zs_linemon (queue_t *, queue_t *); /* handle line monitor for "zs" driver */ +static void close_zs_linemon (queue_t *, queue_t *); + +/*-------------------- CD isr status monitor ---------------*/ + +static int +init_linemon( + 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; + + pprintf(DD_INSTALL, "init_linemon: driver is \"%s\"\n", dname); + +#ifdef sun + if (dname && !strcmp(dname, "zs")) + { + return init_zs_linemon(dq, q); + } + else +#endif + { + pprintf(DD_INSTALL, "init_linemon: driver \"%s\" not suitable for CD monitoring\n", dname); + return 0; + } + } + pprintf(DD_INSTALL, "init_linemon: cannot find driver\n"); + return 0; +} + +static void +close_linemon( + queue_t *q, + 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; + } + pprintf(DD_INSTALL, "close_linemon: cannot find driver close routine for \"%s\"\n", dname); +#endif + } + pprintf(DD_INSTALL, "close_linemon: cannot find driver name\n"); +} + +#ifdef sun +#include <sys/tty.h> +#include <sys/zsdev.h> +#include <sys/ser_async.h> +#include <sys/ser_zscc.h> + +static void zs_xsisr (struct zscom *); /* zs external status interupt handler */ + +/* + * there should be some docs telling how to get to + * sz:zs_usec_delay and zs:initzsops() + */ +#define zs_usec_delay 5 + +struct savedzsops +{ + struct zsops zsops; + struct zsops *oldzsops; +}; + +static struct zsops *emergencyzs; + +static int +init_zs_linemon( + queue_t *q, + 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 asyncline *)q->q_ptr)->za_common; + if (!zs) + { + /* + * well - not found on startup - just say no (shouldn't happen though) + */ + return 0; + } + else + { + /* + * 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), KM_SLEEP); + + if (szs == (struct savedzsops *)0) + { + pprintf(DD_INSTALL, "init_zs_linemon: CD monitor NOT installed - no memory\n"); + + return 0; + } + else + { + parsestream->parse_data = (void *)szs; + + mutex_enter(zs->zs_excl); + + parsestream->parse_dqueue = q; /* remember driver */ + + szs->zsops = *zs->zs_ops; + szs->zsops.zsop_xsint = (void (*) (struct zscom *))zs_xsisr; /* place our bastard */ + szs->oldzsops = zs->zs_ops; + emergencyzs = zs->zs_ops; + + zs->zs_ops = &szs->zsops; /* hook it up */ + /* + * XXX: this is usually done via zsopinit() + * - have yet to find a way to call that routine + */ + zs->zs_xsint = (void (*) (struct zscom *))zs_xsisr; + + mutex_exit(zs->zs_excl); + + pprintf(DD_INSTALL, "init_zs_linemon: CD monitor installed\n"); + + return 1; + } + } +} + +/* + * unregister our ISR routine - must call under splhigh() (or + * whatever block ZS status interrupts) + */ +static void +close_zs_linemon( + queue_t *q, + queue_t *my_q + ) +{ + register struct zscom *zs; + register parsestream_t *parsestream = (parsestream_t *)my_q->q_ptr; + + zs = ((struct asyncline *)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; + + mutex_enter(zs->zs_excl); + + zs->zs_ops = szs->oldzsops; /* reset to previous handler functions */ + /* + * XXX: revert xsint (usually done via zsopinit() - have still to find + * a way to call that bugger + */ + zs->zs_xsint = zs->zs_ops->zsop_xsint; + + mutex_exit(zs->zs_excl); + + kmem_free((caddr_t)szs, sizeof (struct savedzsops)); + + pprintf(DD_INSTALL, "close_zs_linemon: CD monitor deleted\n"); + return; + } +} + +#define ZSRR0_IGNORE (ZSRR0_CD|ZSRR0_SYNC|ZSRR0_CTS) + +#define MAXDEPTH 50 /* maximum allowed stream crawl */ + +/* + * take external status interrupt (only CD interests us) + */ +static void +zs_xsisr( + struct zscom *zs + ) +{ + register struct asyncline *za = (void *)zs->zs_priv; + register queue_t *q; + register unsigned char zsstatus; + register int loopcheck; + register unsigned char cdstate; + register const char *dname = "-UNKNOWN-"; + timespec_t hres_time; + + /* + * pick up current state + */ + zsstatus = SCC_READ0(); + + if (za->za_rr0 ^ (cdstate = zsstatus & ZSRR0_CD)) + { + timestamp_t cdevent; + register int status; + + /* + * time stamp + */ + gethrestime(&hres_time); + cdevent.tv.tv_sec = hres_time.tv_sec; + cdevent.tv.tv_usec = hres_time.tv_nsec / 1000; + + q = za->za_ttycommon.t_readq; + + /* + * logical state + */ + status = cd_invert ? cdstate == 0 : cdstate != 0; + + /* + * 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); + } + + pprintf(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"); + } + } + + if (cdstate) /* fake CARRIER status - XXX currently not coordinated */ + za->za_flags |= ZAS_CARR_ON; + else + za->za_flags &= ~ZAS_CARR_ON; + + /* + * only pretend that CD and ignored transistion (SYNC,CTS) + * have been handled + */ + za->za_rr0 = (za->za_rr0 & ~ZSRR0_IGNORE) | (zsstatus & ZSRR0_IGNORE); + + if (((za->za_rr0 ^ zsstatus) & ~ZSRR0_IGNORE) == 0) + { + /* + * all done - kill status indication and return + */ + SCC_WRITE0(ZSWR0_RESET_STATUS); /* might kill other conditions here */ + return; + } + } + + pprintf(DD_ISR, "zs_xsisr: non CD event 0x%x for \"%s\"\n", + (za->za_rr0 ^ zsstatus) & ~ZSRR0_CD,dname); + /* + * 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 void (*zsisr) (struct zscom *); + + /* + * 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)) + zsisr(zs); + else + panic("zs_xsisr: unable to locate original ISR"); + + pprintf(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: + * + * parsesolaris.c,v + * Revision 4.11 2005/04/16 17:32:10 kardel + * update copyright + * + * Revision 4.10 2004/11/14 16:06:08 kardel + * update Id tags + * + * Revision 4.9 2004/11/14 15:29:41 kardel + * support PPSAPI, upgrade Copyright to Berkeley style + * + * Revision 4.6 1998/11/15 21:56:08 kardel + * ntp_memset not necessary + * + * Revision 4.5 1998/11/15 21:23:37 kardel + * ntp_memset() replicated in Sun kernel files + * + * Revision 4.4 1998/06/14 21:09:40 kardel + * Sun acc cleanup + * + * Revision 4.3 1998/06/13 12:14:59 kardel + * more prototypes + * fix name clashes + * allow for ansi2knr + * + * Revision 4.2 1998/06/12 15:23:08 kardel + * fix prototypes + * adjust for ansi2knr + * + * Revision 4.1 1998/05/24 09:38:46 kardel + * streams initiated iopps calls (M_xHANGUP) are now consistent with the + * respective calls from zs_xsisr() + * simulation of CARRIER status to avoid unecessary M_xHANGUP messages + * + * Revision 4.0 1998/04/10 19:45:38 kardel + * Start 4.0 release version numbering + * + * from V3 3.28 log info deleted 1998/04/11 kardel + */ |