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