aboutsummaryrefslogtreecommitdiff
path: root/usr.sbin/xntpd/kernel/tty_chu_STREAMS.c
blob: eea792d91c6b3720f398f303ee9e5fcf2b6d3ec6 (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
/* tty_chu_STREAMS.c,v 3.1 1993/07/06 01:07:32 jbj Exp
 * CHU STREAMS module for SunOS 4.1.x
 *
 * Version 2.1
 *
 * Copyright 1991-1993, Nick Sayer
 *
 * Special thanks to Greg Onufer for his debug assists.
 * Special thanks to Matthias Urlichs for the loadable driver support
 *   code.
 *
 * 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
 * with -entry _chuinit.
 *
 * You can also add it into the kernel by hacking it into the streams
 * table in the kernel, then adding it to config:
 *
 * pseudo-device    chuN
 *
 * where N is the maximum number of concurent chu sessions you expect
 * to have.
 *
 * HISTORY:
 *
 * 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.
 *
 */

#ifndef LOADABLE
# include "chu.h"
#else
# ifndef NCHU
#  define NCHU 3
#  define KERNEL
# endif
#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/kernel.h>
#include <sys/errno.h>
#include <sys/user.h>

#include <sys/chudefs.h>

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 
{
  char in_use;
  struct chucode chu_struct;
} our_priv_data[NCHU];

#ifdef LOADABLE

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

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

#else

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;

#ifndef LOADABLE
  if (chu_first_open)
  {
    chu_first_open=0;

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

  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;
      return 0;
    }

  u.u_error = EBUSY;
  return (OPENFAIL);

}

/*ARGSUSED*/
static int chuclose(q, flag)
queue_t *q;
int flag;
{
  ((struct priv_data *) (q->q_ptr))->in_use=0;

  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)
  {
    log(LOG_ERR,"chu: cannot allocate message");
    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);

  /*
   * 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;
          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;
          return;
        }
    }

    passback(chuc,q); /* We're done! */
    chuc->ncodechars = 0; /* Start all over again! */
  }
}

#endif