aboutsummaryrefslogtreecommitdiff
path: root/usr.sbin/xntpd/kernel/tty_chu_STREAMS.c
blob: f46e25d2ab519c6e1653c1e9a722d163aa5b9008 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
/*
 * CHU STREAMS module for SunOS
 *
 * Version 2.6
 *
 * Copyright 1991-1994, Nick Sayer
 *
 * Special thanks to Greg Onufer for his debug assists.
 * Special thanks to Matthias Urlichs for the 4.1.x loadable driver support
 *   code.
 * Special wet-noodle whippings to Sun for not properly documenting
 *   ANYTHING that makes this stuff at all possible.
 *
 * Should be PUSHed directly on top of a serial I/O channel.
 * Provides complete chucode structures to user space.
 *
 * COMPILATION:
 *
 *
 * To make a SunOS 4.1.x compatable loadable module (from the ntp kernel
 * directory):
 *
 * % cc -c -I../include -DLOADABLE tty_chu_STREAMS.c
 *
 * The resulting .o file is the loadable module. Modload it
 * thusly:
 *
 * % modload tty_chu_STREAMS.o -entry _chuinit
 *
 * When none of the instances are pushed in a STREAM, you can
 * modunload the driver in the usual manner if you wish.
 *
 * As an alternative to loading it dynamically you can compile it
 * directly into the kernel by hacking str_conf.c. See the README
 * file for more details on doing it the old fashioned way.
 *
 *
 * To make a Solaris 2.x compatable module (from the ntp kernel
 * directory):
 *
 * % {gcc,cc} -c -I../include -DSOLARIS2 tty_chu_STREAMS.c
 * % ld -r -o /usr/kernel/strmod/chu tty_chu_STREAMS.o
 * % chmod 755 /usr/kernel/strmod/chu
 *
 * The OS will load it for you automagically when it is first pushed.
 *
 * If you get syntax errors from <sys/timer.h> (really references
 * to types that weren't typedef'd in gcc's version of types.h),
 * add -D_SYS_TIMER_H to blot out the miscreants.
 *
 * Under Solaris 2.2 and previous, do not attempt to modunload the
 * module unless you're SURE it's not in use. I haven't tried it, but
 * I've been told it won't do the right thing. Under Solaris 2.3 (and
 * presumably future revs) an attempt to unload the module when it's in
 * use will properly refuse with a "busy" message.
 *
 *
 * HISTORY:
 *
 * v2.6 - Mutexed the per-instance chucode just to be safe.
 * v2.5 - Fixed show-stopper bug in Solaris 2.x - qprocson().
 * v2.4 - Added dynamic allocation support for Solaris 2.x.
 * v2.3 - Added support for Solaris 2.x.
 * v2.2 - Added SERVICE IMMEDIATE hack.
 * v2.1 - Added 'sixth byte' heuristics.
 * v2.0 - first version with an actual version number.
 *        Added support for new CHU 'second 31' data format.
 *        Deleted PEDANTIC and ANAL_RETENTIVE.
 *
 */

#ifdef SOLARIS2
# ifndef NCHU
#  define NCHU 1
# endif
# define _KERNEL
#elif defined(LOADABLE)
# ifndef NCHU
#  define NCHU 3
#  define KERNEL
# endif
#else
# include "chu.h"
#endif

#if NCHU > 0

/*
 * Number of microseconds we allow between
 * character arrivals.  The speed is 300 baud
 * so this should be somewhat more than 30 msec
 */
#define	CHUMAXUSEC	(60*1000)	/* 60 msec */

#include <sys/types.h>
#include <sys/stream.h>
#include <sys/param.h>
#include <sys/time.h>
#include <sys/errno.h>
#include <sys/user.h>
#include <syslog.h>
#include <sys/tty.h>

#include <sys/chudefs.h>

#ifdef SOLARIS2

#include <sys/ksynch.h>
#include <sys/kmem.h>
#include <sys/cmn_err.h>
#include <sys/conf.h>
#include <sys/strtty.h>
#include <sys/modctl.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>

#endif

#ifdef LOADABLE

#include <sys/kernel.h>
#include <sys/conf.h>
#include <sys/buf.h>
#include <sundev/mbvar.h>
#include <sun/autoconf.h>
#include <sun/vddrv.h>

