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
|
/* tty_clk.c,v 3.1 1993/07/06 01:07:33 jbj Exp
* tty_clk.c - Generic line driver for receiving radio clock timecodes
*/
#include "clk.h"
#if NCLK > 0
#include "../h/param.h"
#include "../h/types.h"
#include "../h/systm.h"
#include "../h/dir.h"
#include "../h/user.h"
#include "../h/ioctl.h"
#include "../h/tty.h"
#include "../h/proc.h"
#include "../h/file.h"
#include "../h/conf.h"
#include "../h/buf.h"
#include "../h/uio.h"
#include "../h/clist.h"
/*
* This line discipline is intended to provide well performing
* generic support for the reception and time stamping of radio clock
* timecodes. Most radio clock devices return a string where a
* particular character in the code (usually a \r) is on-time
* synchronized with the clock. The idea here is to collect characters
* until (one of) the synchronization character(s) (we allow two) is seen.
* When the magic character arrives we take a timestamp by calling
* microtime() and insert the eight bytes of struct timeval into the
* buffer after the magic character. We then wake up anyone waiting
* for the buffer and return the whole mess on the next read.
*
* To use this the calling program is expected to first open the
* port, and then to set the port into raw mode with the speed
* set appropriately with a TIOCSETP ioctl(), with the erase and kill
* characters set to those to be considered magic (yes, I know this
* is gross, but they were so convenient). If only one character is
* magic you can set then both the same, or perhaps to the alternate
* parity versions of said character. After getting all this set,
* change the line discipline to CLKLDISC and you are on your way.
*
* The only other bit of magic we do in here is to flush the receive
* buffers on writes if the CRMOD flag is set (hack, hack).
*/
/*
* We run this very much like a raw mode terminal, with the exception
* that we store up characters locally until we hit one of the
* magic ones and then dump it into the rawq all at once. We keep
* the buffered data in clists since we can then often move it to
* the rawq without copying. For sanity we limit the number of
* characters between specials, and the total number of characters
* before we flush the rawq, as follows.
*/
#define CLKLINESIZE (256)
#define NCLKCHARS (CLKLINESIZE*4)
struct clkdata {
int inuse;
struct clist clkbuf;
};
#define clk_cc clkbuf.c_cc
#define clk_cf clkbuf.c_cf
#define clk_cl clkbuf.c_cl
struct clkdata clk_data[NCLK];
/*
* Routine for flushing the internal clist
*/
#define clk_bflush(clk) (ndflush(&((clk)->clkbuf), (clk)->clk_cc))
int clk_debug = 0;
/*ARGSUSED*/
clkopen(dev, tp)
dev_t dev;
register struct tty *tp;
{
register struct clkdata *clk;
/*
* Don't allow multiple opens. This will also protect us
* from someone opening /dev/tty
*/
if (tp->t_line == CLKLDISC)
return (EBUSY);
ttywflush(tp);
for (clk = clk_data; clk < &clk_data[NCLK]; clk++)
if (!clk->inuse)
break;
if (clk >= &clk_data[NCLK])
return (EBUSY);
clk->inuse++;
clk->clk_cc = 0;
clk->clk_cf = clk->clk_cl = NULL;
tp->T_LINEP = (caddr_t) clk;
return (0);
}
/*
* Break down... called when discipline changed or from device
* close routine.
*/
clkclose(tp)
register struct tty *tp;
{
register struct clkdata *clk;
register int s = spltty();
clk = (struct clkdata *)tp->T_LINEP;
if (clk->clk_cc > 0)
clk_bflush(clk);
clk->inuse = 0;
tp->t_line = 0; /* paranoid: avoid races */
splx(s);
}
/*
* Receive a write request. We pass these requests on to the terminal
* driver, except that if the CRMOD bit is set in the flags we
* first flush the input queues.
*/
clkwrite(tp, uio)
register struct tty *tp;
struct uio *uio;
{
if (tp->t_flags & CRMOD) {
register struct clkdata *clk;
int s;
s = spltty();
if (tp->t_rawq.c_cc > 0)
ndflush(&tp->t_rawq, tp->t_rawq.c_cc);
clk = (struct clkdata *) tp->T_LINEP;
if (clk->clk_cc > 0)
clk_bflush(clk);
(void)splx(s);
}
ttwrite(tp, uio);
}
/*
* Low level character input routine.
* If the character looks okay, grab a time stamp. If the stuff in
* the buffer is too old, dump it and start fresh. If the character is
* non-BCDish, everything in the buffer too.
*/
clkinput(c, tp)
register int c;
register struct tty *tp;
{
register struct clkdata *clk;
register int i;
register long s;
struct timeval tv;
/*
* Check to see whether this isn't the magic character. If not,
* save the character and return.
*/
#ifdef ultrix
if (c != tp->t_cc[VERASE] && c != tp->t_cc[VKILL]) {
#else
if (c != tp->t_erase && c != tp->t_kill) {
#endif
clk = (struct clkdata *) tp->T_LINEP;
if (clk->clk_cc >= CLKLINESIZE)
clk_bflush(clk);
if (putc(c, &clk->clkbuf) == -1) {
/*
* Hopeless, no clists. Flush what we have
* and hope things improve.
*/
clk_bflush(clk);
}
return;
}
/*
* Here we have a magic character. Get a timestamp and store
* everything.
*/
microtime(&tv);
clk = (struct clkdata *) tp->T_LINEP;
if (putc(c, &clk->clkbuf) == -1)
goto flushout;
#ifdef CLKLDISC
/*
* STREAMS people started writing timestamps this way.
* It's not my fault, I am just going along with the flow...
*/
for (i = 0; i < sizeof(struct timeval); i++)
if (putc(*( ((char*)&tv) + i ), &clk->clkbuf) == -1)
goto flushout;
#else
/*
* This is a machine independant way of puting longs into
* the datastream. It has fallen into disuse...
*/
s = tv.tv_sec;
for (i = 0; i < sizeof(long); i++) {
if (putc((s >> 24) & 0xff, &clk->clkbuf) == -1)
goto flushout;
s <<= 8;
}
s = tv.tv_usec;
for (i = 0; i < sizeof(long); i++) {
if (putc((s >> 24) & 0xff, &clk->clkbuf) == -1)
goto flushout;
s <<= 8;
}
#endif
/*
* If the length of the rawq exceeds our sanity limit, dump
* all the old crap in there before copying this in.
*/
if (tp->t_rawq.c_cc > NCLKCHARS)
ndflush(&tp->t_rawq, tp->t_rawq.c_cc);
/*
* Now copy the buffer in. There is a special case optimization
* here. If there is nothing on the rawq at present we can
* just copy the clists we own over. Otherwise we must concatenate
* the present data on the end.
*/
s = (long)spltty();
if (tp->t_rawq.c_cc <= 0) {
tp->t_rawq = clk->clkbuf;
clk->clk_cc = 0;
clk->clk_cl = clk->clk_cf = NULL;
(void) splx((int)s);
} else {
(void) splx((int)s);
catq(&clk->clkbuf, &tp->t_rawq);
clk_bflush(clk);
}
/*
* Tell the world
*/
ttwakeup(tp);
return;
flushout:
/*
* It would be nice if this never happened. Flush the
* internal clists and hope someone else frees some of them
*/
clk_bflush(clk);
return;
}
/*
* Handle ioctls. We reject most tty-style except those that
* change the line discipline and a couple of others..
*/
clkioctl(tp, cmd, data, flag)
struct tty *tp;
int cmd;
caddr_t data;
int flag;
{
int flags;
struct sgttyb *sg;
if ((cmd>>8) != 't')
return (-1);
switch (cmd) {
case TIOCSETD:
case TIOCGETD:
case TIOCGETP:
case TIOCGETC:
case TIOCOUTQ:
return (-1);
case TIOCSETP:
/*
* He likely wants to set new magic characters in.
* Do this part.
*/
sg = (struct sgttyb *)data;
#ifdef ultrix
tp->t_cc[VERASE] = sg->sg_erase;
tp->t_cc[VKILL] = sg->sg_kill;
#else
tp->t_erase = sg->sg_erase;
tp->t_kill = sg->sg_kill;
#endif
return (0);
case TIOCFLUSH:
flags = *(int *)data;
if (flags == 0 || (flags & FREAD)) {
register struct clkdata *clk;
clk = (struct clkdata *) tp->T_LINEP;
if (clk->clk_cc > 0)
clk_bflush(clk);
}
return (-1);
default:
break;
}
return (ENOTTY); /* not quite appropriate */
}
#endif NCLK
|