freebsd-dev/contrib/ntp/kernel/tty_chu.c
1999-12-09 13:01:21 +00:00

277 lines
5.9 KiB
C

/* tty_chu.c,v 3.1 1993/07/06 01:07:30 jbj Exp
* tty_chu.c - CHU line driver
*/
#include "chu.h"
#if NCHU > 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/chudefs.h"
/*
* Line discipline for receiving CHU time codes.
* Does elementary noise elimination, takes time stamps after
* the arrival of each character, returns a buffer full of the
* received 10 character code and the associated time stamps.
*/
#define NUMCHUBUFS 3
struct chudata {
u_char used; /* Set to 1 when structure in use */
u_char lastindex; /* least recently used buffer */
u_char curindex; /* buffer to use */
u_char sleeping; /* set to 1 when we're sleeping on a buffer */
struct chucode chubuf[NUMCHUBUFS];
} chu_data[NCHU];
/*
* Number of microseconds we allow between
* character arrivals. The speed is 300 baud
* so this should be somewhat more than 30 msec
*/
#define CHUMAXUSEC (50*1000) /* 50 msec */
int chu_debug = 0;
/*
* Open as CHU time discipline. Called when discipline changed
* with ioctl, and changes the interpretation of the information
* in the tty structure.
*/
/*ARGSUSED*/
chuopen(dev, tp)
dev_t dev;
register struct tty *tp;
{
register struct chudata *chu;
/*
* Don't allow multiple opens. This will also protect us
* from someone opening /dev/tty
*/
if (tp->t_line == CHULDISC)
return (EBUSY);
ttywflush(tp);
for (chu = chu_data; chu < &chu_data[NCHU]; chu++)
if (!chu->used)
break;
if (chu >= &chu[NCHU])
return (EBUSY);
chu->used++;
chu->lastindex = chu->curindex = 0;
chu->sleeping = 0;
chu->chubuf[0].ncodechars = 0;
tp->T_LINEP = (caddr_t) chu;
return (0);
}
/*
* Break down... called when discipline changed or from device
* close routine.
*/
chuclose(tp)
register struct tty *tp;
{
register int s = spl5();
((struct chudata *) tp->T_LINEP)->used = 0;
tp->t_cp = 0;
tp->t_inbuf = 0;
tp->t_rawq.c_cc = 0; /* clear queues -- paranoid */
tp->t_canq.c_cc = 0;
tp->t_line = 0; /* paranoid: avoid races */
splx(s);
}
/*
* Read a CHU buffer. Sleep on the current buffer
*/
churead(tp, uio)
register struct tty *tp;
struct uio *uio;
{
register struct chudata *chu;
register struct chucode *chucode;
register int s;
if ((tp->t_state&TS_CARR_ON)==0)
return (EIO);
chu = (struct chudata *) (tp->T_LINEP);
s = spl5();
chucode = &(chu->chubuf[chu->lastindex]);
while (chu->curindex == chu->lastindex) {
chu->sleeping = 1;
sleep((caddr_t)chucode, TTIPRI);
}
chu->sleeping = 0;
if (++(chu->lastindex) >= NUMCHUBUFS)
chu->lastindex = 0;
splx(s);
return (uiomove((caddr_t)chucode, sizeof(*chucode), UIO_READ, 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.
*/
chuinput(c, tp)
register int c;
register struct tty *tp;
{
register struct chudata *chu = (struct chudata *) tp->T_LINEP;
register struct chucode *chuc;
register int i;
long sec, usec;
struct timeval tv;
/*
* Do a check on the BSDness of the character. This delays
* the time stamp a bit but saves a fair amount of overhead
* when the static is bad.
*/
if (((c) & 0xf) > 9 || (((c)>>4) & 0xf) > 9) {
chuc = &(chu->chubuf[chu->curindex]);
chuc->ncodechars = 0; /* blow all previous away */
return;
}
/*
* Call microtime() to get the current time of day
*/
microtime(&tv);
/*
* Compute the difference in this character's time stamp
* and the last. If it exceeds the margin, blow away all
* the characters currently in the buffer.
*/
chuc = &(chu->chubuf[chu->curindex]);
i = (int)chuc->ncodechars;
if (i > 0) {
sec = tv.tv_sec - chuc->codetimes[i-1].tv_sec;
usec = tv.tv_usec - chuc->codetimes[i-1].tv_usec;
if (usec < 0) {
sec -= 1;
usec += 1000000;
}
if (sec != 0 || usec > CHUMAXUSEC) {
i = 0;
chuc->ncodechars = 0;
}
}
/*
* Store the character. If we're done, have to tell someone
*/
chuc->codechars[i] = (u_char)c;
chuc->codetimes[i] = tv;
if (++i < NCHUCHARS) {
/*
* Not much to do here. Save the count and wait
* for another character.
*/
chuc->ncodechars = (u_char)i;
} else {
/*
* Mark this buffer full and point at next. If the
* next buffer is full we overwrite it by bumping the
* next pointer.
*/
chuc->ncodechars = NCHUCHARS;
if (++(chu->curindex) >= NUMCHUBUFS)
chu->curindex = 0;
if (chu->curindex == chu->lastindex)
if (++(chu->lastindex) >= NUMCHUBUFS)
chu->lastindex = 0;
chu->chubuf[chu->curindex].ncodechars = 0;
/*
* Wake up anyone sleeping on this. Also wake up
* selectors and/or deliver a SIGIO as required.
*/
if (tp->t_rsel) {
selwakeup(tp->t_rsel, tp->t_state&TS_RCOLL);
tp->t_state &= ~TS_RCOLL;
tp->t_rsel = 0;
}
if (tp->t_state & TS_ASYNC)
gsignal(tp->t_pgrp, SIGIO);
if (chu->sleeping)
(void) wakeup((caddr_t)chuc);
}
}
/*
* Handle ioctls. We reject all tty-style except those that
* change the line discipline.
*/
chuioctl(tp, cmd, data, flag)
struct tty *tp;
int cmd;
caddr_t data;
int flag;
{
if ((cmd>>8) != 't')
return (-1);
switch (cmd) {
case TIOCSETD:
case TIOCGETD:
case TIOCGETP:
case TIOCGETC:
return (-1);
}
return (ENOTTY); /* not quite appropriate */
}
chuselect(dev, rw)
dev_t dev;
int rw;
{
register struct tty *tp = &cdevsw[major(dev)].d_ttys[minor(dev)];
struct chudata *chu;
int s = spl5();
chu = (struct chudata *) (tp->T_LINEP);
switch (rw) {
case FREAD:
if (chu->curindex != chu->lastindex)
goto win;
if (tp->t_rsel && tp->t_rsel->p_wchan == (caddr_t)&selwait)
tp->t_state |= TS_RCOLL;
else
tp->t_rsel = u.u_procp;
break;
case FWRITE:
goto win;
}
splx(s);
return (0);
win:
splx(s);
return (1);
}
#endif NCHU