Annotation of src/usr.bin/radioctl/radioctl.c, Revision 1.8
1.8 ! mickey 1: /* $OpenBSD: radioctl.c,v 1.7 2002/01/02 19:18:55 mickey 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);
! 89: void update_value(int, u_long *, u_long);
! 90:
! 91: void warn_unsupported(int);
! 92: void usage(void);
! 93:
! 94: void show_verbose(const char *, int);
! 95: void show_int_val(u_long, const char *, char *, int);
! 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 *);
! 99: u_long str_to_long(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;
108:
1.1 gluk 109: char *radiodev = NULL;
1.3 mickey 110: int rd = -1;
1.7 mickey 111: int optchar;
1.1 gluk 112: int show_vars = 0;
113: int set_param = 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':
136: set_param = 1;
1.8 ! mickey 137: mode = O_RDWR;
1.1 gluk 138: break;
139: default:
140: usage();
141: /* NOTREACHED */
142: }
1.8 ! mickey 143: }
1.1 gluk 144:
1.8 ! mickey 145: argc -= optind;
! 146: argv += optind;
1.1 gluk 147:
1.8 ! mickey 148: rd = open(radiodev, mode);
1.1 gluk 149: if (rd < 0)
150: err(1, "%s open error", radiodev);
151:
1.3 mickey 152: if (ioctl(rd, RIOCGINFO, &ri) < 0)
153: err(1, "RIOCGINFO");
1.1 gluk 154:
1.8 ! mickey 155: if (!argc && show_vars && !set_param)
! 156: print_vars(silent);
! 157: else if (argc > 0 && !show_vars) {
! 158: if (set_param) {
! 159: for(; argc--; argv++)
! 160: if (parse_opt(*argv, &opt))
! 161: do_ioctls(rd, &opt, silent);
! 162: } else {
! 163: for(; argc--; argv++)
! 164: if (parse_opt(*argv, &opt)) {
! 165: show_verbose(varname[opt.option],
! 166: silent);
! 167: print_value(opt.option);
! 168: free(opt.string);
! 169: putchar('\n');
! 170: }
1.3 mickey 171: }
1.8 ! mickey 172: }
1.1 gluk 173:
174: if (close(rd) < 0)
175: warn("%s close error", radiodev);
176:
177: return 0;
178: }
179:
1.8 ! mickey 180: void
1.1 gluk 181: usage(void)
182: {
1.7 mickey 183: fprintf(stderr, "usage: %s [-f file] [-n] variable ...\n"
184: " %s [-f file] [-n] -w variable=value ...\n"
185: " %s [-f file] [-n] -a\n",
186: __progname, __progname, __progname);
187: exit(1);
1.1 gluk 188: }
189:
1.8 ! mickey 190: void
1.3 mickey 191: show_verbose(const char *nick, int silent)
1.1 gluk 192: {
1.3 mickey 193: if (!silent)
194: printf("%s=", nick);
195: }
1.1 gluk 196:
1.8 ! mickey 197: void
1.3 mickey 198: warn_unsupported(int optval)
199: {
200: warnx("driver does not support `%s'", varname[optval]);
1.1 gluk 201: }
202:
1.8 ! mickey 203: void
1.3 mickey 204: do_ioctls(int fd, struct opt_t *o, int silent)
1.1 gluk 205: {
1.3 mickey 206: int oval;
1.1 gluk 207:
1.3 mickey 208: if (fd < 0 || o == NULL)
1.1 gluk 209: return;
210:
1.3 mickey 211: if (o->option == OPTION_SEARCH && !(ri.caps & RADIO_CAPS_HW_SEARCH)) {
212: warn_unsupported(o->option);
1.1 gluk 213: return;
214: }
215:
1.3 mickey 216: oval = o->option == OPTION_SEARCH ? OPTION_FREQUENCY : o->option;
217: if (!silent)
218: printf("%s: ", varname[oval]);
219:
220: print_value(o->option);
221: printf(" -> ");
222:
223: if (o->option == OPTION_SEARCH) {
224:
225: if (ioctl(fd, RIOCSSRCH, &o->value) < 0) {
226: warn("RIOCSSRCH");
227: return;
228: }
229:
230: } else {
231:
232: change_value(*o);
233: if (ioctl(fd, RIOCSINFO, &ri) < 0) {
234: warn("RIOCSINFO");
235: return;
236: }
1.1 gluk 237:
238: }
239:
1.3 mickey 240: if (ioctl(fd, RIOCGINFO, &ri) < 0) {
241: warn("RIOCGINFO");
1.1 gluk 242: return;
243: }
244:
1.3 mickey 245: print_value(o->option);
246: putchar('\n');
247: }
248:
1.8 ! mickey 249: void
1.3 mickey 250: change_value(const struct opt_t o)
251: {
252: int unsupported = 0;
1.1 gluk 253:
1.3 mickey 254: if (o.value == VALUE_NONE)
255: return;
1.1 gluk 256:
1.3 mickey 257: switch (o.option) {
258: case OPTION_VOLUME:
259: update_value(o.sign, (u_long *)&ri.volume, o.value);
260: break;
261: case OPTION_FREQUENCY:
262: update_value(o.sign, (u_long *)&ri.freq, o.value);
263: break;
264: case OPTION_REFERENCE:
265: if (ri.caps & RADIO_CAPS_REFERENCE_FREQ)
266: update_value(o.sign, (u_long *)&ri.rfreq, o.value);
1.1 gluk 267: else
1.3 mickey 268: unsupported++;
1.1 gluk 269: break;
1.3 mickey 270: case OPTION_MONO:
271: /* FALLTHROUGH */
272: case OPTION_STEREO:
273: if (ri.caps & RADIO_CAPS_SET_MONO)
274: ri.stereo = o.option == OPTION_MONO ? !o.value : o.value;
1.1 gluk 275: else
1.3 mickey 276: unsupported++;
1.1 gluk 277: break;
1.3 mickey 278: case OPTION_SENSITIVITY:
279: if (ri.caps & RADIO_CAPS_LOCK_SENSITIVITY)
280: update_value(o.sign, (u_long *)&ri.lock, o.value);
281: else
282: unsupported++;
1.1 gluk 283: break;
1.3 mickey 284: case OPTION_MUTE:
285: ri.mute = o.value;
1.1 gluk 286: break;
287: }
288:
1.6 deraadt 289: if (unsupported)
1.3 mickey 290: warn_unsupported(o.option);
1.1 gluk 291: }
292:
293: /*
294: * Convert string to integer representation of a parameter
295: */
1.8 ! mickey 296: int
1.3 mickey 297: str_to_opt(const char *topt)
1.1 gluk 298: {
1.3 mickey 299: int res, toptlen, varlen, len, varsize;
1.1 gluk 300:
301: if (topt == NULL || *topt == '\0')
302: return OPTION_NONE;
303:
304: varsize = sizeof(varname) / sizeof(varname[0]);
305: toptlen = strlen(topt);
306:
307: for (res = 0; res < varsize; res++) {
308: varlen = strlen(varname[res]);
309: len = toptlen > varlen ? toptlen : varlen;
310: if (strncmp(topt, varname[res], len) == 0)
311: return res;
312: }
313:
314: warnx("bad name `%s'", topt);
315: return OPTION_NONE;
316: }
317:
1.8 ! mickey 318: void
1.3 mickey 319: update_value(int sign, u_long *value, u_long update)
1.1 gluk 320: {
1.3 mickey 321: switch (sign) {
322: case SIGN_NONE:
323: *value = update;
1.1 gluk 324: break;
1.3 mickey 325: case SIGN_PLUS:
326: *value += update;
1.1 gluk 327: break;
1.3 mickey 328: case SIGN_MINUS:
329: *value -= update;
1.1 gluk 330: break;
331: }
332: }
333:
334: /*
1.3 mickey 335: * Convert string to unsigned integer
1.1 gluk 336: */
1.8 ! mickey 337: u_long
1.3 mickey 338: str_to_long(char *str, int optval)
1.1 gluk 339: {
340: u_long val;
341:
342: if (str == NULL || *str == '\0')
343: return VALUE_NONE;
344:
345: if (optval == OPTION_FREQUENCY)
346: val = (u_long)1000 * atof(str);
347: else
348: val = (u_long)strtol(str, (char **)NULL, 10);
349:
350: return val;
351: }
352:
353: /*
1.3 mickey 354: * parse string s into struct opt_t
355: * return true on success, false on failure
356: */
1.8 ! mickey 357: int
1.3 mickey 358: parse_opt(char *s, struct opt_t *o) {
359: const char *badvalue = "bad value `%s'";
360: char *topt = NULL;
361: int slen, optlen;
362:
363: if (s == NULL || *s == '\0' || o == NULL)
364: return 0;
365:
366: o->string = NULL;
367: o->option = OPTION_NONE;
368: o->value = VALUE_NONE;
369: o->sign = SIGN_NONE;
370:
371: slen = strlen(s);
372: optlen = strcspn(s, "=");
373:
374: /* Set only o->optval, the rest is missing */
375: if (slen == optlen) {
376: o->option = str_to_opt(s);
377: return o->option == OPTION_NONE ? 0 : 1;
378: }
379:
380: if (optlen > slen - 2) {
381: warnx(badvalue, s);
382: return 0;
383: }
384:
385: slen -= ++optlen;
386:
387: if ((topt = (char *)malloc(optlen)) == NULL) {
388: warn("memory allocation error");
389: return 0;
390: }
391: strlcpy(topt, s, optlen);
392:
393: if ((o->option = str_to_opt(topt)) == OPTION_NONE) {
394: free(topt);
395: return 0;
396: }
397: o->string = topt;
398:
399: topt = &s[optlen];
400: switch (*topt) {
401: case '+':
402: case '-':
403: o->sign = (*topt == '+') ? SIGN_PLUS : SIGN_MINUS;
404: o->value = str_to_long(&topt[1], o->option);
405: break;
406: case 'o':
407: if (strncmp(topt, offchar,
408: slen > OFFCHAR_LEN ? slen : OFFCHAR_LEN) == 0)
409: o->value = 0;
410: else if (strncmp(topt, onchar,
411: slen > ONCHAR_LEN ? slen : ONCHAR_LEN) == 0)
412: o->value = 1;
413: break;
414: case 'u':
415: if (strncmp(topt, "up", slen > 2 ? slen : 2) == 0)
416: o->value = 1;
417: break;
418: case 'd':
419: if (strncmp(topt, "down", slen > 4 ? slen : 4) == 0)
420: o->value = 0;
421: break;
422: default:
423: if (*topt > 47 && *topt < 58)
424: o->value = str_to_long(topt, o->option);
425: break;
426: }
427:
428: if (o->value == VALUE_NONE) {
429: warnx(badvalue, topt);
430: return 0;
431: }
432:
433: return 1;
434: }
435:
436: /*
1.1 gluk 437: * Print current value of the parameter.
438: */
1.8 ! mickey 439: void
1.3 mickey 440: print_value(int optval)
1.1 gluk 441: {
442: if (optval == OPTION_NONE)
443: return;
444:
445: switch (optval) {
446: case OPTION_SEARCH:
447: /* FALLTHROUGH */
448: case OPTION_FREQUENCY:
1.3 mickey 449: printf("%.2fMHz", (float)ri.freq / 1000.);
1.1 gluk 450: break;
451: case OPTION_REFERENCE:
1.3 mickey 452: printf("%ukHz", ri.rfreq);
1.1 gluk 453: break;
454: case OPTION_SENSITIVITY:
1.3 mickey 455: printf("%umkV", ri.lock);
1.1 gluk 456: break;
457: case OPTION_MUTE:
1.3 mickey 458: printf(ri.mute ? onchar : offchar);
459: break;
1.1 gluk 460: case OPTION_MONO:
1.3 mickey 461: printf(ri.stereo ? offchar : onchar);
1.1 gluk 462: break;
463: case OPTION_STEREO:
1.3 mickey 464: printf(ri.stereo ? onchar : offchar);
1.1 gluk 465: break;
1.3 mickey 466: case OPTION_VOLUME:
1.1 gluk 467: default:
1.3 mickey 468: printf("%u", ri.volume);
1.1 gluk 469: break;
470: }
471: }
472:
1.8 ! mickey 473: void
1.3 mickey 474: show_int_val(u_long val, const char *nick, char *append, int silent)
475: {
476: show_verbose(nick, silent);
477: printf("%lu%s\n", val, append);
478: }
479:
1.8 ! mickey 480: void
1.3 mickey 481: show_float_val(float val, const char *nick, char *append, int silent)
482: {
483: show_verbose(nick, silent);
484: printf("%.2f%s\n", val, append);
485: }
486:
1.8 ! mickey 487: void
1.3 mickey 488: show_char_val(const char *val, const char *nick, int silent)
1.1 gluk 489: {
1.3 mickey 490: show_verbose(nick, silent);
491: printf("%s\n", val);
1.1 gluk 492: }
493:
1.3 mickey 494: /*
495: * Print all available parameters
496: */
1.8 ! mickey 497: void
1.3 mickey 498: print_vars(int silent)
1.1 gluk 499: {
1.3 mickey 500: show_int_val(ri.volume, varname[OPTION_VOLUME], "", silent);
501: show_float_val((float)ri.freq / 1000., varname[OPTION_FREQUENCY],
1.6 deraadt 502: "MHz", silent);
1.3 mickey 503: show_char_val(ri.mute ? onchar : offchar, varname[OPTION_MUTE], silent);
504:
505: if (ri.caps & RADIO_CAPS_REFERENCE_FREQ)
506: show_int_val(ri.rfreq, varname[OPTION_REFERENCE], "kHz", silent);
507: if (ri.caps & RADIO_CAPS_LOCK_SENSITIVITY)
508: show_int_val(ri.lock, varname[OPTION_SENSITIVITY], "mkV", silent);
509:
510: if (ri.caps & RADIO_CAPS_DETECT_SIGNAL) {
511: show_verbose("signal", silent);
512: printf("%s\n", ri.info & RADIO_INFO_SIGNAL ? onchar : offchar);
513: }
514: if (ri.caps & RADIO_CAPS_DETECT_STEREO) {
515: show_verbose(varname[OPTION_STEREO], silent);
516: printf("%s\n", ri.info & RADIO_INFO_STEREO ? onchar : offchar);
517: }
1.1 gluk 518:
519: if (!silent)
1.3 mickey 520: puts("card capabilities:");
521: if (ri.caps & RADIO_CAPS_SET_MONO)
522: puts("\tmanageable mono/stereo");
523: if (ri.caps & RADIO_CAPS_HW_SEARCH)
524: puts("\thardware search");
525: if (ri.caps & RADIO_CAPS_HW_AFC)
526: puts("\thardware AFC");
1.1 gluk 527: }