diff options
Diffstat (limited to 'kernel/tty_chu_STREAMS.c')
-rw-r--r-- | kernel/tty_chu_STREAMS.c | 603 |
1 files changed, 603 insertions, 0 deletions
diff --git a/kernel/tty_chu_STREAMS.c b/kernel/tty_chu_STREAMS.c new file mode 100644 index 000000000000..f46e25d2ab51 --- /dev/null +++ b/kernel/tty_chu_STREAMS.c @@ -0,0 +1,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 */ |