This commit was generated by cvs2svn to track changes on a CVS vendor
[unix-history] / sys / kern / tty_clk.c
CommitLineData
c4794f91
GW
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 s = tv.tv_sec;
195 for (i = 0; i < sizeof(long); i++) {
196 if (putc((s >> 24) & 0xff, &clk->clkbuf) == -1)
197 goto flushout;
198 s <<= 8;
199 }
200
201 s = tv.tv_usec;
202 for (i = 0; i < sizeof(long); i++) {
203 if (putc((s >> 24) & 0xff, &clk->clkbuf) == -1)
204 goto flushout;
205 s <<= 8;
206 }
207
208 /*
209 * If the length of the rawq exceeds our sanity limit, dump
210 * all the old crap in there before copying this in.
211 */
212 if (tp->t_rawq.c_cc > NCLKCHARS)
213 ndflush(&tp->t_rawq, tp->t_rawq.c_cc);
214
215 /*
216 * Now copy the buffer in. There is a special case optimization
217 * here. If there is nothing on the rawq at present we can
218 * just copy the clists we own over. Otherwise we must concatenate
219 * the present data on the end.
220 */
221 s = (long)spltty();
222 if (tp->t_rawq.c_cc <= 0) {
223 tp->t_rawq = clk->clkbuf;
224 clk->clk_cc = 0;
225 clk->clk_cl = clk->clk_cf = NULL;
226 (void) splx((int)s);
227 } else {
228 (void) splx((int)s);
229 catq(&clk->clkbuf, &tp->t_rawq);
230 clk_bflush(clk);
231 }
232
233 /*
234 * Tell the world
235 */
236 ttwakeup(tp);
237 return;
238
239flushout:
240 /*
241 * It would be nice if this never happened. Flush the
242 * internal clists and hope someone else frees some of them
243 */
244 clk_bflush(clk);
245 return;
246}
247
248
249/*
250 * Handle ioctls. We reject most tty-style except those that
251 * change the line discipline and a couple of others..
252 */
253clkioctl(tp, cmd, data, flag)
254 struct tty *tp;
255 int cmd;
256 caddr_t data;
257 int flag;
258{
259 int flags;
260 struct sgttyb *sg;
261
262 if ((cmd>>8) != 't')
263 return (-1);
264 switch (cmd) {
265 case TIOCSETD:
266 case TIOCGETD:
267 case TIOCGETP:
268 case TIOCGETC:
269 case TIOCOUTQ:
270 return (-1);
271
272 case TIOCSETP:
273 /*
274 * He likely wants to set new magic characters in.
275 * Do this part.
276 */
277 sg = (struct sgttyb *)data;
278#ifdef ultrix
279 tp->t_cc[VERASE] = sg->sg_erase;
280 tp->t_cc[VKILL] = sg->sg_kill;
281#else
282 tp->t_erase = sg->sg_erase;
283 tp->t_kill = sg->sg_kill;
284#endif
285 return (0);
286
287 case TIOCFLUSH:
288 flags = *(int *)data;
289 if (flags == 0 || (flags & FREAD)) {
290 register struct clkdata *clk;
291
292 clk = (struct clkdata *) tp->T_LINEP;
293 if (clk->clk_cc > 0)
294 clk_bflush(clk);
295 }
296 return (-1);
297
298 default:
299 break;
300 }
301 return (ENOTTY); /* not quite appropriate */
302}
303#endif NCLK