xref: /netbsd-src/usr.bin/radioctl/radioctl.c (revision 0b82381295fd2a46a6844db3be5c48caad493ce9)
1 /* $NetBSD: radioctl.c,v 1.13 2011/09/06 18:27:08 joerg Exp $ */
2 /* $OpenBSD: radioctl.c,v 1.5 2001/12/18 18:42:19 mickey Exp $ */
3 /* $RuOBSD: radioctl.c,v 1.4 2001/10/20 18:09:10 pva Exp $ */
4 
5 /*
6  * Copyright (c) 2001 Vladimir Popov <jumbo@narod.ru>
7  * All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
24  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
25  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
26  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
27  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28  */
29 #include <sys/cdefs.h>
30 
31 #ifndef lint
32 __RCSID("$NetBSD: radioctl.c,v 1.13 2011/09/06 18:27:08 joerg Exp $");
33 #endif
34 
35 #include <sys/ioctl.h>
36 #include <sys/radioio.h>
37 
38 #include <err.h>
39 #include <fcntl.h>
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <string.h>
43 #include <unistd.h>
44 
45 #define RADIO_ENV	"RADIODEVICE"
46 #define RADIODEVICE	"/dev/radio"
47 
48 static const char *varname[] = {
49 	"search",
50 #define OPTION_SEARCH		0x00
51 	"volume",
52 #define OPTION_VOLUME		0x01
53 	"frequency",
54 #define OPTION_FREQUENCY	0x02
55 	"mute",
56 #define OPTION_MUTE		0x03
57 	"reference",
58 #define OPTION_REFERENCE	0x04
59 	"mono",
60 #define OPTION_MONO		0x05
61 	"stereo",
62 #define	OPTION_STEREO		0x06
63 	"sensitivity"
64 #define	OPTION_SENSITIVITY	0x07
65 };
66 
67 #define OPTION_NONE		~0u
68 #define VALUE_NONE		~0u
69 
70 struct opt_t {
71 	char *string;
72 	int option;
73 	int sign;
74 #define SIGN_NONE	0
75 #define SIGN_PLUS	1
76 #define SIGN_MINUS	-1
77 	u_int32_t value;
78 };
79 
80 static const char *onchar = "on";
81 #define ONCHAR_LEN	2
82 static const char *offchar = "off";
83 #define OFFCHAR_LEN	3
84 
85 static struct radio_info ri;
86 
87 static int	parse_opt(char *, struct opt_t *);
88 
89 static void	print_vars(int);
90 static void	do_ioctls(int, struct opt_t *, int);
91 
92 static void	print_value(int);
93 static void	change_value(const struct opt_t);
94 static void	update_value(int, u_int *, u_int);
95 
96 static void     warn_unsupported(int);
97 __dead static void	usage(void);
98 
99 static void	show_verbose(const char *, int);
100 static void	show_int_val(u_long, const char *, const char *, int);
101 static void	show_float_val(float, const char *, const char *, int);
102 static void	show_char_val(const char *, const char *, int);
103 static int	str_to_opt(const char *);
104 static u_long	str_to_long(char *, int);
105 
106 /*
107  * Control behavior of a FM tuner - set frequency, volume etc
108  */
109 int
main(int argc,char ** argv)110 main(int argc, char **argv)
111 {
112 	struct opt_t opt;
113 	const char *radiodev = NULL;
114 	int rd = -1;
115 	int optchar;
116 	char *param = NULL;
117 	int show_vars = 0;
118 	int set_param = 0;
119 	int silent = 0;
120 
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;
134 			break;
135 		case 'f':
136 			radiodev = optarg;
137 			break;
138 		case 'n':
139 			silent = 1;
140 			break;
141 		case 'w':
142 			set_param = 1;
143 			param = optarg;
144 			break;
145 		default:
146 			usage();
147 			/* NOTREACHED */
148 		}
149 	}
150 	argc -= optind;
151 	argv += optind;
152 
153 	rd = open(radiodev, O_RDONLY);
154 	if (rd < 0)
155 		err(1, "%s open error", radiodev);
156 
157 	if (ioctl(rd, RIOCGINFO, &ri) < 0)
158 		err(1, "RIOCGINFO");
159 
160 	if (argc > 1)
161 		if (parse_opt(*(argv + 1), &opt)) {
162 			show_verbose(varname[opt.option], silent);
163 			print_value(opt.option);
164 			free(opt.string);
165 			putchar('\n');
166 		}
167 
168 	if (set_param)
169 		if (parse_opt(param, &opt))
170 			do_ioctls(rd, &opt, silent);
171 
172 	if (show_vars)
173 		print_vars(silent);
174 
175 	if (close(rd) < 0)
176 		warn("%s close error", radiodev);
177 
178 	return 0;
179 }
180 
181 static void
usage(void)182 usage(void)
183 {
184 	const char *progname = getprogname();
185 
186 	fprintf(stderr, "usage:\t%s %s\n\t%s %s\n\t%s %s\n\t%s %s\n",
187 		progname, "[-n] variable ...",
188 		progname, "[-n] -w name=value ...",
189 		progname, "[-n] -a",
190 		progname, "[-n] -f file");
191 	exit(1);
192 }
193 
194 static void
show_verbose(const char * nick,int silent)195 show_verbose(const char *nick, int silent)
196 {
197 	if (!silent)
198 		printf("%s=", nick);
199 }
200 
201 static void
warn_unsupported(int optval)202 warn_unsupported(int optval)
203 {
204 	warnx("driver does not support `%s'", varname[optval]);
205 }
206 
207 static void
do_ioctls(int fd,struct opt_t * o,int silent)208 do_ioctls(int fd, struct opt_t *o, int silent)
209 {
210 	int oval;
211 
212 	if (fd < 0 || o == NULL)
213 		return;
214 
215 	if (o->option == OPTION_SEARCH && !(ri.caps & RADIO_CAPS_HW_SEARCH)) {
216 		warn_unsupported(o->option);
217 		return;
218 	}
219 
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 		}
241 
242 	}
243 
244 	if (ioctl(fd, RIOCGINFO, &ri) < 0) {
245 		warn("RIOCGINFO");
246 		return;
247 	}
248 
249 	print_value(o->option);
250 	putchar('\n');
251 }
252 
253 static void
change_value(const struct opt_t o)254 change_value(const struct opt_t o)
255 {
256 	int unsupported = 0;
257 
258 	if (o.value == VALUE_NONE)
259 		return;
260 
261 	switch (o.option) {
262 	case OPTION_VOLUME:
263 		update_value(o.sign, (u_int *)&ri.volume, o.value);
264 		break;
265 	case OPTION_FREQUENCY:
266 		update_value(o.sign, (u_int *)&ri.freq, o.value);
267 		break;
268 	case OPTION_REFERENCE:
269 		if (ri.caps & RADIO_CAPS_REFERENCE_FREQ)
270 			update_value(o.sign, (u_int *)&ri.rfreq, o.value);
271 		else
272 			unsupported++;
273 		break;
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;
279 		else
280 			unsupported++;
281 		break;
282 	case OPTION_SENSITIVITY:
283 		if (ri.caps & RADIO_CAPS_LOCK_SENSITIVITY)
284 			update_value(o.sign, (u_int *)&ri.lock, o.value);
285 		else
286 			unsupported++;
287 		break;
288 	case OPTION_MUTE:
289 		ri.mute = o.value;
290 		break;
291 	}
292 
293 	if ( unsupported )
294 		warn_unsupported(o.option);
295 }
296 
297 /*
298  * Convert string to integer representation of a parameter
299  */
300 static int
str_to_opt(const char * topt)301 str_to_opt(const char *topt)
302 {
303 	int res, toptlen, varlen, len, varsize;
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("name not found `%s'", topt);
319 	return OPTION_NONE;
320 }
321 
322 static void
update_value(int sign,u_int * value,u_int update)323 update_value(int sign, u_int *value, u_int update)
324 {
325 	switch (sign) {
326 	case SIGN_NONE:
327 		*value  = update;
328 		break;
329 	case SIGN_PLUS:
330 		*value += update;
331 		break;
332 	case SIGN_MINUS:
333 		*value -= update;
334 		break;
335 	}
336 }
337 
338 /*
339  * Convert string to unsigned integer
340  */
341 static u_long
str_to_long(char * str,int optval)342 str_to_long(char *str, int optval)
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, NULL, 10);
353 
354 	return val;
355 }
356 
357 /*
358  * parse string s into struct opt_t
359  * return true on success, false on failure
360  */
361 static int
parse_opt(char * s,struct opt_t * o)362 parse_opt(char *s, struct opt_t *o) {
363 	static 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 == (int)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)) == (int)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 /*
441  * Print current value of the parameter.
442  */
443 static void
print_value(int optval)444 print_value(int optval)
445 {
446 	if (optval == (int)OPTION_NONE)
447 		return;
448 
449 	switch (optval) {
450 	case OPTION_SEARCH:
451 		/* FALLTHROUGH */
452 	case OPTION_FREQUENCY:
453 		printf("%.2fMHz", (float)ri.freq / 1000.);
454 		break;
455 	case OPTION_REFERENCE:
456 		printf("%ukHz", ri.rfreq);
457 		break;
458 	case OPTION_SENSITIVITY:
459 		printf("%umkV", ri.lock);
460 		break;
461 	case OPTION_MUTE:
462 		printf("%s", ri.mute ? onchar : offchar);
463 		break;
464 	case OPTION_MONO:
465 		printf("%s", ri.stereo ? offchar : onchar);
466 		break;
467 	case OPTION_STEREO:
468 		printf("%s", ri.stereo ? onchar : offchar);
469 		break;
470 	case OPTION_VOLUME:
471 	default:
472 		printf("%u", ri.volume);
473 		break;
474 	}
475 }
476 
477 static void
show_int_val(u_long val,const char * nick,const char * append,int silent)478 show_int_val(u_long val, const char *nick, const char *append, int silent)
479 {
480 	show_verbose(nick, silent);
481 	printf("%lu%s\n", val, append);
482 }
483 
484 static void
show_float_val(float val,const char * nick,const char * append,int silent)485 show_float_val(float val, const char *nick, const char *append, int silent)
486 {
487 	show_verbose(nick, silent);
488 	printf("%.2f%s\n", val, append);
489 }
490 
491 static void
show_char_val(const char * val,const char * nick,int silent)492 show_char_val(const char *val, const char *nick, int silent)
493 {
494 	show_verbose(nick, silent);
495 	printf("%s\n", val);
496 }
497 
498 /*
499  * Print all available parameters
500  */
501 static void
print_vars(int silent)502 print_vars(int silent)
503 {
504 	const char *delim;
505 
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 	}
524 
525 	if (!silent)
526 		printf("card capabilities:");
527 	delim = "";
528 	if (ri.caps & RADIO_CAPS_DETECT_STEREO)
529 		printf("%s stereo detect", delim), delim=",";
530 	if (ri.caps & RADIO_CAPS_DETECT_SIGNAL)
531 		printf("%s signal detect", delim), delim=",";
532 	if (ri.caps & RADIO_CAPS_SET_MONO)
533 		printf("%s manageable mono/stereo", delim), delim=",";
534 	if (ri.caps & RADIO_CAPS_HW_SEARCH)
535 		printf("%s hardware search", delim), delim=",";
536 	if (ri.caps & RADIO_CAPS_HW_AFC)
537 		printf("%s hardware AFC", delim), delim=",";
538 	printf("\n");
539 }
540