4 * CHU STREAMS module for SunOS
8 * Copyright 1991-1994, Nick Sayer
10 * Special thanks to Greg Onufer for his debug assists.
11 * Special thanks to Matthias Urlichs for the 4.1.x loadable driver support
13 * Special wet-noodle whippings to Sun for not properly documenting
14 * ANYTHING that makes this stuff at all possible.
16 * Should be PUSHed directly on top of a serial I/O channel.
17 * Provides complete chucode structures to user space.
22 * To make a SunOS 4.1.x compatable loadable module (from the ntp kernel
25 * % cc -c -I../include -DLOADABLE tty_chu_STREAMS.c
27 * The resulting .o file is the loadable module. Modload it
30 * % modload tty_chu_STREAMS.o -entry _chuinit
32 * When none of the instances are pushed in a STREAM, you can
33 * modunload the driver in the usual manner if you wish.
35 * As an alternative to loading it dynamically you can compile it
36 * directly into the kernel by hacking str_conf.c. See the README
37 * file for more details on doing it the old fashioned way.
40 * To make a Solaris 2.x compatable module (from the ntp kernel
43 * % {gcc,cc} -c -I../include -DSOLARIS2 tty_chu_STREAMS.c
44 * % ld -r -o /usr/kernel/strmod/chu tty_chu_STREAMS.o
45 * % chmod 755 /usr/kernel/strmod/chu
47 * The OS will load it for you automagically when it is first pushed.
49 * If you get syntax errors from <sys/timer.h> (really references
50 * to types that weren't typedef'd in gcc's version of types.h),
51 * add -D_SYS_TIMER_H to blot out the miscreants.
53 * Under Solaris 2.2 and previous, do not attempt to modunload the
54 * module unless you're SURE it's not in use. I haven't tried it, but
55 * I've been told it won't do the right thing. Under Solaris 2.3 (and
56 * presumably future revs) an attempt to unload the module when it's in
57 * use will properly refuse with a "busy" message.
62 * v2.6 - Mutexed the per-instance chucode just to be safe.
63 * v2.5 - Fixed show-stopper bug in Solaris 2.x - qprocson().
64 * v2.4 - Added dynamic allocation support for Solaris 2.x.
65 * v2.3 - Added support for Solaris 2.x.
66 * v2.2 - Added SERVICE IMMEDIATE hack.
67 * v2.1 - Added 'sixth byte' heuristics.
68 * v2.0 - first version with an actual version number.
69 * Added support for new CHU 'second 31' data format.
70 * Deleted PEDANTIC and ANAL_RETENTIVE.
79 #elif defined(LOADABLE)
91 * Number of microseconds we allow between
92 * character arrivals. The speed is 300 baud
93 * so this should be somewhat more than 30 msec
95 #define CHUMAXUSEC (60*1000) /* 60 msec */
97 #include <sys/types.h>
98 #include <sys/stream.h>
99 #include <sys/param.h>
100 #include <sys/time.h>
101 #include <sys/errno.h>
102 #include <sys/user.h>
106 #include <sys/chudefs.h>
110 #include <sys/ksynch.h>
111 #include <sys/kmem.h>
112 #include <sys/cmn_err.h>
113 #include <sys/conf.h>
114 #include <sys/strtty.h>
115 #include <sys/modctl.h>
117 #include <sys/sunddi.h>
123 #include <sys/kernel.h>
124 #include <sys/conf.h>
126 #include <sundev/mbvar.h>
127 #include <sun/autoconf.h>
128 #include <sun/vddrv.h>
133 static struct module_info rminfo
= { 0, "chu", 0, INFPSZ
, 0, 0 };
134 static struct module_info wminfo
= { 0, "chu", 0, INFPSZ
, 0, 0 };
135 static int chuopen(), churput(), chuwput(), chuclose();
137 static struct qinit rinit
= { churput
, NULL
, chuopen
, chuclose
, NULL
,
140 static struct qinit winit
= { chuwput
, NULL
, NULL
, NULL
, NULL
,
143 struct streamtab chuinfo
= { &rinit
, &winit
, NULL
, NULL
};
146 * Here's our private data type and structs
151 kmutex_t chucode_mutex
;
155 struct chucode chu_struct
;
159 struct priv_data our_priv_data
[NCHU
];
164 static struct fmodsw fsw
=
171 extern struct mod_ops mod_strmodops
;
173 static struct modlstrmod modlstrmod
=
176 "CHU timecode decoder v2.6",
180 static struct modlinkage modlinkage
=
189 return mod_install(&modlinkage
);
195 return mod_info(&modlinkage
,foo
);
200 return mod_remove(&modlinkage
);
203 #endif /* SOLARIS2 */
209 static struct vdldrv vd
=
213 NULL
, NULL
, NULL
, 0, 0, NULL
, NULL
, 0, 0,
216 static struct fmodsw
*chu_fmod
;
219 chuinit (fc
, vdp
, vdi
, vds
)
230 /* Find free entry in fmodsw */
231 for (dev
= 0; dev
< fmodcnt
; dev
++) {
232 if (fmodsw
[dev
].f_str
== NULL
)
237 chu_fmod
= &fmodsw
[dev
];
239 /* If you think a kernel would have strcpy() you're mistaken. */
240 for (i
= 0; i
<= FMNAMESZ
; i
++)
241 chu_fmod
->f_name
[i
] = wminfo
.mi_idname
[i
];
243 chu_fmod
->f_str
= &chuinfo
;
245 vdp
->vdd_vdtab
= (struct vdlinkage
*) & vd
;
250 for (i
=0; i
<NCHU
; i
++)
251 our_priv_data
[i
].in_use
=0;
259 for (dev
= 0; dev
< NCHU
; dev
++)
260 if (our_priv_data
[dev
].in_use
) {
261 /* One of the modules is still open */
265 chu_fmod
->f_name
[0] = '\0';
266 chu_fmod
->f_str
= NULL
;
277 #endif /* LOADABLE */
279 #if !defined(LOADABLE) && !defined(SOLARIS2)
281 char chu_first_open
=1;
286 static int chuopen(q
, dev
, flag
, sflag
)
294 #if !defined(LOADABLE) && !defined(SOLARIS2)
300 our_priv_data
[i
].in_use
=0;
305 /* According to the docs, calling with KM_SLEEP can never
308 q
->q_ptr
= kmem_alloc( sizeof(struct priv_data
), KM_SLEEP
);
309 ((struct priv_data
*) q
->q_ptr
)->chu_struct
.ncodechars
= 0;
311 mutex_init(&((struct priv_data
*) q
->q_ptr
)->chucode_mutex
,"Chucode Mutex",MUTEX_DRIVER
,NULL
);
314 if (!putnextctl1(WR(q
), M_CTL
, MC_SERVICEIMM
))
317 mutex_destroy(&((struct priv_data
*)q
->q_ptr
)->chucode_mutex
);
318 kmem_free(q
->q_ptr
, sizeof(struct chucode
) );
326 if (!our_priv_data
[i
].in_use
)
328 ((struct priv_data
*) (q
->q_ptr
))=&(our_priv_data
[i
]);
329 our_priv_data
[i
].in_use
++;
330 our_priv_data
[i
].chu_struct
.ncodechars
= 0;
331 if (!putctl1(WR(q
)->q_next
, M_CTL
, MC_SERVICEIMM
))
333 our_priv_data
[i
].in_use
=0;
347 static int chuclose(q
, flag
)
353 mutex_destroy(&((struct priv_data
*)q
->q_ptr
)->chucode_mutex
);
354 kmem_free(q
->q_ptr
, sizeof(struct chucode
) );
356 ((struct priv_data
*) (q
->q_ptr
))->in_use
=0;
362 * Now the crux of the biscuit.
364 * We will be passed data from the man downstairs. If it's not a data
365 * packet, it must be important, so pass it along unmunged. If, however,
366 * it is a data packet, we're gonna do special stuff to it. We're going
367 * to pass each character we get to the old line discipline code we
368 * include below for just such an occasion. When the old ldisc code
369 * gets a full chucode struct, we'll hand it back upstairs.
371 * chuinput takes a single character and q (as quickly as possible).
372 * passback takes a pointer to a chucode struct and q and sends it upstream.
378 static int churput(q
, mp
)
384 switch(mp
->b_datap
->db_type
)
387 for(bp
=mp
; bp
!=NULL
; bp
=bp
->b_cont
)
389 while(bp
->b_rptr
< bp
->b_wptr
)
390 chuinput( ((u_char
)*(bp
->b_rptr
++)) , q
);
402 * Writing to a chu device doesn't make sense, but we'll pass them
403 * through in case they're important.
406 static int chuwput(q
, mp
)
414 * Take a pointer to a filled chucode struct and a queue and
415 * send the chucode stuff upstream
418 void passback(outdata
,q
)
419 struct chucode
*outdata
;
425 mp
=(mblk_t
*) allocb(sizeof(struct chucode
),BPRI_LO
);
430 cmn_err(CE_WARN
,"chu module couldn't allocate message block");
432 log(LOG_ERR
,"chu: cannot allocate message");
437 for(j
=0;j
<sizeof(struct chucode
); j
++)
438 *mp
->b_wptr
++ = *( ((char*)outdata
) + j
);
444 * This routine was copied nearly verbatim from the old line discipline.
450 register struct chucode
*chuc
;
456 * Quick, Batman, get a timestamp! We need to do this
457 * right away. The time between the end of the stop bit
458 * and this point is critical, and should be as nearly
459 * constant and as short as possible. (Un)fortunately,
460 * the Sun's clock granularity is so big this isn't a
463 * uniqtime() is totally undocumented, but there you are.
468 mutex_enter(&((struct priv_data
*)q
->q_ptr
)->chucode_mutex
);
472 * Now, locate the chu struct once so we don't have to do it
475 chuc
=&(((struct priv_data
*) (q
->q_ptr
))->chu_struct
);
478 * Compute the difference in this character's time stamp
479 * and the last. If it exceeds the margin, blow away all
480 * the characters currently in the buffer.
482 i
= (int)chuc
->ncodechars
;
485 sec
= tv
.tv_sec
- chuc
->codetimes
[i
-1].tv_sec
;
486 usec
= tv
.tv_usec
- chuc
->codetimes
[i
-1].tv_usec
;
492 if (sec
!= 0 || usec
> CHUMAXUSEC
)
495 chuc
->ncodechars
= 0;
500 * Store the character.
502 chuc
->codechars
[i
] = (u_char
)c
;
503 chuc
->codetimes
[i
] = tv
;
506 * Now we perform the 'sixth byte' heuristics.
508 * This is a long story.
510 * We used to be able to count on the first byte of the code
511 * having a '6' in the LSD. This prevented most code framing
512 * errors (garbage before the first byte wouldn't typically
513 * have a 6 in the LSD). That's no longer the case.
515 * We can get around this, however, by noting that the 6th byte
516 * must be either equal to or one's complement of the first.
517 * If we get a sixth byte that ISN'T like that, then it may
518 * well be that the first byte is garbage. The right thing
519 * to do is to left-shift the whole buffer one count and
520 * continue to wait for the sixth byte.
522 if (i
== NCHUCHARS
/2)
524 register u_char temp_byte
;
526 temp_byte
=chuc
->codechars
[i
] ^ chuc
->codechars
[0];
528 if ( (temp_byte
) && (temp_byte
!=0xff) )
532 * No match. Left-shift the buffer and try again
534 for(t
=0;t
<=NCHUCHARS
/2;t
++)
536 chuc
->codechars
[t
]=chuc
->codechars
[t
+1];
537 chuc
->codetimes
[t
]=chuc
->codetimes
[t
+1];
540 i
--; /* This is because of the ++i immediately following */
550 * We're not done. Not much to do here. Save the count and wait
551 * for another character.
553 chuc
->ncodechars
= (u_char
)i
;
558 * We are done. Mark this buffer full and pass it along.
560 chuc
->ncodechars
= NCHUCHARS
;
563 * Now we have a choice. Either the front half and back half
564 * have to match, or be one's complement of each other.
566 * So let's try the first byte and see
569 if(chuc
->codechars
[0] == chuc
->codechars
[NCHUCHARS
/2])
571 chuc
->chutype
= CHU_TIME
;
572 for( i
=0; i
<(NCHUCHARS
/2); i
++)
573 if (chuc
->codechars
[i
] != chuc
->codechars
[i
+(NCHUCHARS
/2)])
575 chuc
->ncodechars
= 0;
577 mutex_exit(&((struct priv_data
*)q
->q_ptr
)->chucode_mutex
);
584 chuc
->chutype
= CHU_YEAR
;
585 for( i
=0; i
<(NCHUCHARS
/2); i
++)
586 if (((chuc
->codechars
[i
] ^ chuc
->codechars
[i
+(NCHUCHARS
/2)]) & 0xff)
589 chuc
->ncodechars
= 0;
591 mutex_exit(&((struct priv_data
*)q
->q_ptr
)->chucode_mutex
);
597 passback(chuc
,q
); /* We're done! */
598 chuc
->ncodechars
= 0; /* Start all over again! */
601 mutex_exit(&((struct priv_data
*)q
->q_ptr
)->chucode_mutex
);
605 #endif /* NCHU > 0 */