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