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