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