Home | History | Annotate | Line # | Download | only in radioctl
      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 (at) 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
    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
    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
    195 show_verbose(const char *nick, int silent)
    196 {
    197 	if (!silent)
    198 		printf("%s=", nick);
    199 }
    200 
    201 static void
    202 warn_unsupported(int optval)
    203 {
    204 	warnx("driver does not support `%s'", varname[optval]);
    205 }
    206 
    207 static void
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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