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