NetBSD/dist/ntp/kernel/tty_clk.c

320 lines
7.5 KiB
C

/* $NetBSD: tty_clk.c,v 1.2 2002/05/16 19:41:36 wiz Exp $ */
/* tty_clk.c,v 3.1 1993/07/06 01:07:33 jbj Exp
* tty_clk.c - Generic line driver for receiving radio clock timecodes
*/
#include "clk.h"
#if NCLK > 0
#include "../h/param.h"
#include "../h/types.h"
#include "../h/systm.h"
#include "../h/dir.h"
#include "../h/user.h"
#include "../h/ioctl.h"
#include "../h/tty.h"
#include "../h/proc.h"
#include "../h/file.h"
#include "../h/conf.h"
#include "../h/buf.h"
#include "../h/uio.h"
#include "../h/clist.h"
/*
* This line discipline is intended to provide well performing
* generic support for the reception and time stamping of radio clock
* timecodes. Most radio clock devices return a string where a
* particular character in the code (usually a \r) is on-time
* synchronized with the clock. The idea here is to collect characters
* until (one of) the synchronization character(s) (we allow two) is seen.
* When the magic character arrives we take a timestamp by calling
* microtime() and insert the eight bytes of struct timeval into the
* buffer after the magic character. We then wake up anyone waiting
* for the buffer and return the whole mess on the next read.
*
* To use this the calling program is expected to first open the
* port, and then to set the port into raw mode with the speed
* set appropriately with a TIOCSETP ioctl(), with the erase and kill
* characters set to those to be considered magic (yes, I know this
* is gross, but they were so convenient). If only one character is
* magic you can set then both the same, or perhaps to the alternate
* parity versions of said character. After getting all this set,
* change the line discipline to CLKLDISC and you are on your way.
*
* The only other bit of magic we do in here is to flush the receive
* buffers on writes if the CRMOD flag is set (hack, hack).
*/
/*
* We run this very much like a raw mode terminal, with the exception
* that we store up characters locally until we hit one of the
* magic ones and then dump it into the rawq all at once. We keep
* the buffered data in clists since we can then often move it to
* the rawq without copying. For sanity we limit the number of
* characters between specials, and the total number of characters
* before we flush the rawq, as follows.
*/
#define CLKLINESIZE (256)
#define NCLKCHARS (CLKLINESIZE*4)
struct clkdata {
int inuse;
struct clist clkbuf;
};
#define clk_cc clkbuf.c_cc
#define clk_cf clkbuf.c_cf
#define clk_cl clkbuf.c_cl
struct clkdata clk_data[NCLK];
/*
* Routine for flushing the internal clist
*/
#define clk_bflush(clk) (ndflush(&((clk)->clkbuf), (clk)->clk_cc))
int clk_debug = 0;
/*ARGSUSED*/
clkopen(dev, tp)
dev_t dev;
register struct tty *tp;
{
register struct clkdata *clk;
/*
* Don't allow multiple opens. This will also protect us
* from someone opening /dev/tty
*/
if (tp->t_line == CLKLDISC)
return (EBUSY);
ttywflush(tp);
for (clk = clk_data; clk < &clk_data[NCLK]; clk++)
if (!clk->inuse)
break;
if (clk >= &clk_data[NCLK])
return (EBUSY);
clk->inuse++;
clk->clk_cc = 0;
clk->clk_cf = clk->clk_cl = NULL;
tp->T_LINEP = (caddr_t) clk;
return (0);
}
/*
* Break down... called when discipline changed or from device
* close routine.
*/
clkclose(tp)
register struct tty *tp;
{
register struct clkdata *clk;
register int s = spltty();
clk = (struct clkdata *)tp->T_LINEP;
if (clk->clk_cc > 0)
clk_bflush(clk);
clk->inuse = 0;
tp->t_line = 0; /* paranoid: avoid races */
splx(s);
}
/*
* Receive a write request. We pass these requests on to the terminal
* driver, except that if the CRMOD bit is set in the flags we
* first flush the input queues.
*/
clkwrite(tp, uio)
register struct tty *tp;
struct uio *uio;
{
if (tp->t_flags & CRMOD) {
register struct clkdata *clk;
int s;
s = spltty();
if (tp->t_rawq.c_cc > 0)
ndflush(&tp->t_rawq, tp->t_rawq.c_cc);
clk = (struct clkdata *) tp->T_LINEP;
if (clk->clk_cc > 0)
clk_bflush(clk);
(void)splx(s);
}
ttwrite(tp, uio);
}
/*
* Low level character input routine.
* If the character looks okay, grab a time stamp. If the stuff in
* the buffer is too old, dump it and start fresh. If the character is
* non-BCDish, everything in the buffer too.
*/
clkinput(c, tp)
register int c;
register struct tty *tp;
{
register struct clkdata *clk;
register int i;
register long s;
struct timeval tv;
/*
* Check to see whether this isn't the magic character. If not,
* save the character and return.
*/
#ifdef ultrix
if (c != tp->t_cc[VERASE] && c != tp->t_cc[VKILL]) {
#else
if (c != tp->t_erase && c != tp->t_kill) {
#endif
clk = (struct clkdata *) tp->T_LINEP;
if (clk->clk_cc >= CLKLINESIZE)
clk_bflush(clk);
if (putc(c, &clk->clkbuf) == -1) {
/*
* Hopeless, no clists. Flush what we have
* and hope things improve.
*/
clk_bflush(clk);
}
return;
}
/*
* Here we have a magic character. Get a timestamp and store
* everything.
*/
microtime(&tv);
clk = (struct clkdata *) tp->T_LINEP;
if (putc(c, &clk->clkbuf) == -1)
goto flushout;
#ifdef CLKLDISC
/*
* STREAMS people started writing timestamps this way.
* It's not my fault, I am just going along with the flow...
*/
for (i = 0; i < sizeof(struct timeval); i++)
if (putc(*( ((char*)&tv) + i ), &clk->clkbuf) == -1)
goto flushout;
#else
/*
* This is a machine independant way of puting longs into
* the datastream. It has fallen into disuse...
*/
s = tv.tv_sec;
for (i = 0; i < sizeof(long); i++) {
if (putc((s >> 24) & 0xff, &clk->clkbuf) == -1)
goto flushout;
s <<= 8;
}
s = tv.tv_usec;
for (i = 0; i < sizeof(long); i++) {
if (putc((s >> 24) & 0xff, &clk->clkbuf) == -1)
goto flushout;
s <<= 8;
}
#endif
/*
* If the length of the rawq exceeds our sanity limit, dump
* all the old crap in there before copying this in.
*/
if (tp->t_rawq.c_cc > NCLKCHARS)
ndflush(&tp->t_rawq, tp->t_rawq.c_cc);
/*
* Now copy the buffer in. There is a special case optimization
* here. If there is nothing on the rawq at present we can
* just copy the clists we own over. Otherwise we must concatenate
* the present data on the end.
*/
s = (long)spltty();
if (tp->t_rawq.c_cc <= 0) {
tp->t_rawq = clk->clkbuf;
clk->clk_cc = 0;
clk->clk_cl = clk->clk_cf = NULL;
(void) splx((int)s);
} else {
(void) splx((int)s);
catq(&clk->clkbuf, &tp->t_rawq);
clk_bflush(clk);
}
/*
* Tell the world
*/
ttwakeup(tp);
return;
flushout:
/*
* It would be nice if this never happened. Flush the
* internal clists and hope someone else frees some of them
*/
clk_bflush(clk);
return;
}
/*
* Handle ioctls. We reject most tty-style except those that
* change the line discipline and a couple of others..
*/
clkioctl(tp, cmd, data, flag)
struct tty *tp;
int cmd;
caddr_t data;
int flag;
{
int flags;
struct sgttyb *sg;
if ((cmd>>8) != 't')
return (-1);
switch (cmd) {
case TIOCSETD:
case TIOCGETD:
case TIOCGETP:
case TIOCGETC:
case TIOCOUTQ:
return (-1);
case TIOCSETP:
/*
* He likely wants to set new magic characters in.
* Do this part.
*/
sg = (struct sgttyb *)data;
#ifdef ultrix
tp->t_cc[VERASE] = sg->sg_erase;
tp->t_cc[VKILL] = sg->sg_kill;
#else
tp->t_erase = sg->sg_erase;
tp->t_kill = sg->sg_kill;
#endif
return (0);
case TIOCFLUSH:
flags = *(int *)data;
if (flags == 0 || (flags & FREAD)) {
register struct clkdata *clk;
clk = (struct clkdata *) tp->T_LINEP;
if (clk->clk_cc > 0)
clk_bflush(clk);
}
return (-1);
default:
break;
}
return (ENOTTY); /* not quite appropriate */
}
#endif /* NCLK */