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