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