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