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