Initial import from FreeBSD RELENG_4:
[games.git] / contrib / ntp / kernel / tty_clk.c
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