1 /* $OpenBSD: radioctl.c,v 1.18 2013/11/26 19:25:38 deraadt Exp $ */ 2 /* $RuOBSD: radioctl.c,v 1.4 2001/10/20 18:09:10 pva Exp $ */ 3 4 /* 5 * Copyright (c) 2001 Vladimir Popov <jumbo@narod.ru> 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 21 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 22 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 23 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 24 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 25 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 26 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 #include <sys/ioctl.h> 30 #include <sys/radioio.h> 31 32 #include <dev/ic/bt8xx.h> 33 34 #include <err.h> 35 #include <fcntl.h> 36 #include <stdio.h> 37 #include <stdlib.h> 38 #include <string.h> 39 #include <unistd.h> 40 #include <ctype.h> 41 42 #define RADIO_ENV "RADIODEVICE" 43 #define RADIODEVICE "/dev/radio" 44 45 const char *varname[] = { 46 "search", 47 #define OPTION_SEARCH 0x00 48 "volume", 49 #define OPTION_VOLUME 0x01 50 "frequency", 51 #define OPTION_FREQUENCY 0x02 52 "mute", 53 #define OPTION_MUTE 0x03 54 "reference", 55 #define OPTION_REFERENCE 0x04 56 "mono", 57 #define OPTION_MONO 0x05 58 "stereo", 59 #define OPTION_STEREO 0x06 60 "sensitivity", 61 #define OPTION_SENSITIVITY 0x07 62 "channel", 63 #define OPTION_CHANNEL 0x08 64 "chnlset" 65 #define OPTION_CHNLSET 0x09 66 }; 67 68 #define OPTION_NONE ~0u 69 #define VALUE_NONE ~0u 70 71 struct opt_t { 72 char *string; 73 int option; 74 int sign; 75 #define SIGN_NONE 0 76 #define SIGN_PLUS 1 77 #define SIGN_MINUS -1 78 u_int32_t value; 79 }; 80 81 struct chansets { 82 int value; 83 char *name; 84 } chansets[] = { 85 { CHNLSET_NABCST, "nabcst", }, 86 { CHNLSET_CABLEIRC, "cableirc", }, 87 { CHNLSET_CABLEHRC, "cablehrc", }, 88 { CHNLSET_WEUROPE, "weurope", }, 89 { CHNLSET_JPNBCST, "jpnbcst", }, 90 { CHNLSET_JPNCABLE, "jpncable", }, 91 { CHNLSET_XUSSR, "xussr", }, 92 { CHNLSET_AUSTRALIA, "australia", }, 93 { CHNLSET_FRANCE, "france", }, 94 { 0, NULL } 95 }; 96 97 extern char *__progname; 98 const char *onchar = "on"; 99 #define ONCHAR_LEN 2 100 const char *offchar = "off"; 101 #define OFFCHAR_LEN 3 102 103 struct radio_info ri; 104 unsigned int i = 0; 105 106 int parse_opt(char *, struct opt_t *); 107 108 void print_vars(int, int); 109 void do_ioctls(int, struct opt_t *, int); 110 111 void print_value(int, int); 112 void change_value(const struct opt_t); 113 void update_value(int, int *, int); 114 115 void warn_unsupported(int); 116 void usage(void); 117 118 void show_verbose(const char *, int); 119 void show_int_val(int, const char *, char *, int); 120 void show_float_val(float, const char *, char *, int); 121 void show_char_val(const char *, const char *, int); 122 int str_to_opt(const char *); 123 u_int str_to_int(char *, int); 124 125 /* 126 * Control behavior of a FM tuner - set frequency, volume etc 127 */ 128 int 129 main(int argc, char **argv) 130 { 131 struct opt_t opt; 132 char **avp; 133 134 char *radiodev = NULL; 135 int rd = -1; 136 int optchar; 137 int show_vars = 0; 138 int show_choices = 0; 139 int silent = 0; 140 int mode = O_RDONLY; 141 142 radiodev = getenv(RADIO_ENV); 143 if (radiodev == NULL) 144 radiodev = RADIODEVICE; 145 146 while ((optchar = getopt(argc, argv, "af:nvw")) != -1) { 147 switch (optchar) { 148 case 'a': 149 show_vars = 1; 150 break; 151 case 'f': 152 radiodev = optarg; 153 break; 154 case 'n': 155 silent = 1; 156 break; 157 case 'v': 158 show_choices = 1; 159 break; 160 case 'w': 161 /* backwards compatibility */ 162 break; 163 default: 164 usage(); 165 /* NOTREACHED */ 166 } 167 } 168 169 argc -= optind; 170 argv += optind; 171 172 if (argc == 0) 173 show_vars = 1; 174 175 /* 176 * Scan the options for `name=value` so the 177 * device can be opened in the proper mode. 178 */ 179 for (avp = argv; *avp != NULL; avp++) 180 if (strchr(*avp, '=') != NULL) { 181 mode = O_RDWR; 182 break; 183 } 184 185 rd = open(radiodev, mode); 186 if (rd < 0) 187 err(1, "%s open error", radiodev); 188 189 if (ioctl(rd, RIOCGINFO, &ri) < 0) 190 err(1, "RIOCGINFO"); 191 192 if (!argc && show_vars) 193 print_vars(silent, show_choices); 194 else if (argc > 0 && !show_vars) { 195 if (mode == O_RDWR) { 196 for (; argc--; argv++) 197 if (parse_opt(*argv, &opt)) 198 do_ioctls(rd, &opt, silent); 199 } else { 200 for (; argc--; argv++) 201 if (parse_opt(*argv, &opt)) { 202 show_verbose(varname[opt.option], 203 silent); 204 print_value(opt.option, show_choices); 205 free(opt.string); 206 putchar('\n'); 207 } 208 } 209 } 210 211 if (close(rd) < 0) 212 warn("%s close error", radiodev); 213 214 return 0; 215 } 216 217 void 218 usage(void) 219 { 220 fprintf(stderr, 221 "usage: %s [-anv] [-f file]\n" 222 " %s [-nv] [-f file] name\n" 223 " %s [-n] [-f file] name=value\n", 224 __progname, __progname, __progname); 225 exit(1); 226 } 227 228 void 229 show_verbose(const char *nick, int silent) 230 { 231 if (!silent) 232 printf("%s=", nick); 233 } 234 235 void 236 warn_unsupported(int optval) 237 { 238 warnx("driver does not support `%s'", varname[optval]); 239 } 240 241 void 242 do_ioctls(int fd, struct opt_t *o, int silent) 243 { 244 int oval; 245 246 if (fd < 0 || o == NULL) 247 return; 248 249 if (o->option == OPTION_SEARCH && !(ri.caps & RADIO_CAPS_HW_SEARCH)) { 250 warn_unsupported(o->option); 251 return; 252 } 253 254 oval = o->option == OPTION_SEARCH ? OPTION_FREQUENCY : o->option; 255 if (!silent) 256 printf("%s: ", varname[oval]); 257 258 print_value(o->option, 0); 259 printf(" -> "); 260 261 if (o->option == OPTION_SEARCH) { 262 263 if (ioctl(fd, RIOCSSRCH, &o->value) < 0) { 264 warn("RIOCSSRCH"); 265 return; 266 } 267 268 } else { 269 270 change_value(*o); 271 if (ioctl(fd, RIOCSINFO, &ri) < 0) { 272 warn("RIOCSINFO"); 273 return; 274 } 275 276 } 277 278 if (ioctl(fd, RIOCGINFO, &ri) < 0) { 279 warn("RIOCGINFO"); 280 return; 281 } 282 283 print_value(o->option, 0); 284 putchar('\n'); 285 } 286 287 void 288 change_value(const struct opt_t o) 289 { 290 int unsupported = 0; 291 292 if (o.value == VALUE_NONE) 293 return; 294 295 switch (o.option) { 296 case OPTION_VOLUME: 297 update_value(o.sign, &ri.volume, o.value); 298 break; 299 case OPTION_FREQUENCY: 300 ri.tuner_mode = RADIO_TUNER_MODE_RADIO; 301 update_value(o.sign, &ri.freq, o.value); 302 break; 303 case OPTION_REFERENCE: 304 if (ri.caps & RADIO_CAPS_REFERENCE_FREQ) 305 update_value(o.sign, &ri.rfreq, o.value); 306 else 307 unsupported++; 308 break; 309 case OPTION_MONO: 310 /* FALLTHROUGH */ 311 case OPTION_STEREO: 312 if (ri.caps & RADIO_CAPS_SET_MONO) 313 ri.stereo = o.option == OPTION_MONO ? !o.value : o.value; 314 else 315 unsupported++; 316 break; 317 case OPTION_SENSITIVITY: 318 if (ri.caps & RADIO_CAPS_LOCK_SENSITIVITY) 319 update_value(o.sign, &ri.lock, o.value); 320 else 321 unsupported++; 322 break; 323 case OPTION_MUTE: 324 ri.mute = o.value; 325 break; 326 case OPTION_CHANNEL: 327 ri.tuner_mode = RADIO_TUNER_MODE_TV; 328 update_value(o.sign, &ri.chan, o.value); 329 break; 330 case OPTION_CHNLSET: 331 ri.chnlset = o.value; 332 break; 333 } 334 335 if (unsupported) 336 warn_unsupported(o.option); 337 } 338 339 /* 340 * Convert string to integer representation of a parameter 341 */ 342 int 343 str_to_opt(const char *topt) 344 { 345 int res, toptlen, varlen, len, varsize; 346 347 if (topt == NULL || *topt == '\0') 348 return OPTION_NONE; 349 350 varsize = sizeof(varname) / sizeof(varname[0]); 351 toptlen = strlen(topt); 352 353 for (res = 0; res < varsize; res++) { 354 varlen = strlen(varname[res]); 355 len = toptlen > varlen ? toptlen : varlen; 356 if (strncmp(topt, varname[res], len) == 0) 357 return res; 358 } 359 360 warnx("bad name `%s'", topt); 361 return OPTION_NONE; 362 } 363 364 void 365 update_value(int sign, int *value, int update) 366 { 367 switch (sign) { 368 case SIGN_NONE: 369 *value = update; 370 break; 371 case SIGN_PLUS: 372 *value += update; 373 break; 374 case SIGN_MINUS: 375 *value -= update; 376 break; 377 } 378 } 379 380 /* 381 * Convert string to unsigned integer 382 */ 383 u_int 384 str_to_int(char *str, int optval) 385 { 386 int val; 387 388 if (str == NULL || *str == '\0') 389 return VALUE_NONE; 390 391 if (optval == OPTION_FREQUENCY) 392 val = (int)(1000 * atof(str)); 393 else 394 val = (int)strtol(str, (char **)NULL, 10); 395 396 return val; 397 } 398 399 /* 400 * parse string s into struct opt_t 401 * return true on success, false on failure 402 */ 403 int 404 parse_opt(char *s, struct opt_t *o) { 405 const char *badvalue = "bad value `%s'"; 406 char *topt = NULL; 407 int slen, optlen; 408 409 if (s == NULL || *s == '\0' || o == NULL) 410 return 0; 411 412 o->string = NULL; 413 o->option = OPTION_NONE; 414 o->value = VALUE_NONE; 415 o->sign = SIGN_NONE; 416 417 slen = strlen(s); 418 optlen = strcspn(s, "="); 419 420 /* Set only o->optval, the rest is missing */ 421 if (slen == optlen) { 422 o->option = str_to_opt(s); 423 return o->option == OPTION_NONE ? 0 : 1; 424 } 425 426 if (optlen > slen - 2) { 427 warnx(badvalue, s); 428 return 0; 429 } 430 431 slen -= ++optlen; 432 433 if ((topt = malloc(optlen)) == NULL) { 434 warn("memory allocation error"); 435 return 0; 436 } 437 strlcpy(topt, s, optlen); 438 439 if ((o->option = str_to_opt(topt)) == OPTION_NONE) { 440 free(topt); 441 return 0; 442 } 443 o->string = topt; 444 445 topt = &s[optlen]; 446 447 if (strcmp(o->string, "chnlset") == 0) { 448 for (i = 0; chansets[i].name; i++) 449 if (strncmp(chansets[i].name, topt, 450 strlen(chansets[i].name)) == 0) 451 break; 452 if (chansets[i].name != NULL) { 453 o->value = chansets[i].value; 454 return 1; 455 } else { 456 warnx(badvalue, topt); 457 return 0; 458 } 459 } 460 461 switch (*topt) { 462 case '+': 463 case '-': 464 o->sign = (*topt == '+') ? SIGN_PLUS : SIGN_MINUS; 465 o->value = str_to_int(&topt[1], o->option); 466 break; 467 case 'o': 468 if (strncmp(topt, offchar, 469 slen > OFFCHAR_LEN ? slen : OFFCHAR_LEN) == 0) 470 o->value = 0; 471 else if (strncmp(topt, onchar, 472 slen > ONCHAR_LEN ? slen : ONCHAR_LEN) == 0) 473 o->value = 1; 474 break; 475 case 'u': 476 if (strncmp(topt, "up", slen > 2 ? slen : 2) == 0) 477 o->value = 1; 478 break; 479 case 'd': 480 if (strncmp(topt, "down", slen > 4 ? slen : 4) == 0) 481 o->value = 0; 482 break; 483 default: 484 if (isdigit((unsigned char)*topt)) 485 o->value = str_to_int(topt, o->option); 486 break; 487 } 488 489 if (o->value == VALUE_NONE) { 490 warnx(badvalue, topt); 491 return 0; 492 } 493 494 return 1; 495 } 496 497 /* 498 * Print current value of the parameter. 499 */ 500 void 501 print_value(int optval, int show_choices) 502 { 503 if (optval == OPTION_NONE) 504 return; 505 506 switch (optval) { 507 case OPTION_SEARCH: 508 /* FALLTHROUGH */ 509 case OPTION_FREQUENCY: 510 printf("%.2fMHz", (float)ri.freq / 1000.); 511 break; 512 case OPTION_REFERENCE: 513 printf("%ukHz", ri.rfreq); 514 break; 515 case OPTION_SENSITIVITY: 516 printf("%umkV", ri.lock); 517 break; 518 case OPTION_MUTE: 519 printf(ri.mute ? onchar : offchar); 520 break; 521 case OPTION_MONO: 522 printf(ri.stereo ? offchar : onchar); 523 break; 524 case OPTION_STEREO: 525 printf(ri.stereo ? onchar : offchar); 526 break; 527 case OPTION_CHANNEL: 528 printf("%u", ri.chan); 529 break; 530 case OPTION_CHNLSET: 531 for (i = 0; chansets[i].name; i++) { 532 if (chansets[i].value == ri.chnlset) 533 printf("%s", chansets[i].name); 534 } 535 if (show_choices) { 536 printf("\n\t["); 537 for (i = 0; chansets[i].name; i++) 538 printf("%s ", chansets[i].name); 539 printf("]"); 540 } 541 break; 542 case OPTION_VOLUME: 543 default: 544 printf("%u", ri.volume); 545 break; 546 } 547 } 548 549 void 550 show_int_val(int val, const char *nick, char *append, int silent) 551 { 552 show_verbose(nick, silent); 553 printf("%u%s\n", val, append); 554 } 555 556 void 557 show_float_val(float val, const char *nick, char *append, int silent) 558 { 559 show_verbose(nick, silent); 560 printf("%.2f%s\n", val, append); 561 } 562 563 void 564 show_char_val(const char *val, const char *nick, int silent) 565 { 566 show_verbose(nick, silent); 567 printf("%s\n", val); 568 } 569 570 /* 571 * Print all available parameters 572 */ 573 void 574 print_vars(int silent, int show_choices) 575 { 576 show_int_val(ri.volume, varname[OPTION_VOLUME], "", silent); 577 show_int_val(ri.chan, varname[OPTION_CHANNEL], "", silent); 578 for (i = 0; chansets[i].name; i++) { 579 if (chansets[i].value == ri.chnlset) 580 show_char_val(chansets[i].name, varname[OPTION_CHNLSET], silent); 581 } 582 if (show_choices) { 583 printf("\t[ "); 584 for (i = 0; chansets[i].name; i++) 585 printf("%s ", chansets[i].name); 586 printf("]\n"); 587 } 588 show_float_val((float)ri.freq / 1000., varname[OPTION_FREQUENCY], 589 "MHz", silent); 590 show_char_val(ri.mute ? onchar : offchar, varname[OPTION_MUTE], silent); 591 592 if (ri.caps & RADIO_CAPS_REFERENCE_FREQ) 593 show_int_val(ri.rfreq, varname[OPTION_REFERENCE], "kHz", silent); 594 if (ri.caps & RADIO_CAPS_LOCK_SENSITIVITY) 595 show_int_val(ri.lock, varname[OPTION_SENSITIVITY], "mkV", silent); 596 597 if (ri.caps & RADIO_CAPS_DETECT_SIGNAL) { 598 show_verbose("signal", silent); 599 printf("%s\n", ri.info & RADIO_INFO_SIGNAL ? onchar : offchar); 600 } 601 if (ri.caps & RADIO_CAPS_DETECT_STEREO) { 602 show_verbose(varname[OPTION_STEREO], silent); 603 printf("%s\n", ri.info & RADIO_INFO_STEREO ? onchar : offchar); 604 } 605 606 if (!silent) { 607 printf("mode: %s\n", 608 ri.tuner_mode == RADIO_TUNER_MODE_TV ? "TV" : "radio"); 609 610 puts("card capabilities:"); 611 } 612 613 if (ri.caps & RADIO_CAPS_SET_MONO) 614 puts("\tmanageable mono/stereo"); 615 if (ri.caps & RADIO_CAPS_HW_SEARCH) 616 puts("\thardware search"); 617 if (ri.caps & RADIO_CAPS_HW_AFC) 618 puts("\thardware AFC"); 619 } 620