#endif


static struct module_info rminfo = { 0, "chu", 0, INFPSZ, 0, 0 };
static struct module_info wminfo = { 0, "chu", 0, INFPSZ, 0, 0 };
static int chuopen(), churput(), chuwput(), chuclose();

static struct qinit rinit = { churput, NULL, chuopen, chuclose, NULL,
	&rminfo, NULL };

static struct qinit winit = { chuwput, NULL, NULL, NULL, NULL,
	&wminfo, NULL };

struct streamtab chuinfo = { &rinit, &winit, NULL, NULL };

/*
 * Here's our private data type and structs
 */
struct priv_data 
{
#ifdef SOLARIS2
  kmutex_t chucode_mutex;
#else
  char in_use;
#endif
  struct chucode chu_struct;
};

#ifndef SOLARIS2
struct priv_data our_priv_data[NCHU];
#endif

#ifdef SOLARIS2

static struct fmodsw fsw =
{
  "chu",
  &chuinfo,
  D_NEW | D_MP
};

extern struct mod_ops mod_strmodops;

static struct modlstrmod modlstrmod =
{
  &mod_strmodops,
  "CHU timecode decoder v2.6",
  &fsw
};

static struct modlinkage modlinkage =
{
  MODREV_1,
  (void*) &modlstrmod,
  NULL
};

int _init()
{
  return mod_install(&modlinkage);
}

int _info(foo)
struct modinfo *foo;
{
  return mod_info(&modlinkage,foo);
}

int _fini()
{
  return mod_remove(&modlinkage);
}

#endif /* SOLARIS2 */

#ifdef LOADABLE

# ifdef sun

static struct vdldrv vd =
{
    VDMAGIC_PSEUDO,
    "chu",
    NULL, NULL, NULL, 0, 0, NULL, NULL, 0, 0,
};

static struct fmodsw *chu_fmod;

/*ARGSUSED*/
chuinit (fc, vdp, vdi, vds)
    unsigned int fc;
    struct vddrv *vdp;
    addr_t vdi;
    struct vdstat *vds;
{
    switch (fc) {
    case VDLOAD:
        {
            int dev, i;

            /* Find free entry in fmodsw */
            for (dev = 0; dev < fmodcnt; dev++) {
                if (fmodsw[dev].f_str == NULL)
                    break;
            }
            if (dev == fmodcnt)
                return (ENODEV);
            chu_fmod = &fmodsw[dev];

	    /* If you think a kernel would have strcpy() you're mistaken. */
            for (i = 0; i <= FMNAMESZ; i++)
                chu_fmod->f_name[i] = wminfo.mi_idname[i];

            chu_fmod->f_str = &chuinfo;
        }
        vdp->vdd_vdtab = (struct vdlinkage *) & vd;

	{
	    int i;

	    for (i=0; i<NCHU; i++)
	        our_priv_data[i].in_use=0;
	}

        return 0;
    case VDUNLOAD:
        {
            int dev;

            for (dev = 0; dev < NCHU; dev++)
                if (our_priv_data[dev].in_use) {
                    /* One of the modules is still open */
                    return (EBUSY);
                }
        }
        chu_fmod->f_name[0] = '\0';
        chu_fmod->f_str = NULL;
        return 0;
    case VDSTAT:
        return 0;
    default:
        return EIO;
    }
}

# endif /* sun */

#endif /* LOADABLE */

#if !defined(LOADABLE) && !defined(SOLARIS2)

char chu_first_open=1;

#endif

