1/*	$NetBSD: tty_chu.c,v 1.1.1.1 2009/12/13 16:54:57 kardel Exp $	*/
2
3/* tty_chu.c,v 3.1 1993/07/06 01:07:30 jbj Exp
4 * tty_chu.c - CHU line driver
5 */
6
7#include "chu.h"
8#if NCHU > 0
9
10#include "../h/param.h"
11#include "../h/types.h"
12#include "../h/systm.h"
13#include "../h/dir.h"
14#include "../h/user.h"
15#include "../h/ioctl.h"
16#include "../h/tty.h"
17#include "../h/proc.h"
18#include "../h/file.h"
19#include "../h/conf.h"
20#include "../h/buf.h"
21#include "../h/uio.h"
22
23#include "../h/chudefs.h"
24
25/*
26 * Line discipline for receiving CHU time codes.
27 * Does elementary noise elimination, takes time stamps after
28 * the arrival of each character, returns a buffer full of the
29 * received 10 character code and the associated time stamps.
30 */
31#define	NUMCHUBUFS	3
32
33struct chudata {
34	u_char used;		/* Set to 1 when structure in use */
35	u_char lastindex;	/* least recently used buffer */
36	u_char curindex;	/* buffer to use */
37	u_char sleeping;	/* set to 1 when we're sleeping on a buffer */
38	struct chucode chubuf[NUMCHUBUFS];
39} chu_data[NCHU];
40
41/*
42 * Number of microseconds we allow between
43 * character arrivals.  The speed is 300 baud
44 * so this should be somewhat more than 30 msec
45 */
46#define	CHUMAXUSEC	(50*1000)	/* 50 msec */
47
48int chu_debug = 0;
49
50/*
51 * Open as CHU time discipline.  Called when discipline changed
52 * with ioctl, and changes the interpretation of the information
53 * in the tty structure.
54 */
55/*ARGSUSED*/
56chuopen(dev, tp)
57	dev_t dev;
58	register struct tty *tp;
59{
60	register struct chudata *chu;
61
62	/*
63	 * Don't allow multiple opens.  This will also protect us
64	 * from someone opening /dev/tty
65	 */
66	if (tp->t_line == CHULDISC)
67		return (EBUSY);
68	ttywflush(tp);
69	for (chu = chu_data; chu < &chu_data[NCHU]; chu++)
70		if (!chu->used)
71			break;
72	if (chu >= &chu[NCHU])
73		return (EBUSY);
74	chu->used++;
75	chu->lastindex = chu->curindex = 0;
76	chu->sleeping = 0;
77	chu->chubuf[0].ncodechars = 0;
78	tp->T_LINEP = (caddr_t) chu;
79	return (0);
80}
81
82/*
83 * Break down... called when discipline changed or from device
84 * close routine.
85 */
86chuclose(tp)
87	register struct tty *tp;
88{
89	register int s = spl5();
90
91	((struct chudata *) tp->T_LINEP)->used = 0;
92	tp->t_cp = 0;
93	tp->t_inbuf = 0;
94	tp->t_rawq.c_cc = 0;		/* clear queues -- paranoid */
95	tp->t_canq.c_cc = 0;
96	tp->t_line = 0;			/* paranoid: avoid races */
97	splx(s);
98}
99
100/*
101 * Read a CHU buffer.  Sleep on the current buffer
102 */
103churead(tp, uio)
104	register struct tty *tp;
105	struct uio *uio;
106{
107	register struct chudata *chu;
108	register struct chucode *chucode;
109	register int s;
110
111	if ((tp->t_state&TS_CARR_ON)==0)
112		return (EIO);
113
114	chu = (struct chudata *) (tp->T_LINEP);
115
116	s = spl5();
117	chucode = &(chu->chubuf[chu->lastindex]);
118	while (chu->curindex == chu->lastindex) {
119		chu->sleeping = 1;
120		sleep((caddr_t)chucode, TTIPRI);
121	}
122	chu->sleeping = 0;
123	if (++(chu->lastindex) >= NUMCHUBUFS)
124		chu->lastindex = 0;
125	splx(s);
126
127	return (uiomove((caddr_t)chucode, sizeof(*chucode), UIO_READ, uio));
128}
129
130/*
131 * Low level character input routine.
132 * If the character looks okay, grab a time stamp.  If the stuff in
133 * the buffer is too old, dump it and start fresh.  If the character is
134 * non-BCDish, everything in the buffer too.
135 */
136chuinput(c, tp)
137	register int c;
138	register struct tty *tp;
139{
140	register struct chudata *chu = (struct chudata *) tp->T_LINEP;
141	register struct chucode *chuc;
142	register int i;
143	long sec, usec;
144	struct timeval tv;
145
146	/*
147	 * Do a check on the BSDness of the character.  This delays
148	 * the time stamp a bit but saves a fair amount of overhead
149	 * when the static is bad.
150	 */
151	if (((c) & 0xf) > 9 || (((c)>>4) & 0xf) > 9) {
152		chuc = &(chu->chubuf[chu->curindex]);
153		chuc->ncodechars = 0;	/* blow all previous away */
154		return;
155	}
156
157	/*
158	 * Call microtime() to get the current time of day
159	 */
160	microtime(&tv);
161
162	/*
163	 * Compute the difference in this character's time stamp
164	 * and the last.  If it exceeds the margin, blow away all
165	 * the characters currently in the buffer.
166	 */
167	chuc = &(chu->chubuf[chu->curindex]);
168	i = (int)chuc->ncodechars;
169	if (i > 0) {
170		sec = tv.tv_sec - chuc->codetimes[i-1].tv_sec;
171		usec = tv.tv_usec - chuc->codetimes[i-1].tv_usec;
172		if (usec < 0) {
173			sec -= 1;
174			usec += 1000000;
175		}
176		if (sec != 0 || usec > CHUMAXUSEC) {
177			i = 0;
178			chuc->ncodechars = 0;
179		}
180	}
181
182	/*
183	 * Store the character.  If we're done, have to tell someone
184	 */
185	chuc->codechars[i] = (u_char)c;
186	chuc->codetimes[i] = tv;
187
188	if (++i < NCHUCHARS) {
189		/*
190		 * Not much to do here.  Save the count and wait
191		 * for another character.
192		 */
193		chuc->ncodechars = (u_char)i;
194	} else {
195		/*
196		 * Mark this buffer full and point at next.  If the
197		 * next buffer is full we overwrite it by bumping the
198		 * next pointer.
199		 */
200		chuc->ncodechars = NCHUCHARS;
201		if (++(chu->curindex) >= NUMCHUBUFS)
202			chu->curindex = 0;
203		if (chu->curindex == chu->lastindex)
204			if (++(chu->lastindex) >= NUMCHUBUFS)
205				chu->lastindex = 0;
206		chu->chubuf[chu->curindex].ncodechars = 0;
207
208		/*
209		 * Wake up anyone sleeping on this.  Also wake up
210		 * selectors and/or deliver a SIGIO as required.
211		 */
212		if (tp->t_rsel) {
213			selwakeup(tp->t_rsel, tp->t_state&TS_RCOLL);
214			tp->t_state &= ~TS_RCOLL;
215			tp->t_rsel = 0;
216		}
217		if (tp->t_state & TS_ASYNC)
218			gsignal(tp->t_pgrp, SIGIO);
219		if (chu->sleeping)
220			(void) wakeup((caddr_t)chuc);
221	}
222}
223
224/*
225 * Handle ioctls.  We reject all tty-style except those that
226 * change the line discipline.
227 */
228chuioctl(tp, cmd, data, flag)
229	struct tty *tp;
230	int cmd;
231	caddr_t data;
232	int flag;
233{
234
235	if ((cmd>>8) != 't')
236		return (-1);
237	switch (cmd) {
238	case TIOCSETD:
239	case TIOCGETD:
240	case TIOCGETP:
241	case TIOCGETC:
242		return (-1);
243	}
244	return (ENOTTY);	/* not quite appropriate */
245}
246
247
248chuselect(dev, rw)
249	dev_t dev;
250	int rw;
251{
252	register struct tty *tp = &cdevsw[major(dev)].d_ttys[minor(dev)];
253	struct chudata *chu;
254	int s = spl5();
255
256	chu = (struct chudata *) (tp->T_LINEP);
257
258	switch (rw) {
259
260	case FREAD:
261		if (chu->curindex != chu->lastindex)
262			goto win;
263		if (tp->t_rsel && tp->t_rsel->p_wchan == (caddr_t)&selwait)
264			tp->t_state |= TS_RCOLL;
265		else
266			tp->t_rsel = u.u_procp;
267		break;
268
269	case FWRITE:
270		goto win;
271	}
272	splx(s);
273	return (0);
274win:
275	splx(s);
276	return (1);
277}
278#endif NCHU
279