320 lines
7.5 KiB
C
320 lines
7.5 KiB
C
/* $NetBSD: tty_clk.c,v 1.3 2003/12/04 16:23:36 drochner 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
|