/*ARGSUSED*/
static int chuopen(q, dev, flag, sflag)
queue_t *q;
dev_t dev;
int flag;
int sflag;
{
  int i;

#if !defined(LOADABLE) && !defined(SOLARIS2)
  if (chu_first_open)
  {
    chu_first_open=0;

    for(i=0;i<NCHU;i++)
      our_priv_data[i].in_use=0;
  }
#endif

#ifdef SOLARIS2
  /* According to the docs, calling with KM_SLEEP can never
     fail */

  q->q_ptr = kmem_alloc( sizeof(struct priv_data), KM_SLEEP );
  ((struct priv_data *) q->q_ptr)->chu_struct.ncodechars = 0;

  mutex_init(&((struct priv_data *) q->q_ptr)->chucode_mutex,"Chucode Mutex",MUTEX_DRIVER,NULL);
  qprocson(q);

  if (!putnextctl1(WR(q), M_CTL, MC_SERVICEIMM))
  {
    qprocsoff(q);
    mutex_destroy(&((struct priv_data *)q->q_ptr)->chucode_mutex);
    kmem_free(q->q_ptr, sizeof(struct chucode) );
    return (EFAULT);
  }

  return 0;

#else
  for(i=0;i<NCHU;i++)
    if (!our_priv_data[i].in_use)
    {
      ((struct priv_data *) (q->q_ptr))=&(our_priv_data[i]);
      our_priv_data[i].in_use++;
      our_priv_data[i].chu_struct.ncodechars = 0;
      if (!putctl1(WR(q)->q_next, M_CTL, MC_SERVICEIMM))
      {
        our_priv_data[i].in_use=0;
        u.u_error = EFAULT;
	return (OPENFAIL);
      }
      return 0;
    }

  u.u_error = EBUSY;
  return (OPENFAIL);
#endif

}

/*ARGSUSED*/
static int chuclose(q, flag)
queue_t *q;
int flag;
{
#ifdef SOLARIS2
  qprocsoff(q);
  mutex_destroy(&((struct priv_data *)q->q_ptr)->chucode_mutex);
  kmem_free(q->q_ptr, sizeof(struct chucode) );
#else
  ((struct priv_data *) (q->q_ptr))->in_use=0;
#endif
  return (0);
}

/*
 * Now the crux of the biscuit.
 *
 * We will be passed data from the man downstairs. If it's not a data
 * packet, it must be important, so pass it along unmunged. If, however,
 * it is a data packet, we're gonna do special stuff to it. We're going
 * to pass each character we get to the old line discipline code we
 * include below for just such an occasion. When the old ldisc code
 * gets a full chucode struct, we'll hand it back upstairs.
 *
 * chuinput takes a single character and q (as quickly as possible).
 * passback takes a pointer to a chucode struct and q and sends it upstream.
 */

void chuinput();
void passback();

static int churput(q, mp)
queue_t *q;
mblk_t *mp;
{
  mblk_t *bp;

  switch(mp->b_datap->db_type)
  {
    case M_DATA:
      for(bp=mp; bp!=NULL; bp=bp->b_cont)
      {
	while(bp->b_rptr < bp->b_wptr)
	  chuinput( ((u_char)*(bp->b_rptr++)) , q );
      }
      freemsg(mp);
    break;
    default:
      putnext(q,mp);
    break;
  }

}

/*
 * Writing to a chu device doesn't make sense, but we'll pass them
 * through in case they're important.
 */

static int chuwput(q, mp)
queue_t *q;
mblk_t *mp;
{
  putnext(q,mp);
}

/*
 * Take a pointer to a filled chucode struct and a queue and
 * send the chucode stuff upstream
 */

void passback(outdata,q)
struct chucode *outdata;
queue_t *q;
{
  mblk_t *mp;
  int j;

  mp=(mblk_t*) allocb(sizeof(struct chucode),BPRI_LO);

  if (mp==NULL)
  {
#ifdef SOLARIS2
    cmn_err(CE_WARN,"chu module couldn't allocate message block");
#else
    log(LOG_ERR,"chu: cannot allocate message");
#endif
    return;
  }

  for(j=0;j<sizeof(struct chucode); j++)
    *mp->b_wptr++ = *( ((char*)outdata) + j );

  putnext(q,mp);
}

/*
 * This routine was copied nearly verbatim from the old line discipline.
 */
