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