GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/radioctl/radioctl.c Lines: 0 231 0.0 %
Date: 2017-11-07 Branches: 0 180 0.0 %

Line Branch Exec Source
1
/* $OpenBSD: radioctl.c,v 1.19 2013/12/21 06:54:53 guenther Exp $ */
2
/* $RuOBSD: radioctl.c,v 1.4 2001/10/20 18:09:10 pva Exp $ */
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 <dev/ic/bt8xx.h>
33
34
#include <err.h>
35
#include <fcntl.h>
36
#include <stdio.h>
37
#include <stdlib.h>
38
#include <string.h>
39
#include <unistd.h>
40
#include <ctype.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
	"channel",
63
#define OPTION_CHANNEL		0x08
64
	"chnlset"
65
#define OPTION_CHNLSET		0x09
66
};
67
68
#define OPTION_NONE		~0u
69
#define VALUE_NONE		~0u
70
71
struct opt_t {
72
	char *string;
73
	int option;
74
	int sign;
75
#define SIGN_NONE	0
76
#define SIGN_PLUS	1
77
#define SIGN_MINUS	-1
78
	u_int32_t value;
79
};
80
81
struct chansets {
82
	int value;
83
	char *name;
84
} chansets[] = {
85
{ CHNLSET_NABCST,	"nabcst",	},
86
{ CHNLSET_CABLEIRC,	"cableirc",	},
87
{ CHNLSET_CABLEHRC,	"cablehrc",	},
88
{ CHNLSET_WEUROPE,	"weurope",	},
89
{ CHNLSET_JPNBCST,	"jpnbcst",	},
90
{ CHNLSET_JPNCABLE,	"jpncable",	},
91
{ CHNLSET_XUSSR,	"xussr",	},
92
{ CHNLSET_AUSTRALIA,	"australia",	},
93
{ CHNLSET_FRANCE,	"france",	},
94
{ 0, NULL }
95
};
96
97
extern char *__progname;
98
const char *onchar = "on";
99
#define ONCHAR_LEN	2
100
const char *offchar = "off";
101
#define OFFCHAR_LEN	3
102
103
struct radio_info ri;
104
unsigned int i = 0;
105
106
int	parse_opt(char *, struct opt_t *);
107
108
void	print_vars(int, int);
109
void	do_ioctls(int, struct opt_t *, int);
110
111
void	print_value(int, int);
112
void	change_value(const struct opt_t);
113
void	update_value(int, int *, int);
114
115
void	warn_unsupported(int);
116
void	usage(void);
117
118
void	show_verbose(const char *, int);
119
void	show_int_val(int, const char *, char *, int);
120
void	show_float_val(float, const char *, char *, int);
121
void	show_char_val(const char *, const char *, int);
122
int	str_to_opt(const char *);
123
u_int	str_to_int(char *, int);
124
125
/*
126
 * Control behavior of a FM tuner - set frequency, volume etc
127
 */