void chuinput(c,q)
register u_char c;
queue_t *q;
{
  register struct chucode *chuc;
  register int i;
  long sec, usec;
  struct timeval tv;

  /*
   * Quick, Batman, get a timestamp! We need to do this
   * right away. The time between the end of the stop bit
   * and this point is critical, and should be as nearly
   * constant and as short as possible. (Un)fortunately,
   * the Sun's clock granularity is so big this isn't a
   * major problem.
   *
   * uniqtime() is totally undocumented, but there you are.
   */
  uniqtime(&tv);

#ifdef SOLARIS2
  mutex_enter(&((struct priv_data *)q->q_ptr)->chucode_mutex);
#endif

  /*
   * Now, locate the chu struct once so we don't have to do it
   * over and over.
   */
  chuc=&(((struct priv_data *) (q->q_ptr))->chu_struct);

	/*
	 * Compute the difference in this character's time stamp
	 * and the last.  If it exceeds the margin, blow away all
	 * the characters currently in the buffer.
	 */
  i = (int)chuc->ncodechars;
  if (i > 0)
  {
    sec = tv.tv_sec - chuc->codetimes[i-1].tv_sec;
    usec = tv.tv_usec - chuc->codetimes[i-1].tv_usec;
    if (usec < 0)
    {
      sec -= 1;
      usec += 1000000;
    }
    if (sec != 0 || usec > CHUMAXUSEC)
    {
      i = 0;
      chuc->ncodechars = 0;
    }
  }

  /*
   * Store the character.
   */
  chuc->codechars[i] = (u_char)c;
  chuc->codetimes[i] = tv;

  /*
   * Now we perform the 'sixth byte' heuristics.
   *
   * This is a long story.
   *
   * We used to be able to count on the first byte of the code
   * having a '6' in the LSD. This prevented most code framing
   * errors (garbage before the first byte wouldn't typically
   * have a 6 in the LSD). That's no longer the case.
   *
   * We can get around this, however, by noting that the 6th byte
   * must be either equal to or one's complement of the first.
   * If we get a sixth byte that ISN'T like that, then it may
   * well be that the first byte is garbage. The right thing
   * to do is to left-shift the whole buffer one count and
   * continue to wait for the sixth byte.
   */
  if (i == NCHUCHARS/2)
  {
    register u_char temp_byte;

    temp_byte=chuc->codechars[i] ^ chuc->codechars[0];

    if ( (temp_byte) && (temp_byte!=0xff) )
    {
      register int t;
      /*
       * No match. Left-shift the buffer and try again
       */
      for(t=0;t<=NCHUCHARS/2;t++)
      {
	chuc->codechars[t]=chuc->codechars[t+1];
	chuc->codetimes[t]=chuc->codetimes[t+1];
      }

      i--; /* This is because of the ++i immediately following */
    }
  }

  /*
   * We done yet?
   */
  if (++i < NCHUCHARS)
  {
    /*
     * We're not done. Not much to do here. Save the count and wait
     * for another character.
     */
    chuc->ncodechars = (u_char)i;
  }
  else
  {
    /*
     * We are done. Mark this buffer full and pass it along.
     */
    chuc->ncodechars = NCHUCHARS;

    /*
     * Now we have a choice. Either the front half and back half
     * have to match, or be one's complement of each other.
     *
     * So let's try the first byte and see
     */

    if(chuc->codechars[0] == chuc->codechars[NCHUCHARS/2])
    {
      chuc->chutype = CHU_TIME;
      for( i=0; i<(NCHUCHARS/2); i++)
        if (chuc->codechars[i] != chuc->codechars[i+(NCHUCHARS/2)])
        {
          chuc->ncodechars = 0;
#ifdef SOLARIS2
          mutex_exit(&((struct priv_data *)q->q_ptr)->chucode_mutex);
#endif
          return;
        }
    }
    else
    {
      chuc->chutype = CHU_YEAR;
      for( i=0; i<(NCHUCHARS/2); i++)
        if (((chuc->codechars[i] ^ chuc->codechars[i+(NCHUCHARS/2)]) & 0xff)
	  != 0xff )
        {
          chuc->ncodechars = 0;
#ifdef SOLARIS2
          mutex_exit(&((struct priv_data *)q->q_ptr)->chucode_mutex);
#endif
          return;
        }
    }

    passback(chuc,q); /* We're done! */
    chuc->ncodechars = 0; /* Start all over again! */
  }
#ifdef SOLARIS2
  mutex_exit(&((struct priv_data *)q->q_ptr)->chucode_mutex);
#endif
}

#endif /* NCHU > 0 */