1/* tty_clk.c,v 3.1 1993/07/06 01:07:33 jbj Exp
2 * tty_clk.c - Generic line driver for receiving radio clock timecodes
3 */
4
5#include "clk.h"
6#if NCLK > 0
7
8#include "../h/param.h"
9#include "../h/types.h"
10#include "../h/systm.h"
11#include "../h/dir.h"
12#include "../h/user.h"
13#include "../h/ioctl.h"
14#include "../h/tty.h"
15#include "../h/proc.h"
16#include "../h/file.h"
17#include "../h/conf.h"
18#include "../h/buf.h"
19#include "../h/uio.h"
20#include "../h/clist.h"
21
22/*
23 * This line discipline is intended to provide well performing
24 * generic support for the reception and time stamping of radio clock
25 * timecodes.  Most radio clock devices return a string where a
26 * particular character in the code (usually a \r) is on-time
27 * synchronized with the clock.  The idea here is to collect characters
28 * until (one of) the synchronization character(s) (we allow two) is seen.
29 * When the magic character arrives we take a timestamp by calling
30 * microtime() and insert the eight bytes of struct timeval into the
31 * buffer after the magic character.  We then wake up anyone waiting
32 * for the buffer and return the whole mess on the next read.
33 *
34 * To use this the calling program is expected to first open the
35 * port, and then to set the port into raw mode with the speed
36 * set appropriately with a TIOCSETP ioctl(), with the erase and kill
37 * characters set to those to be considered magic (yes, I know this
38 * is gross, but they were so convenient).  If only one character is
39 * magic you can set then both the same, or perhaps to the alternate
40 * parity versions of said character.  After getting all this set,
41 * change the line discipline to CLKLDISC and you are on your way.
42 *
43 * The only other bit of magic we do in here is to flush the receive
44 * buffers on writes if the CRMOD flag is set (hack, hack).
45 */
46
47/*
48 * We run this very much like a raw mode terminal, with the exception
49 * that we store up characters locally until we hit one of the
50 * magic ones and then dump it into the rawq all at once.  We keep
51 * the buffered data in clists since we can then often move it to
52 * the rawq without copying.  For sanity we limit the number of
53 * characters between specials, and the total number of characters
54 * before we flush the rawq, as follows.
55 */
56#define	CLKLINESIZE	(256)
57#define	NCLKCHARS	(CLKLINESIZE*4)
58
59struct clkdata {
60	int inuse;
61	struct clist clkbuf;
62};
63#define	clk_cc	clkbuf.c_cc
64#define	clk_cf	clkbuf.c_cf
65#define	clk_cl	clkbuf.c_cl
66
67struct clkdata clk_data[NCLK];
68
69/*
70 * Routine for flushing the internal clist
71 */
72#define	clk_bflush(clk)		(ndflush(&((clk)->clkbuf), (clk)->clk_cc))
73
74int clk_debug = 0;
75
76/*ARGSUSED*/
77clkopen(dev, tp)
78	dev_t dev;
79	register struct tty *tp;
80{
81	register struct clkdata *clk;
82
83	/*
84	 * Don't allow multiple opens.  This will also protect us
85	 * from someone opening /dev/tty
86	 */
87	if (tp->t_line == CLKLDISC)
88		return (EBUSY);
89	ttywflush(tp);
90	for (clk = clk_data; clk < &clk_data[NCLK]; clk++)
91		if (!clk->inuse)
92			break;
93	if (clk >= &clk_data[NCLK])
94		return (EBUSY);
95	clk->inuse++;
96	clk->clk_cc = 0;
97	clk->clk_cf = clk->clk_cl = NULL;
98	tp->T_LINEP = (caddr_t) clk;
99	return (0);
100}
101
102
103/*
104 * Break down... called when discipline changed or from device
105 * close routine.
106 */
107clkclose(tp)
108	register struct tty *tp;
109{
110	register struct clkdata *clk;
111	register int s = spltty();
112
113	clk = (struct clkdata *)tp->T_LINEP;
114	if (clk->clk_cc > 0)
115		clk_bflush(clk);
116	clk->inuse = 0;
117	tp->t_line = 0;			/* paranoid: avoid races */
118	splx(s);
119}
120
121
122/*
123 * Receive a write request.  We pass these requests on to the terminal
124 * driver, except that if the CRMOD bit is set in the flags we
125 * first flush the input queues.
126 */
127clkwrite(tp, uio)
128	register struct tty *tp;
129	struct uio *uio;
130{
131	if (tp->t_flags & CRMOD) {
132		register struct clkdata *clk;
133		int s;
134
135		s = spltty();
136		if (tp->t_rawq.c_cc > 0)
137			ndflush(&tp->t_rawq, tp->t_rawq.c_cc);
138		clk = (struct clkdata *) tp->T_LINEP;
139		if (clk->clk_cc > 0)
140			clk_bflush(clk);
141		(void)splx(s);
142	}
143	ttwrite(tp, uio);
144}
145
146
147/*
148 * Low level character input routine.
149 * If the character looks okay, grab a time stamp.  If the stuff in
150 * the buffer is too old, dump it and start fresh.  If the character is
151 * non-BCDish, everything in the buffer too.
152 */
153clkinput(c, tp)
154	register int c;
155	register struct tty *tp;
156{
157	register struct clkdata *clk;
158	register int i;
159	register long s;
160	struct timeval tv;
161
162	/*
163	 * Check to see whether this isn't the magic character.  If not,
164	 * save the character and return.
165	 */
166#ifdef ultrix
167	if (c != tp->t_cc[VERASE] && c != tp->t_cc[VKILL]) {
168#else
169	if (c != tp->t_erase && c != tp->t_kill) {
170#endif
171		clk = (struct clkdata *) tp->T_LINEP;
172		if (clk->clk_cc >= CLKLINESIZE)
173			clk_bflush(clk);
174		if (putc(c, &clk->clkbuf) == -1) {
175			/*
176			 * Hopeless, no clists.  Flush what we have
177			 * and hope things improve.
178			 */
179			clk_bflush(clk);
180		}
181		return;
182	}
183
184	/*
185	 * Here we have a magic character.  Get a timestamp and store
186	 * everything.
187	 */
188	microtime(&tv);
189	clk = (struct clkdata *) tp->T_LINEP;
190
191	if (putc(c, &clk->clkbuf) == -1)
192		goto flushout;
193
194#ifdef CLKLDISC
195	/*
196	 * STREAMS people started writing timestamps this way.
197	 * It's not my fault, I am just going along with the flow...
198	 */
199	for (i = 0; i < sizeof(struct timeval); i++)
200		if (putc(*( ((char*)&tv) + i ), &clk->clkbuf) == -1)
201			goto flushout;
202#else
203	/*
204	 * This is a machine independant way of puting longs into
205	 * the datastream.  It has fallen into disuse...
206	 */
207	s = tv.tv_sec;
208	for (i = 0; i < sizeof(long); i++) {
209		if (putc((s >> 24) & 0xff, &clk->clkbuf) == -1)
210			goto flushout;
211		s <<= 8;
212	}
213
214	s = tv.tv_usec;
215	for (i = 0; i < sizeof(long); i++) {
216		if (putc((s >> 24) & 0xff, &clk->clkbuf) == -1)
217			goto flushout;
218		s <<= 8;
219	}
220#endif
221
222	/*
223	 * If the length of the rawq exceeds our sanity limit, dump
224	 * all the old crap in there before copying this in.
225	 */
226	if (tp->t_rawq.c_cc > NCLKCHARS)
227		ndflush(&tp->t_rawq, tp->t_rawq.c_cc);
228
229	/*
230	 * Now copy the buffer in.  There is a special case optimization
231	 * here.  If there is nothing on the rawq at present we can
232	 * just copy the clists we own over.  Otherwise we must concatenate
233	 * the present data on the end.
234	 */
235	s = (long)spltty();
236	if (tp->t_rawq.c_cc <= 0) {
237		tp->t_rawq = clk->clkbuf;
238		clk->clk_cc = 0;
239		clk->clk_cl = clk->clk_cf = NULL;
240		(void) splx((int)s);
241	} else {
242		(void) splx((int)s);
243		catq(&clk->clkbuf, &tp->t_rawq);
244		clk_bflush(clk);
245	}
246
247	/*
248	 * Tell the world
249	 */
250	ttwakeup(tp);
251	return;
252
253flushout:
254	/*
255	 * It would be nice if this never happened.  Flush the
256	 * internal clists and hope someone else frees some of them
257	 */
258	clk_bflush(clk);
259	return;
260}
261
262
263/*
264 * Handle ioctls.  We reject most tty-style except those that
265 * change the line discipline and a couple of others..
266 */
267clkioctl(tp, cmd, data, flag)
268	struct tty *tp;
269	int cmd;
270	caddr_t data;
271	int flag;
272{
273	int flags;
274	struct sgttyb *sg;
275
276	if ((cmd>>8) != 't')
277		return (-1);
278	switch (cmd) {
279	case TIOCSETD:
280	case TIOCGETD:
281	case TIOCGETP:
282	case TIOCGETC:
283	case TIOCOUTQ:
284		return (-1);
285
286	case TIOCSETP:
287		/*
288		 * He likely wants to set new magic characters in.
289		 * Do this part.
290		 */
291		sg = (struct sgttyb *)data;
292#ifdef ultrix
293		tp->t_cc[VERASE] = sg->sg_erase;
294		tp->t_cc[VKILL] = sg->sg_kill;
295#else
296		tp->t_erase = sg->sg_erase;
297		tp->t_kill = sg->sg_kill;
298#endif
299		return (0);
300
301	case TIOCFLUSH:
302		flags = *(int *)data;
303		if (flags == 0 || (flags & FREAD)) {
304			register struct clkdata *clk;
305
306			clk = (struct clkdata *) tp->T_LINEP;
307			if (clk->clk_cc > 0)
308				clk_bflush(clk);
309		}
310		return (-1);
311
312	default:
313		break;
314	}
315	return (ENOTTY);	/* not quite appropriate */
316}
317#endif NCLK
318