128
int
129
main(int argc, char **argv)
130
{
131
	struct opt_t opt;
132
	char **avp;
133
134
	char *radiodev = NULL;
135
	int rd = -1;
136
	int optchar;
137
	int show_vars = 0;
138
	int show_choices = 0;
139
	int silent = 0;
140
	int mode = O_RDONLY;
141
142
	radiodev = getenv(RADIO_ENV);
143
	if (radiodev == NULL)
144
		radiodev = RADIODEVICE;
145
146
	while ((optchar = getopt(argc, argv, "af:nvw")) != -1) {
147
		switch (optchar) {
148
		case 'a':
149
			show_vars = 1;
150
			break;
151
		case 'f':
152
			radiodev = optarg;
153
			break;
154
		case 'n':
155
			silent = 1;
156
			break;
157
		case 'v':
158
			show_choices = 1;
159
			break;
160
		case 'w':
161
			/* backwards compatibility */
162
			break;
163
		default:
164
			usage();
165
			/* NOTREACHED */
166
		}
167
	}
168
169
	argc -= optind;
170
	argv += optind;
171
172
	if (argc == 0)
173
		show_vars = 1;
174
175
	/*
176
	 * Scan the options for `name=value` so the
177
	 * device can be opened in the proper mode.
178
	 */
179
	for (avp = argv; *avp != NULL; avp++)
180
		if (strchr(*avp, '=') != NULL) {
181
			mode = O_RDWR;
182
			break;
183
		}
184
185
	rd = open(radiodev, mode);
186
	if (rd < 0)
187
		err(1, "%s open error", radiodev);
188
189
	if (ioctl(rd, RIOCGINFO, &ri) < 0)
190
		err(1, "RIOCGINFO");
191
192
	if (!argc && show_vars)
193
		print_vars(silent, show_choices);
194
	else if (argc > 0 && !show_vars) {
195
		if (mode == O_RDWR) {
196
			for (; argc--; argv++)
197
				if (parse_opt(*argv, &opt))
198
					do_ioctls(rd, &opt, silent);
199
		} else {
200
			for (; argc--; argv++)
201
				if (parse_opt(*argv, &opt)) {
202
					show_verbose(varname[opt.option],
203
					    silent);
204
					print_value(opt.option, show_choices);
205
					free(opt.string);
206
					putchar('\n');
207
				}
208
		}
209
	}
210
211
	if (close(rd) < 0)
212
		warn("%s close error", radiodev);
213
214
	return 0;
215
}
216
217
void
218
usage(void)
219
{
220
	fprintf(stderr,
221
	    "usage: %s [-anv] [-f file]\n"
222
	    "       %s [-nv] [-f file] name\n"
223
	    "       %s [-n] [-f file] name=value\n",
224
	    __progname, __progname, __progname);
225
	exit(1);
226
}
227
228
void
229
show_verbose(const char *nick, int silent)
230
{
231
	if (!silent)
232
		printf("%s=", nick);
233
}
234
235
void
236
warn_unsupported(int optval)
237
{
238
	warnx("driver does not support `%s'", varname[optval]);
239
}
240
241
void
242
do_ioctls(int fd, struct opt_t *o, int silent)
243
{
244
	int oval;
245
246
	if (fd < 0 || o == NULL)
247
		return;
248
249
	if (o->option == OPTION_SEARCH && !(ri.caps & RADIO_CAPS_HW_SEARCH)) {
250
		warn_unsupported(o->option);
251
		return;
252
	}
253
254
	oval = o->option == OPTION_SEARCH ? OPTION_FREQUENCY : o->option;
255
	if (!silent)
256
		printf("%s: ", varname[oval]);
257
258
	print_value(o->option, 0);
259
	printf(" -> ");
260
261
	if (o->option == OPTION_SEARCH) {
262
263
		if (ioctl(fd, RIOCSSRCH, &o->value) < 0) {
264
			warn("RIOCSSRCH");
265
			return;
266
		}
267
268
	} else {
269
270
		change_value(*o);
271
		if (ioctl(fd, RIOCSINFO, &ri) < 0) {
272
			warn("RIOCSINFO");
273
			return;
274
		}
275
276
	}
277
278
	if (ioctl(fd, RIOCGINFO, &ri) < 0) {
279
		warn("RIOCGINFO");
280
		return;
281
	}
282
283
	print_value(o->option, 0);
284
	putchar('\n');
285
}
286
287
void
288
change_value(const struct opt_t o)
289
{
290
	int unsupported = 0;
291
292
	if (o.value == VALUE_NONE)
293
		return;
294
295
	switch (o.option) {
296
	case OPTION_VOLUME:
297
		update_value(o.sign, &ri.volume, o.value);
298
		break;
299
	case OPTION_FREQUENCY:
300
		ri.tuner_mode = RADIO_TUNER_MODE_RADIO;
301
		update_value(o.sign, &ri.freq, o.value);
302
		break;
303
	case OPTION_REFERENCE:
304
		if (ri.caps & RADIO_CAPS_REFERENCE_FREQ)
305
			update_value(o.sign, &ri.rfreq, o.value);
306
		else
307
			unsupported++;
308
		break;
309
	case OPTION_MONO:
310
		/* FALLTHROUGH */
311
	case OPTION_STEREO:
312
		if (ri.caps & RADIO_CAPS_SET_MONO)
313
			ri.stereo = o.option == OPTION_MONO ? !o.value : o.value;
314
		else
315
			unsupported++;
316
		break;
317
	case OPTION_SENSITIVITY:
318
		if (ri.caps & RADIO_CAPS_LOCK_SENSITIVITY)
319
			update_value(o.sign, &ri.lock, o.value);
320
		else
321
			unsupported++;
322
		break;
323
	case OPTION_MUTE:
324
		ri.mute = o.value;
325
		break;
326
	case OPTION_CHANNEL:
327
		ri.tuner_mode = RADIO_TUNER_MODE_TV;
328
		update_value(o.sign, &ri.chan, o.value);
329
		break;
330
	case OPTION_CHNLSET:
331
		ri.chnlset = o.value;
332
		break;
333
	}
334
335
	if (unsupported)
336
		warn_unsupported(o.option);
337
}
338
339
/*
340
 * Convert string to integer representation of a parameter
341
 */
