#define _KERNEL
#ifndef lint
static char rcsid[] = "parsesolaris.c,v 4.6 1998/11/15 21:56:08 kardel RELEASE_19991128_A";
#endif
#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__
#include <stdarg.h>
#endif
#include "ntp_fp.h"
#include "parse.h"
#include <sys/parsestreams.h>
static struct streamtab parseinfo;
static struct fmodsw fmod_templ =
{
"parse",
&parseinfo,
D_NEW|D_MP|D_MTQPAIR,
};
extern struct mod_ops mod_strmodops;
static struct modlstrmod modlstrmod =
{
&mod_strmodops,
"PARSE - NTP reference",
&fmod_templ
};
static struct modlinkage modlinkage =
{
MODREV_1,
{
&modlstrmod,
NULL
}
};
int
_init(
void
)
{
static char revision[] = "4.6";
char *s, *S;
char *t;
#ifndef lint
t = rcsid;
#endif
s = revision;
if (*s == '$')
{
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))
{
(void) strncpy(t, s, (unsigned)(S - s));
}
}
return (mod_install(&modlinkage));
}
int
_info(
struct modinfo *modinfop
)
{
return (mod_info(&modlinkage, modinfop));
}
int
_fini(
void
)
{
if (mod_remove(&modlinkage) != DDI_SUCCESS)
{
return EBUSY;
}
else
return DDI_SUCCESS;
}
static int parseopen P((queue_t *, dev_t *, int, int, cred_t *));
static int parseclose P((queue_t *, int));
static int parsewput P((queue_t *, mblk_t *));
static int parserput P((queue_t *, mblk_t *));
static int parsersvc P((queue_t *));
static struct module_info driverinfo =
{
0,
fmod_templ.f_name,
0,
INFPSZ,
1,
0
};
static struct qinit rinit =
{
parserput,
parsersvc,
parseopen,
parseclose,
NULL,
&driverinfo,
NULL
};
static struct qinit winit =
{
parsewput,
NULL,
NULL,
NULL,
NULL,
&driverinfo,
NULL
};
static struct streamtab parseinfo =
{
&rinit,
&winit,
NULL,
NULL
};
int cd_invert = 1;
#ifdef PARSEDEBUG
int parsedebug = ~0;
#else
int parsedebug = 0;
#endif
#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 P((queue_t *));
static void close_linemon P((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,
const char *form,
...
)
{
va_list ap;
va_start(ap, form);
if (lev & parsedebug)
vcmn_err(CE_CONT, (char *)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 = (struct stroptions *)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;
}
}
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)
{
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))
{
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);
pprintf(DD_OPEN, "parse: OPEN - SUCCEEDED\n");
if (!notice)
{
cmn_err(CE_CONT, "?%s: Copyright (c) 1993-1998, Frank Kardel\n", modlstrmod.strmod_linkinfo);
notice = 1;
}
return 0;
}
else
{
qprocsoff(q);
kmem_free((caddr_t)parse, sizeof(parsestream_t));
return EIO;
}
}
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;
}
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;
}
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 = (struct iocblk *)mp->b_rptr;
switch (iocp->ioc_cmd)
{
default:
pprintf(DD_WPUT, "parse: parsewput - forward M_IOCTL\n");
putnext(q, mp);
break;
case CIOGETEV:
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 & (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 *)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;
}
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;
}
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:
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 ctime;
timespec_t hres_time;
gethrestime(&hres_time);
ctime.tv.tv_sec = hres_time.tv_sec;
ctime.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)
{
while (mp != (mblk_t *)NULL)
{
ch = rdchar(&mp);
if (ch != ~0 && parse_ioread(&parse->parse_io, (unsigned int)ch, &ctime))
{
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, &ctime))
{
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;
}
}
case M_HANGUP:
case M_UNHANGUP:
{
register parsestream_t * parse = (parsestream_t *)q->q_ptr;
timestamp_t ctime;
timespec_t hres_time;
register mblk_t *nmp;
register int status = cd_invert ^ (type == M_UNHANGUP);
gethrestime(&hres_time);
ctime.tv.tv_sec = hres_time.tv_sec;
ctime.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, &ctime))
{
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 = ctime.tv;
++(parse->parse_ppsclockev.serial);
}
}
}
return 0;
}
static int init_zs_linemon P((queue_t *, queue_t *));
static void close_zs_linemon P((queue_t *, queue_t *));
static int
init_linemon(
queue_t *q
)
{
register queue_t *dq;
dq = WR(q);
while (dq->q_next)
{
dq = dq->q_next;
}
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
)
{
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 P((struct zscom *));
#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;
zs = ((struct asyncline *)q->q_ptr)->za_common;
if (!zs)
{
return 0;
}
else
{
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;
szs->zsops = *zs->zs_ops;
szs->zsops.zsop_xsint = (void (*) P((struct zscom *)))zs_xsisr;
szs->oldzsops = zs->zs_ops;
emergencyzs = zs->zs_ops;
zs->zs_ops = &szs->zsops;
zs->zs_xsint = (void (*) P((struct zscom *)))zs_xsisr;
mutex_exit(zs->zs_excl);
pprintf(DD_INSTALL, "init_zs_linemon: CD monitor installed\n");
return 1;
}
}
}
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)
{
return;
}
else
{
register struct savedzsops *szs = (struct savedzsops *)parsestream->parse_data;
mutex_enter(zs->zs_excl);
zs->zs_ops = szs->oldzsops;
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
static void
zs_xsisr(
struct zscom *zs
)
{
register struct asyncline *za = (struct asyncline *)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;
zsstatus = SCC_READ0();
if (za->za_rr0 ^ (cdstate = zsstatus & ZSRR0_CD))
{
timestamp_t cdevent;
register int status;
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;
status = cd_invert ? cdstate == 0 : cdstate != 0;
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))
{
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))
{
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)
za->za_flags |= ZAS_CARR_ON;
else
za->za_flags &= ~ZAS_CARR_ON;
za->za_rr0 = (za->za_rr0 & ~ZSRR0_IGNORE) | (zsstatus & ZSRR0_IGNORE);
if (((za->za_rr0 ^ zsstatus) & ~ZSRR0_IGNORE) == 0)
{
SCC_WRITE0(ZSWR0_RESET_STATUS);
return;
}
}
pprintf(DD_ISR, "zs_xsisr: non CD event 0x%x for \"%s\"\n",
(za->za_rr0 ^ zsstatus) & ~ZSRR0_CD,dname);
dname = (char *) 0;
q = za->za_ttycommon.t_readq;
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))
{
register void (*zsisr) P((struct zscom *));
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);
return;
}
}
q = q->q_next;
if (!loopcheck--)
{
panic("zs_xsisr: STREAMS Queue corrupted - non CD event");
}
}
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