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