1/* $NetBSD$ */ 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