342
int
343
str_to_opt(const char *topt)
344
{
345
	int res, toptlen, varlen, len, varsize;
346
347
	if (topt == NULL || *topt == '\0')
348
		return OPTION_NONE;
349
350
	varsize = sizeof(varname) / sizeof(varname[0]);
351
	toptlen = strlen(topt);
352
353
	for (res = 0; res < varsize; res++) {
354
		varlen = strlen(varname[res]);
355
		len = toptlen > varlen ? toptlen : varlen;
356
		if (strncmp(topt, varname[res], len) == 0)
357
			return res;
358
	}
359
360
	warnx("bad name `%s'", topt);
361
	return OPTION_NONE;
362
}
363
364
void
365
update_value(int sign, int *value, int update)
366
{
367
	switch (sign) {
368
	case SIGN_NONE:
369
		*value  = update;
370
		break;
371
	case SIGN_PLUS:
372
		*value += update;
373
		break;
374
	case SIGN_MINUS:
375
		*value -= update;
376
		break;
377
	}
378
}
379
380
/*
381
 * Convert string to unsigned integer
382
 */
383
u_int
384
str_to_int(char *str, int optval)
385
{
386
	int val;
387
388
	if (str == NULL || *str == '\0')
389
		return VALUE_NONE;
390
391
	if (optval == OPTION_FREQUENCY)
392
		val = (int)(1000 * atof(str));
393
	else
394
		val = (int)strtol(str, (char **)NULL, 10);
395
396
	return val;
397
}
398
399
/*
400
 * parse string s into struct opt_t
401
 * return true on success, false on failure
402
 */
403
int
404
parse_opt(char *s, struct opt_t *o) {
405
	static const char badvalue[] = "bad value `%s'";
406
	char *topt = NULL;
407
	int slen, optlen;
408
409
	if (s == NULL || *s == '\0' || o == NULL)
410
		return 0;
411
412
	o->string = NULL;
413
	o->option = OPTION_NONE;
414
	o->value = VALUE_NONE;
415
	o->sign = SIGN_NONE;
416
417
	slen = strlen(s);
418
	optlen = strcspn(s, "=");
419
420
	/* Set only o->optval, the rest is missing */
421
	if (slen == optlen) {
422
		o->option = str_to_opt(s);
423
		return o->option == OPTION_NONE ? 0 : 1;
424
	}
425
426
	if (optlen > slen - 2) {
427
		warnx(badvalue, s);
428
		return 0;
429
	}
430
431
	slen -= ++optlen;
432
433
	if ((topt = malloc(optlen)) == NULL) {
434
		warn("memory allocation error");
435
		return 0;
436
	}
437
	strlcpy(topt, s, optlen);
438
439
	if ((o->option = str_to_opt(topt)) == OPTION_NONE) {
440
		free(topt);
441
		return 0;
442
	}
443
	o->string = topt;
444
445
	topt = &s[optlen];
446
447
	if (strcmp(o->string, "chnlset") == 0) {
448
		for (i = 0; chansets[i].name; i++)
449
			if (strncmp(chansets[i].name, topt,
450
				strlen(chansets[i].name)) == 0)
451
					break;
452
		if (chansets[i].name != NULL) {
453
			o->value = chansets[i].value;
454
			return 1;
455
		} else {
456
			warnx(badvalue, topt);
457
			return 0;
458
		}
459
	}
460
461
	switch (*topt) {
462
	case '+':
463
	case '-':
464
		o->sign = (*topt == '+') ? SIGN_PLUS : SIGN_MINUS;
465
		o->value = str_to_int(&topt[1], o->option);
466
		break;
467
	case 'o':
468
		if (strncmp(topt, offchar,
469
		    slen > OFFCHAR_LEN ? slen : OFFCHAR_LEN) == 0)
470
			o->value = 0;
471
		else if (strncmp(topt, onchar,
472
		    slen > ONCHAR_LEN ? slen : ONCHAR_LEN) == 0)
473
			o->value = 1;
474
		break;
475
	case 'u':
476
		if (strncmp(topt, "up", slen > 2 ? slen : 2) == 0)
477
			o->value = 1;
478
		break;
479
	case 'd':
480
		if (strncmp(topt, "down", slen > 4 ? slen : 4) == 0)
481
			o->value = 0;
482
		break;
483
	default:
484
		if (isdigit((unsigned char)*topt))
485
			o->value = str_to_int(topt, o->option);
486
		break;
487
	}
488
489
	if (o->value == VALUE_NONE) {
490
		warnx(badvalue, topt);
491
		return 0;
492
	}
493
494
	return 1;
495
}
496
497
/*
498
 * Print current value of the parameter.
499
 */
