1818eba70c
Reviewed by: delphij (suggested MFC) Approved by: roberto Security: CVE-2015-1798, CVE-2015-1799 Security: VuXML ebd84c96-dd7e-11e4-854e-3c970e169bc2 MFC after: 1 month
1368 lines
30 KiB
C
1368 lines
30 KiB
C
/*
|
|
* /src/NTP/ntp4-dev/libparse/parsestreams.c,v 4.11 2005/04/16 17:32:10 kardel RELEASE_20050508_A
|
|
*
|
|
* parsestreams.c,v 4.11 2005/04/16 17:32:10 kardel RELEASE_20050508_A
|
|
*
|
|
* STREAMS module for reference clocks
|
|
* (SunOS4.x)
|
|
*
|
|
* Copyright (c) 1995-2005 by Frank Kardel <kardel <AT> ntp.org>
|
|
* Copyright (c) 1989-1994 by Frank Kardel, Friedrich-Alexander Universitaet Erlangen-Nuernberg, 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 /* MUST */
|
|
#define VDDRV /* SHOULD */
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
# include "config.h"
|
|
#endif
|
|
|
|
#ifndef lint
|
|
static char rcsid[] = "parsestreams.c,v 4.11 2005/04/16 17:32:10 kardel RELEASE_20050508_A";
|
|
#endif
|
|
|
|
#ifndef KERNEL
|
|
#include "Bletch: MUST COMPILE WITH KERNEL DEFINE"
|
|
#endif
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/conf.h>
|
|
#include <sys/buf.h>
|
|
#include <sys/param.h>
|
|
#include <sys/sysmacros.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/tty.h>
|
|
|
|
#ifdef VDDRV
|
|
#include <sun/vddrv.h>
|
|
#endif
|
|
|
|
#include "ntp_stdlib.h"
|
|
#include "ntp_fp.h"
|
|
/*
|
|
* just make checking compilers more silent
|
|
*/
|
|
extern int printf (const char *, ...);
|
|
extern int putctl1 (queue_t *, int, int);
|
|
extern int canput (queue_t *);
|
|
extern void putbq (queue_t *, mblk_t *);
|
|
extern void freeb (mblk_t *);
|
|
extern void qreply (queue_t *, mblk_t *);
|
|
extern void freemsg (mblk_t *);
|
|
extern void panic (const char *, ...);
|
|
extern void usec_delay (int);
|
|
|
|
#include "parse.h"
|
|
#include "sys/parsestreams.h"
|
|
|
|
/*
|
|
* use microtime instead of uniqtime if advised to
|
|
*/
|
|
#ifdef MICROTIME
|
|
#define uniqtime microtime
|
|
#endif
|
|
|
|
#ifdef VDDRV
|
|
static unsigned int parsebusy = 0;
|
|
|
|
/*--------------- loadable driver section -----------------------------*/
|
|
|
|
extern struct streamtab parseinfo;
|
|
|
|
|
|
#ifdef PPS_SYNC
|
|
static char mnam[] = "PARSEPPS "; /* name this baby - keep room for revision number */
|
|
#else
|
|
static char mnam[] = "PARSE "; /* name this baby - keep room for revision number */
|
|
#endif
|
|
struct vdldrv parsesync_vd =
|
|
{
|
|
VDMAGIC_PSEUDO, /* nothing like a real driver - a STREAMS module */
|
|
mnam,
|
|
};
|
|
|
|
/*
|
|
* strings support usually not in kernel
|
|
*/
|
|
static int
|
|
Strlen(
|
|
register const char *s
|
|
)
|
|
{
|
|
register int c;
|
|
|
|
c = 0;
|
|
if (s)
|
|
{
|
|
while (*s++)
|
|
{
|
|
c++;
|
|
}
|
|
}
|
|
return c;
|
|
}
|
|
|
|
static void
|
|
Strncpy(
|
|
register char *t,
|
|
register char *s,
|
|
register int c
|
|
)
|
|
{
|
|
if (s && t)
|
|
{
|
|
while ((c-- > 0) && (*t++ = *s++))
|
|
;
|
|
}
|
|
}
|
|
|
|
static int
|
|
Strcmp(
|
|
register const char *s,
|
|
register const 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(
|
|
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;
|
|
}
|
|
|
|
void
|
|
ntp_memset(
|
|
char *a,
|
|
int x,
|
|
int c
|
|
)
|
|
{
|
|
while (c-- > 0)
|
|
*a++ = x;
|
|
}
|
|
|
|
/*
|
|
* driver init routine
|
|
* since no mechanism gets us into and out of the fmodsw, we have to
|
|
* do it ourselves
|
|
*/
|
|
/*ARGSUSED*/
|
|
int
|
|
xxxinit(
|
|
unsigned int fc,
|
|
struct vddrv *vdp,
|
|
addr_t vdin,
|
|
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[] = "4.7";
|
|
char *s, *S, *t;
|
|
|
|
s = rcsid; /* NOOP - keep compilers happy */
|
|
|
|
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 (queue_t *, dev_t, int, int);
|
|
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 char mn[] = "parse";
|
|
|
|
static struct module_info driverinfo =
|
|
{
|
|
0, /* module ID number */
|
|
mn, /* 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 (struct timeval *);
|
|
|
|
/*--------------- 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)
|
|
|
|
static int init_linemon (queue_t *);
|
|
static void close_linemon (queue_t *, queue_t *);
|
|
|
|
#define M_PARSE 0x0001
|
|
#define M_NOPARSE 0x0002
|
|
|
|
static int
|
|
setup_stream(
|
|
queue_t *q,
|
|
int mode
|
|
)
|
|
{
|
|
mblk_t *mp;
|
|
|
|
mp = allocb(sizeof(struct stroptions), BPRI_MED);
|
|
if (mp)
|
|
{
|
|
struct stroptions *str = (struct stroptions *)(void *)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(
|
|
queue_t *q,
|
|
dev_t dev,
|
|
int flag,
|
|
int sflag
|
|
)
|
|
{
|
|
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"));
|
|
#ifdef VDDRV
|
|
parsebusy--;
|
|
#endif
|
|
return OPENFAIL;
|
|
}
|
|
WR(q)->q_ptr = q->q_ptr;
|
|
|
|
parse = (parsestream_t *)(void *)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 */
|
|
|
|
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)
|
|
{
|
|
#ifdef VDDRV
|
|
printf("%s: Copyright (C) 1991-2005, Frank Kardel\n", parsesync_vd.Drv_name);
|
|
#else
|
|
printf("%s: Copyright (C) 1991-2005, Frank Kardel\n", "parsestreams.c,v 4.11 2005/04/16 17:32:10 kardel RELEASE_20050508_A");
|
|
#endif
|
|
notice = 1;
|
|
}
|
|
|
|
return MODOPEN;
|
|
}
|
|
else
|
|
{
|
|
kmem_free((caddr_t)parse, sizeof(parsestream_t));
|
|
|
|
#ifdef VDDRV
|
|
parsebusy--;
|
|
#endif
|
|
return OPENFAIL;
|
|
}
|
|
}
|
|
|
|
/*ARGSUSED*/
|
|
static int
|
|
parseclose(
|
|
queue_t *q,
|
|
int flags
|
|
)
|
|
{
|
|
register parsestream_t *parse = (parsestream_t *)(void *)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
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* move unrecognized stuff upward
|
|
*/
|
|
static int
|
|
parsersvc(
|
|
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;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* do ioctls and
|
|
* send stuff down - dont care about
|
|
* flow control
|
|
*/
|
|
static int
|
|
parsewput(
|
|
queue_t *q,
|
|
register mblk_t *mp
|
|
)
|
|
{
|
|
register int ok = 1;
|
|
register mblk_t *datap;
|
|
register struct iocblk *iocp;
|
|
parsestream_t *parse = (parsestream_t *)(void *)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 *)(void *)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 *)(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 = (parsectl_t *)(void *)mp->b_cont->b_rptr;
|
|
|
|
switch (iocp->ioc_cmd)
|
|
{
|
|
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;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* read characters from streams buffers
|
|
*/
|
|
static unsigned long
|
|
rdchar(
|
|
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 (unsigned)~0;
|
|
}
|
|
|
|
/*
|
|
* convert incoming data
|
|
*/
|
|
static int
|
|
parserput(
|
|
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 *)(void *)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, (unsigned int)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, (unsigned int)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 *)(void *)q->q_ptr;
|
|
timestamp_t ctime;
|
|
register mblk_t *nmp;
|
|
register int status = cd_invert ^ (type == M_UNHANGUP);
|
|
|
|
uniqtime(&ctime.tv);
|
|
|
|
parseprintf(DD_RPUT,("parse: parserput - M_%sHANGUP\n", (type == M_HANGUP) ? "" : "UN"));
|
|
|
|
if ((parse->parse_status & PARSE_ENABLE) &&
|
|
parse_iopps(&parse->parse_io, (int)(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);
|
|
}
|
|
}
|
|
}
|
|
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(
|
|
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(
|
|
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 <sundev/zsreg.h>
|
|
#include <sundev/zscom.h>
|
|
#include <sundev/zsvar.h>
|
|
|
|
static unsigned long cdmask = ZSRR0_CD;
|
|
|
|
struct savedzsops
|
|
{
|
|
struct zsops zsops;
|
|
struct zsops *oldzsops;
|
|
};
|
|
|
|
struct zsops *emergencyzs;
|
|
extern void zsopinit (struct zscom *, struct zsops *);
|
|
static int zs_xsisr (struct zscom *); /* zs external status interupt handler */
|
|
|
|
static int
|
|
init_zs_linemon(
|
|
register queue_t *q,
|
|
register queue_t *my_q
|
|
)
|
|
{
|
|
register struct zscom *zs;
|
|
register struct savedzsops *szs;
|
|
register parsestream_t *parsestream = (parsestream_t *)(void *)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 *)(void *)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 *)(void *)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 = 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(
|
|
register queue_t *q,
|
|
register queue_t *my_q
|
|
)
|
|
{
|
|
register struct zscom *zs;
|
|
register parsestream_t *parsestream = (parsestream_t *)(void *)my_q->q_ptr;
|
|
|
|
zs = ((struct zsaline *)(void *)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 void hardpps (struct timeval *, long);
|
|
#ifdef PPS_NEW
|
|
extern struct timeval timestamp;
|
|
#else
|
|
extern struct timeval pps_time;
|
|
#endif
|
|
#endif
|
|
|
|
/*
|
|
* take external status interrupt (only CD interests us)
|
|
*/
|
|
static int
|
|
zs_xsisr(
|
|
struct zscom *zs
|
|
)
|
|
{
|
|
register struct zsaline *za = (struct zsaline *)(void *)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 unsigned int s;
|
|
register long usec;
|
|
#endif
|
|
|
|
/*
|
|
* pick up current state
|
|
*/
|
|
zsstatus = zsaddr->zscc_control;
|
|
|
|
if ((za->za_rr0 ^ zsstatus) & (cdmask))
|
|
{
|
|
timestamp_t cdevent;
|
|
register int status;
|
|
|
|
za->za_rr0 = (za->za_rr0 & ~(cdmask)) | (zsstatus & (cdmask));
|
|
|
|
#ifdef PPS_SYNC
|
|
s = splclock();
|
|
#ifdef PPS_NEW
|
|
usec = timestamp.tv_usec;
|
|
#else
|
|
usec = pps_time.tv_usec;
|
|
#endif
|
|
#endif
|
|
/*
|
|
* time stamp
|
|
*/
|
|
uniqtime(&cdevent.tv);
|
|
|
|
#ifdef PPS_SYNC
|
|
(void)splx(s);
|
|
#endif
|
|
|
|
/*
|
|
* logical state
|
|
*/
|
|
status = cd_invert ? (zsstatus & cdmask) == 0 : (zsstatus & cdmask) != 0;
|
|
|
|
#ifdef PPS_SYNC
|
|
if (status)
|
|
{
|
|
usec = cdevent.tv.tv_usec - usec;
|
|
if (usec < 0)
|
|
usec += 1000000;
|
|
|
|
hardpps(&cdevent.tv, usec);
|
|
}
|
|
#endif
|
|
|
|
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 *)(void *)q->q_ptr)->parse_status & PARSE_ENABLE) &&
|
|
parse_iopps(&((parsestream_t *)(void *)q->q_ptr)->parse_io, (int)(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 *)(void *)q->q_ptr)->parse_io);
|
|
}
|
|
|
|
if (status)
|
|
{
|
|
((parsestream_t *)(void *)q->q_ptr)->parse_ppsclockev.tv = cdevent.tv;
|
|
++(((parsestream_t *)(void *)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
|
|
*/
|
|
ZSDELAY(2);
|
|
|
|
if (!((za->za_rr0 ^ zsstatus) & ~(cdmask)))
|
|
{
|
|
/*
|
|
* all done - kill status indication and return
|
|
*/
|
|
zsaddr->zscc_control = ZSWR0_RESET_STATUS; /* might kill other conditions here */
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
if (zsstatus & cdmask) /* fake CARRIER status */
|
|
za->za_flags |= ZAS_CARR_ON;
|
|
else
|
|
za->za_flags &= ~ZAS_CARR_ON;
|
|
|
|
/*
|
|
* 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 avoided 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) (struct zscom *);
|
|
|
|
/*
|
|
* back home - phew (hopping along stream queues might
|
|
* prove dangerous to your health)
|
|
*/
|
|
if ((zsisr = ((struct savedzsops *)((parsestream_t *)(void *)q->q_ptr)->parse_data)->oldzsops->zsop_xsint))
|
|
return 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 0;
|
|
}
|
|
}
|
|
|
|
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");
|
|
return 0;
|
|
}
|
|
#endif /* sun */
|
|
|
|
/*
|
|
* History:
|
|
*
|
|
* parsestreams.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.7 1999/11/28 09:13:53 kardel
|
|
* RECON_4_0_98F
|
|
*
|
|
* Revision 4.6 1998/12/20 23:45:31 kardel
|
|
* fix types and warnings
|
|
*
|
|
* Revision 4.5 1998/11/15 21:23:38 kardel
|
|
* ntp_memset() replicated in Sun kernel files
|
|
*
|
|
* Revision 4.4 1998/06/13 12:15:59 kardel
|
|
* superfluous variable removed
|
|
*
|
|
* Revision 4.3 1998/06/12 15:23:08 kardel
|
|
* fix prototypes
|
|
* adjust for ansi2knr
|
|
*
|
|
* Revision 4.2 1998/05/24 18:16:22 kardel
|
|
* moved copy of shadow status to the beginning
|
|
*
|
|
* Revision 4.1 1998/05/24 09:38:47 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.37 log info deleted 1998/04/11 kardel
|
|
*/
|