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