1/* tty_clk.c,v 3.1 1993/07/06 01:07:33 jbj Exp 2 * tty_clk.c - Generic line driver for receiving radio clock timecodes 3 */ 4 5#include "clk.h" 6#if NCLK > 0 7 8#include "../h/param.h" 9#include "../h/types.h" 10#include "../h/systm.h" 11#include "../h/dir.h" 12#include "../h/user.h" 13#include "../h/ioctl.h" 14#include "../h/tty.h" 15#include "../h/proc.h" 16#include "../h/file.h" 17#include "../h/conf.h" 18#include "../h/buf.h" 19#include "../h/uio.h" 20#include "../h/clist.h" 21 22/* 23 * This line discipline is intended to provide well performing 24 * generic support for the reception and time stamping of radio clock 25 * timecodes. Most radio clock devices return a string where a 26 * particular character in the code (usually a \r) is on-time 27 * synchronized with the clock. The idea here is to collect characters 28 * until (one of) the synchronization character(s) (we allow two) is seen. 29 * When the magic character arrives we take a timestamp by calling 30 * microtime() and insert the eight bytes of struct timeval into the 31 * buffer after the magic character. We then wake up anyone waiting 32 * for the buffer and return the whole mess on the next read. 33 * 34 * To use this the calling program is expected to first open the 35 * port, and then to set the port into raw mode with the speed 36 * set appropriately with a TIOCSETP ioctl(), with the erase and kill 37 * characters set to those to be considered magic (yes, I know this 38 * is gross, but they were so convenient). If only one character is 39 * magic you can set then both the same, or perhaps to the alternate 40 * parity versions of said character. After getting all this set, 41 * change the line discipline to CLKLDISC and you are on your way. 42 * 43 * The only other bit of magic we do in here is to flush the receive 44 * buffers on writes if the CRMOD flag is set (hack, hack). 45 */ 46 47/* 48 * We run this very much like a raw mode terminal, with the exception 49 * that we store up characters locally until we hit one of the 50 * magic ones and then dump it into the rawq all at once. We keep 51 * the buffered data in clists since we can then often move it to 52 * the rawq without copying. For sanity we limit the number of 53 * characters between specials, and the total number of characters 54 * before we flush the rawq, as follows. 55 */ 56#define CLKLINESIZE (256) 57#define NCLKCHARS (CLKLINESIZE*4) 58 59struct clkdata { 60 int inuse; 61 struct clist clkbuf; 62}; 63#define clk_cc clkbuf.c_cc 64#define clk_cf clkbuf.c_cf 65#define clk_cl clkbuf.c_cl 66 67struct clkdata clk_data[NCLK]; 68 69/* 70 * Routine for flushing the internal clist 71 */ 72#define clk_bflush(clk) (ndflush(&((clk)->clkbuf), (clk)->clk_cc)) 73 74int clk_debug = 0; 75 76/*ARGSUSED*/ 77clkopen(dev, tp) 78 dev_t dev; 79 register struct tty *tp; 80{ 81 register struct clkdata *clk; 82 83 /* 84 * Don't allow multiple opens. This will also protect us 85 * from someone opening /dev/tty 86 */ 87 if (tp->t_line == CLKLDISC) 88 return (EBUSY); 89 ttywflush(tp); 90 for (clk = clk_data; clk < &clk_data[NCLK]; clk++) 91 if (!clk->inuse) 92 break; 93 if (clk >= &clk_data[NCLK]) 94 return (EBUSY); 95 clk->inuse++; 96 clk->clk_cc = 0; 97 clk->clk_cf = clk->clk_cl = NULL; 98 tp->T_LINEP = (caddr_t) clk; 99 return (0); 100} 101 102 103/* 104 * Break down... called when discipline changed or from device 105 * close routine. 106 */ 107clkclose(tp) 108 register struct tty *tp; 109{ 110 register struct clkdata *clk; 111 register int s = spltty(); 112 113 clk = (struct clkdata *)tp->T_LINEP; 114 if (clk->clk_cc > 0) 115 clk_bflush(clk); 116 clk->inuse = 0; 117 tp->t_line = 0; /* paranoid: avoid races */ 118 splx(s); 119} 120 121 122/* 123 * Receive a write request. We pass these requests on to the terminal 124 * driver, except that if the CRMOD bit is set in the flags we 125 * first flush the input queues. 126 */ 127clkwrite(tp, uio) 128 register struct tty *tp; 129 struct uio *uio; 130{ 131 if (tp->t_flags & CRMOD) { 132 register struct clkdata *clk; 133 int s; 134 135 s = spltty(); 136 if (tp->t_rawq.c_cc > 0) 137 ndflush(&tp->t_rawq, tp->t_rawq.c_cc); 138 clk = (struct clkdata *) tp->T_LINEP; 139 if (clk->clk_cc > 0) 140 clk_bflush(clk); 141 (void)splx(s); 142 } 143 ttwrite(tp, uio); 144} 145 146 147/* 148 * Low level character input routine. 149 * If the character looks okay, grab a time stamp. If the stuff in 150 * the buffer is too old, dump it and start fresh. If the character is 151 * non-BCDish, everything in the buffer too. 152 */ 153clkinput(c, tp) 154 register int c; 155 register struct tty *tp; 156{ 157 register struct clkdata *clk; 158 register int i; 159 register long s; 160 struct timeval tv; 161 162 /* 163 * Check to see whether this isn't the magic character. If not, 164 * save the character and return. 165 */ 166#ifdef ultrix 167 if (c != tp->t_cc[VERASE] && c != tp->t_cc[VKILL]) { 168#else 169 if (c != tp->t_erase && c != tp->t_kill) { 170#endif 171 clk = (struct clkdata *) tp->T_LINEP; 172 if (clk->clk_cc >= CLKLINESIZE) 173 clk_bflush(clk); 174 if (putc(c, &clk->clkbuf) == -1) { 175 /* 176 * Hopeless, no clists. Flush what we have 177 * and hope things improve. 178 */ 179 clk_bflush(clk); 180 } 181 return; 182 } 183 184 /* 185 * Here we have a magic character. Get a timestamp and store 186 * everything. 187 */ 188 microtime(&tv); 189 clk = (struct clkdata *) tp->T_LINEP; 190 191 if (putc(c, &clk->clkbuf) == -1) 192 goto flushout; 193 194#ifdef CLKLDISC 195 /* 196 * STREAMS people started writing timestamps this way. 197 * It's not my fault, I am just going along with the flow... 198 */ 199 for (i = 0; i < sizeof(struct timeval); i++) 200 if (putc(*( ((char*)&tv) + i ), &clk->clkbuf) == -1) 201 goto flushout; 202#else 203 /* 204 * This is a machine independant way of puting longs into 205 * the datastream. It has fallen into disuse... 206 */ 207 s = tv.tv_sec; 208 for (i = 0; i < sizeof(long); i++) { 209 if (putc((s >> 24) & 0xff, &clk->clkbuf) == -1) 210 goto flushout; 211 s <<= 8; 212 } 213 214 s = tv.tv_usec; 215 for (i = 0; i < sizeof(long); i++) { 216 if (putc((s >> 24) & 0xff, &clk->clkbuf) == -1) 217 goto flushout; 218 s <<= 8; 219 } 220#endif 221 222 /* 223 * If the length of the rawq exceeds our sanity limit, dump 224 * all the old crap in there before copying this in. 225 */ 226 if (tp->t_rawq.c_cc > NCLKCHARS) 227 ndflush(&tp->t_rawq, tp->t_rawq.c_cc); 228 229 /* 230 * Now copy the buffer in. There is a special case optimization 231 * here. If there is nothing on the rawq at present we can 232 * just copy the clists we own over. Otherwise we must concatenate 233 * the present data on the end. 234 */ 235 s = (long)spltty(); 236 if (tp->t_rawq.c_cc <= 0) { 237 tp->t_rawq = clk->clkbuf; 238 clk->clk_cc = 0; 239 clk->clk_cl = clk->clk_cf = NULL; 240 (void) splx((int)s); 241 } else { 242 (void) splx((int)s); 243 catq(&clk->clkbuf, &tp->t_rawq); 244 clk_bflush(clk); 245 } 246 247 /* 248 * Tell the world 249 */ 250 ttwakeup(tp); 251 return; 252 253flushout: 254 /* 255 * It would be nice if this never happened. Flush the 256 * internal clists and hope someone else frees some of them 257 */ 258 clk_bflush(clk); 259 return; 260} 261 262 263/* 264 * Handle ioctls. We reject most tty-style except those that 265 * change the line discipline and a couple of others.. 266 */ 267clkioctl(tp, cmd, data, flag) 268 struct tty *tp; 269 int cmd; 270 caddr_t data; 271 int flag; 272{ 273 int flags; 274 struct sgttyb *sg; 275 276 if ((cmd>>8) != 't') 277 return (-1); 278 switch (cmd) { 279 case TIOCSETD: 280 case TIOCGETD: 281 case TIOCGETP: 282 case TIOCGETC: 283 case TIOCOUTQ: 284 return (-1); 285 286 case TIOCSETP: 287 /* 288 * He likely wants to set new magic characters in. 289 * Do this part. 290 */ 291 sg = (struct sgttyb *)data; 292#ifdef ultrix 293 tp->t_cc[VERASE] = sg->sg_erase; 294 tp->t_cc[VKILL] = sg->sg_kill; 295#else 296 tp->t_erase = sg->sg_erase; 297 tp->t_kill = sg->sg_kill; 298#endif 299 return (0); 300 301 case TIOCFLUSH: 302 flags = *(int *)data; 303 if (flags == 0 || (flags & FREAD)) { 304 register struct clkdata *clk; 305 306 clk = (struct clkdata *) tp->T_LINEP; 307 if (clk->clk_cc > 0) 308 clk_bflush(clk); 309 } 310 return (-1); 311 312 default: 313 break; 314 } 315 return (ENOTTY); /* not quite appropriate */ 316} 317#endif NCLK 318