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:
59: struct 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:
67: struct 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:
74: int clk_debug = 0;
75:
76: /*ARGSUSED*/
77: clkopen(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: */
107: clkclose(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: */
127: clkwrite(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: */
153: clkinput(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:
253: flushout:
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: */
267: clkioctl(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
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>