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