/* 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
* 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)
#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))
register struct clkdata
*clk
;
* Don't allow multiple opens. This will also protect us
* from someone opening /dev/tty
if (tp
->t_line
== CLKLDISC
)
for (clk
= clk_data
; clk
< &clk_data
[NCLK
]; clk
++)
if (clk
>= &clk_data
[NCLK
])
clk
->clk_cf
= clk
->clk_cl
= NULL
;
tp
->T_LINEP
= (caddr_t
) clk
;
* Break down... called when discipline changed or from device
register struct clkdata
*clk
;
register int s
= spltty();
clk
= (struct clkdata
*)tp
->T_LINEP
;
tp
->t_line
= 0; /* paranoid: avoid races */
* 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.
if (tp
->t_flags
& CRMOD
) {
register struct clkdata
*clk
;
ndflush(&tp
->t_rawq
, tp
->t_rawq
.c_cc
);
clk
= (struct clkdata
*) tp
->T_LINEP
;
* 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.
register struct clkdata
*clk
;
* Check to see whether this isn't the magic character. If not,
* save the character and return.
if (c
!= tp
->t_cc
[VERASE
] && c
!= tp
->t_cc
[VKILL
]) {
if (c
!= tp
->t_erase
&& c
!= tp
->t_kill
) {
clk
= (struct clkdata
*) tp
->T_LINEP
;
if (clk
->clk_cc
>= CLKLINESIZE
)
if (putc(c
, &clk
->clkbuf
) == -1) {
* Hopeless, no clists. Flush what we have
* and hope things improve.
* Here we have a magic character. Get a timestamp and store
clk
= (struct clkdata
*) tp
->T_LINEP
;
if (putc(c
, &clk
->clkbuf
) == -1)
for (i
= 0; i
< sizeof(long); i
++) {
if (putc((s
>> 24) & 0xff, &clk
->clkbuf
) == -1)
for (i
= 0; i
< sizeof(long); i
++) {
if (putc((s
>> 24) & 0xff, &clk
->clkbuf
) == -1)
* 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.
if (tp
->t_rawq
.c_cc
<= 0) {
tp
->t_rawq
= clk
->clkbuf
;
clk
->clk_cl
= clk
->clk_cf
= NULL
;
catq(&clk
->clkbuf
, &tp
->t_rawq
);
* It would be nice if this never happened. Flush the
* internal clists and hope someone else frees some of them
* Handle ioctls. We reject most tty-style except those that
* change the line discipline and a couple of others..
clkioctl(tp
, cmd
, data
, flag
)
* He likely wants to set new magic characters in.
sg
= (struct sgttyb
*)data
;
tp
->t_cc
[VERASE
] = sg
->sg_erase
;
tp
->t_cc
[VKILL
] = sg
->sg_kill
;
tp
->t_erase
= sg
->sg_erase
;
tp
->t_kill
= sg
->sg_kill
;
if (flags
== 0 || (flags
& FREAD
)) {
register struct clkdata
*clk
;
clk
= (struct clkdata
*) tp
->T_LINEP
;
return (ENOTTY
); /* not quite appropriate */