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
131static struct module_info rminfo = { 0, "chu", 0, INFPSZ, 0, 0 };
132static struct module_info wminfo = { 0, "chu", 0, INFPSZ, 0, 0 };
133static int chuopen(), churput(), chuwput(), chuclose();
134
135static struct qinit rinit = { churput, NULL, chuopen, chuclose, NULL,
136	&rminfo, NULL };
137
138static struct qinit winit = { chuwput, NULL, NULL, NULL, NULL,
139	&wminfo, NULL };
140
141struct streamtab chuinfo = { &rinit, &winit, NULL, NULL };
142
143/*
144 * Here's our private data type and structs
145 */
146struct 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
157struct priv_data our_priv_data[NCHU];
158#endif
159
160#ifdef SOLARIS2
161
162static struct fmodsw fsw =
163{
164  "chu",
165  &chuinfo,
166  D_NEW | D_MP
167};
168
169extern struct mod_ops mod_strmodops;
170
171static struct modlstrmod modlstrmod =
172{
173  &mod_strmodops,
174  "CHU timecode decoder v2.6",
175  &fsw
176};
177
178static struct modlinkage modlinkage =
179{
180  MODREV_1,
181  (void*) &modlstrmod,
182  NULL
183};
184
185int _init()
186{
187  return mod_install(&modlinkage);
188}
189
190int _info(foo)
191struct modinfo *foo;
192{
193  return mod_info(&modlinkage,foo);
194}
195
196int _fini()
197{
198  return mod_remove(&modlinkage);
199}
200
201#endif /* SOLARIS2 */
202
203#ifdef LOADABLE
204
205# ifdef sun
206
207static struct vdldrv vd =
208{
209    VDMAGIC_PSEUDO,
210    "chu",
211    NULL, NULL, NULL, 0, 0, NULL, NULL, 0, 0,
212};
213
214static struct fmodsw *chu_fmod;
215
216/*ARGSUSED*/
217chuinit (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
279char chu_first_open=1;
280
281#endif
282
283/*ARGSUSED*/
284static int chuopen(q, dev, flag, sflag)
285queue_t *q;
286dev_t dev;
287int flag;
288int 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*/
345static int chuclose(q, flag)
346queue_t *q;
347int 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
373void chuinput();
374void passback();
375
376static int churput(q, mp)
377queue_t *q;
378mblk_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
404static int chuwput(q, mp)
405queue_t *q;
406mblk_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
416void passback(outdata,q)
417struct chucode *outdata;
418queue_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 */
444void chuinput(c,q)
445register u_char c;
446queue_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 */
604