Annotation of src/usr.bin/radioctl/radioctl.c, Revision 1.16
1.16 ! jmc 1: /* $OpenBSD: radioctl.c,v 1.15 2008/10/16 02:21:26 jakemsr 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.1 gluk 141: radiodev = getenv(RADIO_ENV);
142: if (radiodev == NULL)
143: radiodev = RADIODEVICE;
144:
1.12 robert 145: while ((optchar = getopt(argc, argv, "af:nvw")) != -1) {
1.1 gluk 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;
1.12 robert 156: case 'v':
157: show_choices = 1;
158: break;
1.1 gluk 159: case 'w':
1.9 jaredy 160: /* backwards compatibility */
1.1 gluk 161: break;
162: default:
163: usage();
164: /* NOTREACHED */
165: }
1.8 mickey 166: }
1.1 gluk 167:
1.8 mickey 168: argc -= optind;
169: argv += optind;
1.15 jakemsr 170:
171: if (argc == 0)
172: show_vars = 1;
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.14 sobrado 195: for (; argc--; argv++)
1.8 mickey 196: if (parse_opt(*argv, &opt))
197: do_ioctls(rd, &opt, silent);
198: } else {
1.14 sobrado 199: for (; argc--; argv++)
1.8 mickey 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.16 ! jmc 220: "usage: %s [-anv] [-f file]\n"
1.14 sobrado 221: " %s [-nv] [-f file] name\n"
222: " %s [-n] [-f file] name=value\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.13 jakemsr 299: ri.tuner_mode = RADIO_TUNER_MODE_RADIO;
1.11 robert 300: update_value(o.sign, &ri.freq, o.value);
1.3 mickey 301: break;
302: case OPTION_REFERENCE:
303: if (ri.caps & RADIO_CAPS_REFERENCE_FREQ)
1.11 robert 304: update_value(o.sign, &ri.rfreq, o.value);
1.1 gluk 305: else
1.3 mickey 306: unsupported++;
1.1 gluk 307: break;
1.3 mickey 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;
1.1 gluk 313: else
1.3 mickey 314: unsupported++;
1.1 gluk 315: break;
1.3 mickey 316: case OPTION_SENSITIVITY:
317: if (ri.caps & RADIO_CAPS_LOCK_SENSITIVITY)
1.11 robert 318: update_value(o.sign, &ri.lock, o.value);
1.3 mickey 319: else
320: unsupported++;
1.1 gluk 321: break;
1.3 mickey 322: case OPTION_MUTE:
323: ri.mute = o.value;
1.1 gluk 324: break;
1.12 robert 325: case OPTION_CHANNEL:
1.13 jakemsr 326: ri.tuner_mode = RADIO_TUNER_MODE_TV;
1.12 robert 327: update_value(o.sign, &ri.chan, o.value);
328: break;
329: case OPTION_CHNLSET:
330: ri.chnlset = o.value;
331: break;
1.1 gluk 332: }
333:
1.6 deraadt 334: if (unsupported)
1.3 mickey 335: warn_unsupported(o.option);
1.1 gluk 336: }
337:
338: /*
339: * Convert string to integer representation of a parameter
340: */
1.8 mickey 341: int
1.3 mickey 342: str_to_opt(const char *topt)
1.1 gluk 343: {
1.3 mickey 344: int res, toptlen, varlen, len, varsize;
1.1 gluk 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:
1.8 mickey 363: void
1.11 robert 364: update_value(int sign, int *value, int update)
1.1 gluk 365: {
1.3 mickey 366: switch (sign) {
367: case SIGN_NONE:
368: *value = update;
1.1 gluk 369: break;
1.3 mickey 370: case SIGN_PLUS:
371: *value += update;
1.1 gluk 372: break;
1.3 mickey 373: case SIGN_MINUS:
374: *value -= update;
1.1 gluk 375: break;
376: }
377: }
378:
379: /*
1.3 mickey 380: * Convert string to unsigned integer
1.1 gluk 381: */
1.11 robert 382: u_int
383: str_to_int(char *str, int optval)
1.1 gluk 384: {
1.11 robert 385: int val;
1.1 gluk 386:
387: if (str == NULL || *str == '\0')
388: return VALUE_NONE;
389:
390: if (optval == OPTION_FREQUENCY)
1.11 robert 391: val = (int)(1000 * atof(str));
1.1 gluk 392: else
1.11 robert 393: val = (int)strtol(str, (char **)NULL, 10);
1.1 gluk 394:
395: return val;
396: }
397:
398: /*
1.3 mickey 399: * parse string s into struct opt_t
400: * return true on success, false on failure
401: */
1.8 mickey 402: int
1.3 mickey 403: parse_opt(char *s, struct opt_t *o) {
404: const char *badvalue = "bad value `%s'";
405: char *topt = NULL;
406: int slen, optlen;
1.12 robert 407:
1.3 mickey 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];
1.12 robert 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:
1.3 mickey 460: switch (*topt) {
461: case '+':
462: case '-':
463: o->sign = (*topt == '+') ? SIGN_PLUS : SIGN_MINUS;
1.11 robert 464: o->value = str_to_int(&topt[1], o->option);
1.3 mickey 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)
1.11 robert 484: o->value = str_to_int(topt, o->option);
1.3 mickey 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: /*
1.1 gluk 497: * Print current value of the parameter.
498: */
1.8 mickey 499: void
1.12 robert 500: print_value(int optval, int show_choices)
1.1 gluk 501: {
502: if (optval == OPTION_NONE)
503: return;
504:
505: switch (optval) {
506: case OPTION_SEARCH:
507: /* FALLTHROUGH */
508: case OPTION_FREQUENCY:
1.3 mickey 509: printf("%.2fMHz", (float)ri.freq / 1000.);
1.1 gluk 510: break;
511: case OPTION_REFERENCE:
1.3 mickey 512: printf("%ukHz", ri.rfreq);
1.1 gluk 513: break;
514: case OPTION_SENSITIVITY:
1.3 mickey 515: printf("%umkV", ri.lock);
1.1 gluk 516: break;
517: case OPTION_MUTE:
1.3 mickey 518: printf(ri.mute ? onchar : offchar);
519: break;
1.1 gluk 520: case OPTION_MONO:
1.3 mickey 521: printf(ri.stereo ? offchar : onchar);
1.1 gluk 522: break;
523: case OPTION_STEREO:
1.3 mickey 524: printf(ri.stereo ? onchar : offchar);
1.1 gluk 525: break;
1.12 robert 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;
1.3 mickey 541: case OPTION_VOLUME:
1.1 gluk 542: default:
1.3 mickey 543: printf("%u", ri.volume);
1.1 gluk 544: break;
545: }
546: }
547:
1.8 mickey 548: void
1.11 robert 549: show_int_val(int val, const char *nick, char *append, int silent)
1.3 mickey 550: {
551: show_verbose(nick, silent);
1.11 robert 552: printf("%u%s\n", val, append);
1.3 mickey 553: }
554:
1.8 mickey 555: void
1.3 mickey 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:
1.8 mickey 562: void
1.3 mickey 563: show_char_val(const char *val, const char *nick, int silent)
1.1 gluk 564: {
1.3 mickey 565: show_verbose(nick, silent);
566: printf("%s\n", val);
1.1 gluk 567: }
568:
1.3 mickey 569: /*
570: * Print all available parameters
571: */
1.8 mickey 572: void
1.12 robert 573: print_vars(int silent, int show_choices)
1.1 gluk 574: {
1.3 mickey 575: show_int_val(ri.volume, varname[OPTION_VOLUME], "", silent);
1.12 robert 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: }
1.3 mickey 587: show_float_val((float)ri.freq / 1000., varname[OPTION_FREQUENCY],
1.6 deraadt 588: "MHz", silent);
1.3 mickey 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: }
1.1 gluk 604:
1.13 jakemsr 605: if (!silent) {
606: printf("mode: %s\n",
607: ri.tuner_mode == RADIO_TUNER_MODE_TV ? "TV" : "radio");
608:
1.3 mickey 609: puts("card capabilities:");
1.13 jakemsr 610: }
611:
1.3 mickey 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");
1.1 gluk 618: }