500
void
501
print_value(int optval, int show_choices)
502
{
503
	if (optval == OPTION_NONE)
504
		return;
505
506
	switch (optval) {
507
	case OPTION_SEARCH:
508
		/* FALLTHROUGH */
509
	case OPTION_FREQUENCY:
510
		printf("%.2fMHz", (float)ri.freq / 1000.);
511
		break;
512
	case OPTION_REFERENCE:
513
		printf("%ukHz", ri.rfreq);
514
		break;
515
	case OPTION_SENSITIVITY:
516
		printf("%umkV", ri.lock);
517
		break;
518
	case OPTION_MUTE:
519
		printf("%s", ri.mute ? onchar : offchar);
520
		break;
521
	case OPTION_MONO:
522
		printf("%s", ri.stereo ? offchar : onchar);
523
		break;
524
	case OPTION_STEREO:
525
		printf("%s", ri.stereo ? onchar : offchar);
526
		break;
527
	case OPTION_CHANNEL:
528
		printf("%u", ri.chan);
529
		break;
530
	case OPTION_CHNLSET:
531
		for (i = 0; chansets[i].name; i++) {
532
			if (chansets[i].value == ri.chnlset)
533
				printf("%s", chansets[i].name);
534
		}
535
		if (show_choices) {
536
			printf("\n\t[");
537
			for (i = 0; chansets[i].name; i++)
538
				printf("%s ", chansets[i].name);
539
			printf("]");
540
		}
541
		break;
542
	case OPTION_VOLUME:
543
	default:
544
		printf("%u", ri.volume);
545
		break;
546
	}
547
}
548
549
void
550
show_int_val(int val, const char *nick, char *append, int silent)
551
{
552
	show_verbose(nick, silent);
553
	printf("%u%s\n", val, append);
554
}
555
556
void
557
show_float_val(float val, const char *nick, char *append, int silent)
558
{
559
	show_verbose(nick, silent);
560
	printf("%.2f%s\n", val, append);
561
}
562
563
void
564
show_char_val(const char *val, const char *nick, int silent)
565
{
566
	show_verbose(nick, silent);
567
	printf("%s\n", val);
568
}
569
570
/*
571
 * Print all available parameters
572
 */
573
void
574
print_vars(int silent, int show_choices)
575
{
576
	show_int_val(ri.volume, varname[OPTION_VOLUME], "", silent);
577
	show_int_val(ri.chan, varname[OPTION_CHANNEL], "", silent);
578
	for (i = 0; chansets[i].name; i++) {
579
		if (chansets[i].value == ri.chnlset)
580
			show_char_val(chansets[i].name, varname[OPTION_CHNLSET], silent);
581
	}
582
	if (show_choices) {
583
		printf("\t[ ");
584
		for (i = 0; chansets[i].name; i++)
585
			printf("%s ", chansets[i].name);
586
		printf("]\n");
587
	}
588
	show_float_val((float)ri.freq / 1000., varname[OPTION_FREQUENCY],
589
	    "MHz", silent);
590
	show_char_val(ri.mute ? onchar : offchar, varname[OPTION_MUTE], silent);
591
592
	if (ri.caps & RADIO_CAPS_REFERENCE_FREQ)
593
		show_int_val(ri.rfreq, varname[OPTION_REFERENCE], "kHz", silent);
594
	if (ri.caps & RADIO_CAPS_LOCK_SENSITIVITY)
595
		show_int_val(ri.lock, varname[OPTION_SENSITIVITY], "mkV", silent);
596
597
	if (ri.caps & RADIO_CAPS_DETECT_SIGNAL) {
598
		show_verbose("signal", silent);
599
		printf("%s\n", ri.info & RADIO_INFO_SIGNAL ? onchar : offchar);
600
	}
601
	if (ri.caps & RADIO_CAPS_DETECT_STEREO) {
602
		show_verbose(varname[OPTION_STEREO], silent);
603
		printf("%s\n", ri.info & RADIO_INFO_STEREO ? onchar : offchar);
604
	}
605
606
	if (!silent) {
607
		printf("mode: %s\n",
608
		    ri.tuner_mode == RADIO_TUNER_MODE_TV ? "TV" : "radio");
609
610
		puts("card capabilities:");
611
	}
612
613
	if (ri.caps & RADIO_CAPS_SET_MONO)
614
		puts("\tmanageable mono/stereo");
615
	if (ri.caps & RADIO_CAPS_HW_SEARCH)
616
		puts("\thardware search");
617
	if (ri.caps & RADIO_CAPS_HW_AFC)
618
		puts("\thardware AFC");
619
}