1/*	$NetBSD: clktest.c,v 1.1.1.1 2009/12/13 16:53:41 kardel Exp $	*/
2
3/* clktest.c,v 3.1 1993/07/06 01:05:23 jbj Exp
4 * clktest - test the clock line discipline
5 *
6 * usage: clktest -b bps -f -t timeo -s cmd -c char1 -a char2 /dev/whatever
7 */
8
9#include "clktest-opts.h"
10
11#define	STREQ(a, b)	(*(a) == *(b) && strcmp((a), (b)) == 0)
12
13#if defined(ULT_2_0_SUCKS)
14#ifndef sigmask
15#define	sigmask(m)	(1<<(m))
16#endif
17#endif
18
19#ifndef STREAM
20# ifndef CLKLDISC
21    CLOCK_LINE_DISCIPLINE_NEEDED_BY_THIS_PROGRAM;
22# endif
23#else
24# ifdef CLKLDISC
25    ONLY_ONE_CLOCK_LINE_DISCIPLINE_FOR_THIS_PROGRAM;
26# endif
27#endif
28
29/*
30 * Mask for blocking SIGIO and SIGALRM
31 */
32#define	BLOCKSIGMASK	(sigmask(SIGIO)|sigmask(SIGALRM))
33
34#define progname clktestOptions.pzProgName
35
36struct timeval timeout = { 0 };
37char *cmd = NULL;
38int cmdlen;
39
40#ifdef CLKLDISC
41u_long magic1 = DEFMAGIC;
42u_long magic2 = DEFMAGIC;
43#endif
44
45int speed = B9600;
46int ttflags = RAW|EVENP|ODDP;
47
48volatile int wasalarmed;
49volatile int iosig;
50
51struct timeval lasttv;
52
53extern u_long ustotslo[];
54extern u_long ustotsmid[];
55extern u_long ustotshi[];
56
57int alarming();
58int ioready();
59
60/*
61 * main - parse arguments and handle options
62 */
63int
64main(
65	int argc,
66	char *argv[]
67	)
68{
69	int fd;
70	struct sgttyb ttyb;
71	struct itimerval itimer;
72
73#ifdef STREAM
74	magic[0] = 0;
75#endif
76
77	{
78	    int ct = optionProcess( &clktestOptions, argc, argv );
79	    if (HAVE_OPT(COMMAND) && (strlen(OPT_ARG(COMMAND)) == 0)) {
80		fputs( "The command option string must not be empty\n", stderr );
81		USAGE( EXIT_FAILURE );
82	    }
83
84	    if ((argc -= ct) != 1) {
85		fputs( "Missing tty device name\n", stderr );
86		USAGE( EXIT_FAILURE );
87	    }
88	    argv += ct;
89	}
90#ifdef STREAM
91	if (!strlen(magic))
92	    strcpy(magic,DEFMAGIC);
93#endif
94
95	fd = open(*argv, HAVE_OPT(TIMEOUT) ? O_RDWR : O_RDONLY, 0777);
96	if (fd == -1) {
97		fprintf(stderr, "%s: open(%s): ", progname, *argv);
98		perror("");
99		exit(1);
100	}
101
102	if (ioctl(fd, TIOCEXCL, (char *)0) < 0) {
103		(void) fprintf(stderr, "%s: ioctl(TIOCEXCL): ", progname);
104		perror("");
105		exit(1);
106	}
107
108	/*
109	 * If we have the clock discipline, set the port to raw.  Otherwise
110	 * we run cooked.
111	 */
112	ttyb.sg_ispeed = ttyb.sg_ospeed = speed;
113#ifdef CLKLDISC
114	ttyb.sg_erase = (char)magic1;
115	ttyb.sg_kill = (char)magic2;
116#endif
117	ttyb.sg_flags = (short)ttflags;
118	if (ioctl(fd, TIOCSETP, (char *)&ttyb) < 0) {
119		(void) fprintf(stderr, "%s: ioctl(TIOCSETP): ", progname);
120		perror("");
121		exit(1);
122	}
123
124	if (fcntl(fd, F_SETOWN, getpid()) == -1) {
125		(void) fprintf(stderr, "%s: fcntl(F_SETOWN): ", progname);
126		perror("");
127		exit(1);
128	}
129
130#ifdef CLKLDISC
131	{
132		int ldisc;
133		ldisc = CLKLDISC;
134		if (ioctl(fd, TIOCSETD, (char *)&ldisc) < 0) {
135			(void) fprintf(stderr, "%s: ioctl(TIOCSETD): ", progname);
136			perror("");
137			exit(1);
138		}
139	}
140#endif
141#ifdef STREAM
142	if (ioctl(fd, I_POP, 0) >=0 ) ;
143	if (ioctl(fd, I_PUSH, "clk") < 0) {
144		(void) fprintf(stderr, "%s: ioctl(I_PUSH): ", progname);
145		perror("");
146		exit(1);
147	}
148	if (ioctl(fd, CLK_SETSTR, magic) < 0) {
149		(void) fprintf(stderr, "%s: ioctl(CLK_SETSTR): ", progname);
150		perror("");
151		exit(1);
152	}
153#endif
154
155
156	(void) gettimeofday(&lasttv, (struct timezone *)0);
157	if (HAVE_OPT(TIMEOUT)) {
158		/*
159		 * set non-blocking, async I/O on the descriptor
160		 */
161		iosig = 0;
162		(void) signal(SIGIO, ioready);
163		if (fcntl(fd, F_SETFL, FNDELAY|FASYNC) < 0) {
164			(void) fprintf(stderr, "%s: fcntl(F_SETFL): ",
165				       progname);
166			perror("");
167			exit(1);
168		}
169
170		/*
171		 * Set up the alarm interrupt.
172		 */
173		wasalarmed = 0;
174		(void) signal(SIGALRM, alarming);
175		timeout.tv_sec = OPT_VALUE_TIMEOUT;
176		itimer.it_interval = itimer.it_value = timeout;
177		setitimer(ITIMER_REAL, &itimer, (struct itimerval *)0);
178		doboth(fd);
179	}
180	doioonly(fd);
181}
182
183
184/*
185 * doboth - handle both I/O and alarms via SIGIO
186 */
187int
188doboth(
189	int fd
190	)
191{
192	int n;
193	int sawalarm;
194	int sawiosig;
195	int omask;
196	fd_set fds;
197	struct timeval tvzero;
198
199	sawalarm = 0;
200	sawiosig = 0;
201	FD_ZERO(&fds);
202	for (;;) {
203		omask = sigblock(BLOCKSIGMASK);
204		if (wasalarmed) {		/* alarmed? */
205			sawalarm = 1;
206			wasalarmed = 0;
207		}
208		if (iosig) {
209			sawiosig = 1;
210			iosig = 0;
211		}
212
213		if (!sawalarm && !sawiosig) {
214			/*
215			 * Nothing to do.  Wait for something.
216			 */
217			sigpause(omask);
218			if (wasalarmed) {		/* alarmed? */
219				sawalarm = 1;
220				wasalarmed = 0;
221			}
222			if (iosig) {
223				sawiosig = 1;
224				iosig = 0;
225			}
226		}
227		(void)sigsetmask(omask);
228
229		if (sawiosig) {
230
231			do {
232				tvzero.tv_sec = tvzero.tv_usec = 0;
233				FD_SET(fd, &fds);
234				n = select(fd+1, &fds, (fd_set *)0,
235					   (fd_set *)0, &tvzero);
236				if (n > 0)
237				    doio(fd);
238			} while (n > 0);
239
240			if (n == -1) {
241				(void) fprintf(stderr, "%s: select: ",
242					       progname);
243				perror("");
244				exit(1);
245			}
246			sawiosig = 0;
247		}
248		if (sawalarm) {
249			doalarm(fd);
250			sawalarm = 0;
251		}
252	}
253}
254
255
256/*
257 * doioonly - do I/O.  This avoids the use of signals
258 */
259int
260doioonly(
261	int fd
262	)
263{
264	int n;
265	fd_set fds;
266
267	FD_ZERO(&fds);
268	for (;;) {
269		FD_SET(fd, &fds);
270		n = select(fd+1, &fds, (fd_set *)0, (fd_set *)0,
271			   (struct timeval *)0);
272		if (n > 0)
273		    doio(fd);
274	}
275}
276
277
278/*
279 * doio - read a buffer full of stuff and print it out
280 */
281int
282doio(
283	int fd
284	)
285{
286	register char *rp, *rpend;
287	register char *cp;
288	register int i;
289	char raw[512];
290	struct timeval tv, tvd;
291	int rlen;
292	int ind;
293	char cooked[2049];
294	static char *digits = "0123456789abcdef";
295
296	rlen = read(fd, raw, sizeof(raw));
297	if (rlen < 0) {
298		(void) fprintf(stderr, "%s: read(): ", progname);
299		perror("");
300		return;
301	}
302	if (rlen == 0) {
303		(void) printf("Zero length read\n");
304		return;
305	}
306
307	cp = cooked;
308	rp = raw;
309	rpend = &raw[rlen];
310	ind = 0;
311
312	while (rp < rpend) {
313		ind = 1;
314		if (isprint(*rp))
315		    *cp++ = *rp;
316		else {
317			*cp++ = '<';
318			*cp++ = digits[((*rp)>>4) & 0xf];
319			*cp++ = digits[*rp & 0xf];
320			*cp++ = '>';
321		}
322		if (
323#ifdef CLKLDISC
324			(*rp == (char)magic1 || *rp == (char)magic2)
325#else
326			( strchr( magic, *rp) != NULL )
327#endif
328			) {
329			rp++;
330			ind = 0;
331			*cp = '\0';
332			if ((rpend - rp) < sizeof(struct timeval)) {
333				(void)printf(
334					"Too little data (%d): %s\n",
335					rpend-rp, cooked);
336				return;
337			}
338
339			tv.tv_sec = 0;
340			for (i = 0; i < 4; i++) {
341				tv.tv_sec <<= 8;
342				tv.tv_sec |= ((long)*rp++) & 0xff;
343			}
344			tv.tv_usec = 0;
345			for (i = 0; i < 4; i++) {
346				tv.tv_usec <<= 8;
347				tv.tv_usec |= ((long)*rp++) & 0xff;
348			}
349
350			tvd.tv_sec = tv.tv_sec - lasttv.tv_sec;
351			tvd.tv_usec = tv.tv_usec - lasttv.tv_usec;
352			if (tvd.tv_usec < 0) {
353				tvd.tv_usec += 1000000;
354				tvd.tv_sec--;
355			}
356
357			(void)printf("%lu.%06lu %lu.%06lu %s\n",
358				     tv.tv_sec, tv.tv_usec, tvd.tv_sec, tvd.tv_usec,
359				     cooked);
360			lasttv = tv;
361		} else {
362			rp++;
363		}
364	}
365
366	if (ind) {
367		*cp = '\0';
368		(void)printf("Incomplete data: %s\n", cooked);
369	}
370}
371
372
373/*
374 * doalarm - send a string out the port, if we have one.
375 */
376int
377doalarm(
378	int fd
379	)
380{
381	int n;
382
383	if (! HAVE_OPT(COMMAND))
384	    return;
385
386	n = write(fd, cmd, cmdlen);
387
388	if (n < 0) {
389		(void) fprintf(stderr, "%s: write(): ", progname);
390		perror("");
391	} else if (n < cmdlen) {
392		(void) printf("Short write (%d bytes, should be %d)\n",
393			      n, cmdlen);
394	}
395}
396
397
398/*
399 * alarming - receive alarm interupt
400 */
401void
402alarming(void)
403{
404	wasalarmed = 1;
405}
406
407/*
408 * ioready - handle SIGIO interrupt
409 */
410void
411ioready(void)
412{
413	iosig = 1;
414}
415