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