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