/* $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