Add diclaimer of copyright to _osname() manual page.
[unix-history] / contrib / xntpd / kernel / tty_chu_STREAMS.c
CommitLineData
98771864
GW
1/*
2 * CHU STREAMS module for SunOS
09169146 3 *
98771864 4 * Version 2.3
09169146 5 *
98771864 6 * Copyright 1991-1994, Nick Sayer
09169146
GW
7 *
8 * Special thanks to Greg Onufer for his debug assists.
98771864 9 * Special thanks to Matthias Urlichs for the 4.1.x loadable driver support
09169146 10 * code.
98771864
GW
11 * Special wet-noodle whippings to Sun for not properly documenting
12 * ANYTHING that makes this stuff at all possible.
09169146
GW
13 *
14 * Should be PUSHed directly on top of a serial I/O channel.
15 * Provides complete chucode structures to user space.
16 *
17 * COMPILATION:
18 *
98771864 19 *
09169146
GW
20 * To make a SunOS 4.1.x compatable loadable module (from the ntp kernel
21 * directory):
22 *
23 * % cc -c -I../include -DLOADABLE tty_chu_STREAMS.c
24 *
25 * The resulting .o file is the loadable module. Modload it
98771864
GW
26 * thusly:
27 *
28 * % modload tty_chu_STREAMS.o -entry _chuinit
29 *
30 * When none of the instances are pushed in a STREAM, you can
31 * modunload the driver in the usual manner if you wish.
32 *
33 * As an alternative to loading it dynamically you can compile it
34 * directly into the kernel by hacking str_conf.c. See the README
35 * file for more details on doing it the old fashioned way.
09169146 36 *
09169146 37 *
98771864
GW
38 * To make a Solaris 2.x compatable module (from the ntp kernel
39 * directory):
40 *
41 * % {gcc,cc} -c -I../include -DSOLARIS2 tty_chu_STREAMS.c
42 * % ld -r -o /usr/kernel/strmod/chu tty_chu_STREAMS.o
43 * % chmod 755 /usr/kernel/strmod/chu
44 *
45 * The OS will load it for you automagically when it is first pushed.
09169146 46 *
09169146
GW
47 *
48 * HISTORY:
49 *
98771864
GW
50 * v2.3 - Added support for Solaris 2.x.
51 * v2.2 - Added SERVICE IMMEDIATE hack.
09169146
GW
52 * v2.1 - Added 'sixth byte' heuristics.
53 * v2.0 - first version with an actual version number.
54 * Added support for new CHU 'second 31' data format.
55 * Deleted PEDANTIC and ANAL_RETENTIVE.
56 *
57 */
58
98771864
GW
59#ifdef SOLARIS2
60# ifndef NCHU
61# define NCHU 3
62# define _KERNEL
63# endif
64#elif defined(LOADABLE)
09169146
GW
65# ifndef NCHU
66# define NCHU 3
67# define KERNEL
68# endif
98771864
GW
69#else
70# include "chu.h"
09169146
GW
71#endif
72
73#if NCHU > 0
74
75/*
76 * Number of microseconds we allow between
77 * character arrivals. The speed is 300 baud
78 * so this should be somewhat more than 30 msec
79 */
80#define CHUMAXUSEC (60*1000) /* 60 msec */
81
82#include <sys/types.h>
83#include <sys/stream.h>
84#include <sys/param.h>
85#include <sys/time.h>
09169146
GW
86#include <sys/errno.h>
87#include <sys/user.h>
98771864
GW
88#include <syslog.h>
89#include <sys/tty.h>
09169146
GW
90
91#include <sys/chudefs.h>
92
98771864
GW
93#ifdef SOLARIS2
94
95#include <sys/conf.h>
96#include <sys/strtty.h>
97#include <sys/modctl.h>
98#include <sys/ddi.h>
99#include <sys/sunddi.h>
100
101#endif
102
103#ifdef LOADABLE
104
105#include <sys/kernel.h>
106#include <sys/conf.h>
107#include <sys/buf.h>
108#include <sundev/mbvar.h>
109#include <sun/autoconf.h>
110#include <sun/vddrv.h>
111
112#endif
113
114
09169146
GW
115static struct module_info rminfo = { 0, "chu", 0, INFPSZ, 0, 0 };
116static struct module_info wminfo = { 0, "chu", 0, INFPSZ, 0, 0 };
117static int chuopen(), churput(), chuwput(), chuclose();
118
119static struct qinit rinit = { churput, NULL, chuopen, chuclose, NULL,
120 &rminfo, NULL };
121
122static struct qinit winit = { chuwput, NULL, NULL, NULL, NULL,
123 &wminfo, NULL };
124
125struct streamtab chuinfo = { &rinit, &winit, NULL, NULL };
126
127/*
128 * Here's our private data type and structs
129 */
130struct priv_data
131{
132 char in_use;
133 struct chucode chu_struct;
134} our_priv_data[NCHU];
135
98771864
GW
136#ifdef SOLARIS2
137
138static struct fmodsw fsw =
139{
140 "chu",
141 &chuinfo,
142 D_NEW
143};
144
145extern struct mod_ops mod_strmodops;
146
147static struct modlstrmod modlstrmod =
148{
149 &mod_strmodops,
150 "CHU timecode decoder v2.3",
151 &fsw
152};
153
154static struct modlinkage modlinkage =
155{
156 MODREV_1,
157 (void*) &modlstrmod,
158 NULL
159};
160
161int _init()
162{
163 int i;
164
165 for (i=0; i<NCHU; i++)
166 our_priv_data[i].in_use=0;
167
168 return mod_install(&modlinkage);
169}
170
171int _info(foo)
172struct modinfo *foo;
173{
174 return mod_info(&modlinkage,foo);
175}
176
177int _fini()
178{
179 int dev;
180
181 for (dev = 0; dev < NCHU; dev++)
182 if (our_priv_data[dev].in_use)
183 {
184 /* One of the modules is still open */
185 /* This is likely supposed to be impossible under Solaris 2.x */
186 return (EBUSY);
187 }
188
189 return mod_remove(&modlinkage);
190}
191
192#endif /* SOLARIS2 */
193
09169146
GW
194#ifdef LOADABLE
195
98771864 196# ifdef sun
09169146
GW
197
198static struct vdldrv vd =
199{
200 VDMAGIC_PSEUDO,
201 "chu",
202 NULL, NULL, NULL, 0, 0, NULL, NULL, 0, 0,
203};
204
205static struct fmodsw *chu_fmod;
206
207/*ARGSUSED*/
208chuinit (fc, vdp, vdi, vds)
209 unsigned int fc;
210 struct vddrv *vdp;
211 addr_t vdi;
212 struct vdstat *vds;
213{
214 switch (fc) {
215 case VDLOAD:
216 {
217 int dev, i;
218
219 /* Find free entry in fmodsw */
220 for (dev = 0; dev < fmodcnt; dev++) {
221 if (fmodsw[dev].f_str == NULL)
222 break;
223 }
224 if (dev == fmodcnt)
225 return (ENODEV);
226 chu_fmod = &fmodsw[dev];
227
228 /* If you think a kernel would have strcpy() you're mistaken. */
229 for (i = 0; i <= FMNAMESZ; i++)
230 chu_fmod->f_name[i] = wminfo.mi_idname[i];
231
232 chu_fmod->f_str = &chuinfo;
233 }
234 vdp->vdd_vdtab = (struct vdlinkage *) & vd;
235
236 {
237 int i;
238
239 for (i=0; i<NCHU; i++)
240 our_priv_data[i].in_use=0;
241 }
242
243 return 0;
244 case VDUNLOAD:
245 {
246 int dev;
247
248 for (dev = 0; dev < NCHU; dev++)
249 if (our_priv_data[dev].in_use) {
250 /* One of the modules is still open */
251 return (EBUSY);
252 }
253 }
254 chu_fmod->f_name[0] = '\0';
255 chu_fmod->f_str = NULL;
256 return 0;
257 case VDSTAT:
258 return 0;
259 default:
260 return EIO;
261 }
262}
263
98771864 264# endif /* sun */
09169146 265
98771864
GW
266#endif /* LOADABLE */
267
268#if !defined(LOADABLE) && !defined(SOLARIS2)
09169146
GW
269
270char chu_first_open=1;
271
272#endif
273
274/*ARGSUSED*/
275static int chuopen(q, dev, flag, sflag)
276queue_t *q;
277dev_t dev;
278int flag;
279int sflag;
280{
281 int i;
282
98771864 283#if !defined(LOADABLE) && !defined(SOLARIS2)
09169146
GW
284 if (chu_first_open)
285 {
286 chu_first_open=0;
287
288 for(i=0;i<NCHU;i++)
289 our_priv_data[i].in_use=0;
290 }
291#endif
292
293 for(i=0;i<NCHU;i++)
294 if (!our_priv_data[i].in_use)
295 {
296 ((struct priv_data *) (q->q_ptr))=&(our_priv_data[i]);
297 our_priv_data[i].in_use++;
298 our_priv_data[i].chu_struct.ncodechars = 0;
98771864
GW
299 if (!putctl1(WR(q)->q_next, M_CTL, MC_SERVICEIMM))
300 {
301 our_priv_data[i].in_use=0;
302#ifdef SOLARIS2
303 return (EFAULT);
304#else
305 u.u_error = EFAULT;
306 return (OPENFAIL);
307#endif
308 }
09169146
GW
309 return 0;
310 }
311
98771864
GW
312#ifdef SOLARIS2
313 return (EBUSY);
314#else
09169146
GW
315 u.u_error = EBUSY;
316 return (OPENFAIL);
98771864 317#endif
09169146
GW
318
319}
320
321/*ARGSUSED*/
322static int chuclose(q, flag)
323queue_t *q;
324int flag;
325{
326 ((struct priv_data *) (q->q_ptr))->in_use=0;
327
328 return (0);
329}
330
331/*
332 * Now the crux of the biscuit.
333 *
334 * We will be passed data from the man downstairs. If it's not a data
335 * packet, it must be important, so pass it along unmunged. If, however,
336 * it is a data packet, we're gonna do special stuff to it. We're going
337 * to pass each character we get to the old line discipline code we
338 * include below for just such an occasion. When the old ldisc code
339 * gets a full chucode struct, we'll hand it back upstairs.
340 *
341 * chuinput takes a single character and q (as quickly as possible).
342 * passback takes a pointer to a chucode struct and q and sends it upstream.
343 */
344
345void chuinput();
346void passback();
347
348static int churput(q, mp)
349queue_t *q;
350mblk_t *mp;
351{
352 mblk_t *bp;
353
354 switch(mp->b_datap->db_type)
355 {
356 case M_DATA:
357 for(bp=mp; bp!=NULL; bp=bp->b_cont)
358 {
359 while(bp->b_rptr < bp->b_wptr)
360 chuinput( ((u_char)*(bp->b_rptr++)) , q );
361 }
362 freemsg(mp);
363 break;
364 default:
365 putnext(q,mp);
366 break;
367 }
368
369}
370
371/*
372 * Writing to a chu device doesn't make sense, but we'll pass them
373 * through in case they're important.
374 */
375
376static int chuwput(q, mp)
377queue_t *q;
378mblk_t *mp;
379{
380 putnext(q,mp);
381}
382
383/*
384 * Take a pointer to a filled chucode struct and a queue and
385 * send the chucode stuff upstream
386 */
387
388void passback(outdata,q)
389struct chucode *outdata;
390queue_t *q;
391{
392 mblk_t *mp;
393 int j;
394
395 mp=(mblk_t*) allocb(sizeof(struct chucode),BPRI_LO);
396
397 if (mp==NULL)
398 {
98771864
GW
399#ifdef SOLARIS2
400 /* XXX we can't log it because strlog() is too complicated. This isn't
401 supposed to happen anyway. The hell with it. */
402#else
09169146 403 log(LOG_ERR,"chu: cannot allocate message");
98771864 404#endif
09169146
GW
405 return;
406 }
407
408 for(j=0;j<sizeof(struct chucode); j++)
409 *mp->b_wptr++ = *( ((char*)outdata) + j );
410
411 putnext(q,mp);
412}
413
414/*
415 * This routine was copied nearly verbatim from the old line discipline.
416 */
417void chuinput(c,q)
418register u_char c;
419queue_t *q;
420{
421 register struct chucode *chuc;
422 register int i;
423 long sec, usec;
424 struct timeval tv;
425
426 /*
427 * Quick, Batman, get a timestamp! We need to do this
428 * right away. The time between the end of the stop bit
429 * and this point is critical, and should be as nearly
430 * constant and as short as possible. (Un)fortunately,
431 * the Sun's clock granularity is so big this isn't a
432 * major problem.
433 *
434 * uniqtime() is totally undocumented, but there you are.
435 */
436 uniqtime(&tv);
437
438 /*
439 * Now, locate the chu struct once so we don't have to do it
440 * over and over.
441 */
442 chuc=&(((struct priv_data *) (q->q_ptr))->chu_struct);
443
444 /*
445 * Compute the difference in this character's time stamp
446 * and the last. If it exceeds the margin, blow away all
447 * the characters currently in the buffer.
448 */
449 i = (int)chuc->ncodechars;
450 if (i > 0)
451 {
452 sec = tv.tv_sec - chuc->codetimes[i-1].tv_sec;
453 usec = tv.tv_usec - chuc->codetimes[i-1].tv_usec;
454 if (usec < 0)
455 {
456 sec -= 1;
457 usec += 1000000;
458 }
459 if (sec != 0 || usec > CHUMAXUSEC)
460 {
461 i = 0;
462 chuc->ncodechars = 0;
463 }
464 }
465
466 /*
467 * Store the character.
468 */
469 chuc->codechars[i] = (u_char)c;
470 chuc->codetimes[i] = tv;
471
472 /*
473 * Now we perform the 'sixth byte' heuristics.
474 *
475 * This is a long story.
476 *
477 * We used to be able to count on the first byte of the code
478 * having a '6' in the LSD. This prevented most code framing
479 * errors (garbage before the first byte wouldn't typically
480 * have a 6 in the LSD). That's no longer the case.
481 *
482 * We can get around this, however, by noting that the 6th byte
483 * must be either equal to or one's complement of the first.
484 * If we get a sixth byte that ISN'T like that, then it may
485 * well be that the first byte is garbage. The right thing
486 * to do is to left-shift the whole buffer one count and
487 * continue to wait for the sixth byte.
488 */
489 if (i == NCHUCHARS/2)
490 {
491 register u_char temp_byte;
492
493 temp_byte=chuc->codechars[i] ^ chuc->codechars[0];
494
495 if ( (temp_byte) && (temp_byte!=0xff) )
496 {
497 register int t;
498 /*
499 * No match. Left-shift the buffer and try again
500 */
501 for(t=0;t<=NCHUCHARS/2;t++)
502 {
503 chuc->codechars[t]=chuc->codechars[t+1];
504 chuc->codetimes[t]=chuc->codetimes[t+1];
505 }
506
507 i--; /* This is because of the ++i immediately following */
508 }
509 }
510
511 /*
512 * We done yet?
513 */
514 if (++i < NCHUCHARS)
515 {
516 /*
517 * We're not done. Not much to do here. Save the count and wait
518 * for another character.
519 */
520 chuc->ncodechars = (u_char)i;
521 }
522 else
523 {
524 /*
525 * We are done. Mark this buffer full and pass it along.
526 */
527 chuc->ncodechars = NCHUCHARS;
528
529 /*
530 * Now we have a choice. Either the front half and back half
531 * have to match, or be one's complement of each other.
532 *
533 * So let's try the first byte and see
534 */
535
536 if(chuc->codechars[0] == chuc->codechars[NCHUCHARS/2])
537 {
538 chuc->chutype = CHU_TIME;
539 for( i=0; i<(NCHUCHARS/2); i++)
540 if (chuc->codechars[i] != chuc->codechars[i+(NCHUCHARS/2)])
541 {
542 chuc->ncodechars = 0;
543 return;
544 }
545 }
546 else
547 {
548 chuc->chutype = CHU_YEAR;
549 for( i=0; i<(NCHUCHARS/2); i++)
550 if (((chuc->codechars[i] ^ chuc->codechars[i+(NCHUCHARS/2)]) & 0xff)
551 != 0xff )
552 {
553 chuc->ncodechars = 0;
554 return;
555 }
556 }
557
558 passback(chuc,q); /* We're done! */
559 chuc->ncodechars = 0; /* Start all over again! */
560 }
561}
562
98771864 563#endif /* NCHU > 0 */