Add a program for controlling radio(4) devices. Written by Maxim Tsyplakov

and Vladimir Popov for OpenBSD.
This commit is contained in:
augustss 2002-01-01 21:56:28 +00:00
parent 32d039b3bf
commit 9c9a9cad20
4 changed files with 702 additions and 3 deletions

View File

@ -1,4 +1,4 @@
# $NetBSD: Makefile,v 1.120 2001/12/03 22:28:19 martin Exp $
# $NetBSD: Makefile,v 1.121 2002/01/01 21:56:28 augustss Exp $
# from: @(#)Makefile 8.3 (Berkeley) 1/7/94
.include <bsd.own.mk>
@ -15,8 +15,8 @@ SUBDIR= apply apropos asa at audio banner basename biff \
make man menuc mesg midiplay mixerctl mkdep mkfifo \
mklocale mkstr mktemp \
modstat msgc msgs netgroup netstat newsyslog nfsstat nice nl nohup \
pagesize passwd paste patch pmc pr printenv printf quota rdist \
renice rev rlogin rpcgen rpcinfo rs rsh rup \
pagesize passwd paste patch pmc pr printenv printf quota radioctl \
rdist renice rev rlogin rpcgen rpcinfo rs rsh rup \
ruptime rusers rwall rwho script sed shar shlock \
showmount shuffle skey skeyinfo skeyinit soelim sort split \
su systat tail talk tcopy tee telnet tftp time \

View File

@ -0,0 +1,6 @@
# $NetBSD: Makefile,v 1.1 2002/01/01 21:56:28 augustss Exp $
# $OpenBSD: Makefile,v 1.2 2001/10/04 22:43:45 gluk Exp $
PROG= radioctl
.include <bsd.prog.mk>

162
usr.bin/radioctl/radioctl.1 Normal file
View File

@ -0,0 +1,162 @@
.\" $NetBSD: radioctl.1,v 1.1 2002/01/01 21:56:29 augustss Exp $
.\" $OpenBSD: radioctl.1,v 1.4 2001/12/06 10:14:32 mickey Exp $
.\"
.\" Copyright (c) 2001 Vladimir Popov
.\" All rights reserved.
.\"
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions
.\" are met:
.\" 1. Redistributions of source code must retain the above copyright
.\" notice, this list of conditions and the following disclaimer.
.\" 2. Redistributions in binary form must reproduce the above copyright
.\" notice, this list of conditions and the following disclaimer in the
.\" documentation and/or other materials provided with the distribution.
.\"
.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
.\" BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
.\" USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
.\" ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
.\"
.Dd September 16, 2001
.Dt RADIOCTL 1
.Os
.Sh NAME
.Nm radioctl
.Nd control radio tuners
.Sh SYNOPSIS
.Nm radioctl
.Op Fl f Ar file
.Op Fl n
.Fl a
.Nm radioctl
.Op Fl f Ar file
.Op Fl n
.Ar name
.Nm radioctl
.Op Fl f Ar file
.Op Fl n
.Fl w
.Ar name=value
.Sh DESCRIPTION
The
.Nm
command displays or sets various variables that affect the radio tuner
behavior. If a variable is present on the command line,
.Nm
prints the current value of this variable for the specified device.
By default,
.Nm
operates on the
.Pa /dev/radio
device.
.Pp
The options are as follows:
.Bl -tag -width Ds
.It Fl a
Print all device variables and their current values.
.It Fl w Ar name=value
Attempt to set the specified variable
.Ar name
to
.Ar value .
.It Fl f Ar file
Specify an alternative radio tuner device.
.It Fl n
Suppress printing of the variable name.
.El
.Pp
Values may be specified in either absolute or relative forms.
The relative form is indicated by a prefix of
.Ql +
or
.Ql -
to denote an increase or decrease, respectively.
.Pp
The exact set of controls that can be manipulated depends on
the tuner.
The general format (in both getting and setting a value) is
.Pp
.Va name = value
.Pp
The
.Va name
indicates what part of the tuner the control affects.
.Pp
Write only controls:
.Bl -tag -width search
.It search
Only for cards that allow hardware search. Can be
.Ql up
or
.Ql down .
.El
.Pp
Read-write controls:
.Bl -tag -width volume
.It frequency
Float value from 87.5 to 108.0.
.It volume
Integer value from 0 to 255.
.It mute
Mutes the card (volume is not affected),
.Ql on
or
.Ql off .
.It mono
Forces card output to mono,
.Ql on
or
.Ql off .
Only for cards that allow forced mono.
.It reference
Reference frequency. Can be 25 kHz, 50 kHz and 100 kHz. Not all cards allow
to change the reference frequency.
.It sensitivity
Station locking sensitivity. Can be 5 mkV, 10 mkV, 30 mkV and 150 mkV. Not all
cards allow to change the station locking sensitivity.
.El
.Pp
All the remaining controls (signal, stereo and card capabilities) are read-only
and can be viewed using option
.Fl a .
.Sh EXAMPLES
The command
.Pp
.Dl "radioctl -a"
.Pp
can produce
.Bd -literal
volume=255
frequency=106.30MHz
mute=off
reference=50kHz
signal=on
stereo=on
card capabilities:
manageable mono/stereo
.Ed
.Sh ENVIRONMENT
The following environment variable affects the execution of
.Nm radioctl :
.Bl -tag -width RADIODEVICE
.It Ev RADIODEVICE
The radio tuner device to use.
.El
.Sh FILES
.Bl -tag -width /dev/radio
.It Pa /dev/radio
radio tuner device
.El
.Sh SEE ALSO
.Xr radio 4
.Sh HISTORY
.Nm
command first appeared in
.Ox 3.0 .

531
usr.bin/radioctl/radioctl.c Normal file
View File

@ -0,0 +1,531 @@
/* $NetBSD: radioctl.c,v 1.1 2002/01/01 21:56:29 augustss Exp $ */
/* $OpenBSD: radioctl.c,v 1.5 2001/12/18 18:42:19 mickey Exp $ */
/* $RuOBSD: radioctl.c,v 1.4 2001/10/20 18:09:10 pva Exp $ */
/*
* Copyright (c) 2001 Vladimir Popov <jumbo@narod.ru>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <sys/ioctl.h>
#include <sys/radioio.h>
#include <err.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define RADIO_ENV "RADIODEVICE"
#define RADIODEVICE "/dev/radio"
const char *varname[] = {
"search",
#define OPTION_SEARCH 0x00
"volume",
#define OPTION_VOLUME 0x01
"frequency",
#define OPTION_FREQUENCY 0x02
"mute",
#define OPTION_MUTE 0x03
"reference",
#define OPTION_REFERENCE 0x04
"mono",
#define OPTION_MONO 0x05
"stereo",
#define OPTION_STEREO 0x06
"sensitivity"
#define OPTION_SENSITIVITY 0x07
};
#define OPTION_NONE ~0u
#define VALUE_NONE ~0u
struct opt_t {
char *string;
int option;
int sign;
#define SIGN_NONE 0
#define SIGN_PLUS 1
#define SIGN_MINUS -1
u_int32_t value;
};
extern char *__progname;
const char *onchar = "on";
#define ONCHAR_LEN 2
const char *offchar = "off";
#define OFFCHAR_LEN 3
static struct radio_info ri;
static int parse_opt(char *, struct opt_t *);
static void print_vars(int);
static void do_ioctls(int, struct opt_t *, int);
static void print_value(int);
static void change_value(const struct opt_t);
static void update_value(int, u_long *, u_long);
static void warn_unsupported(int);
static void usage(void);
static void show_verbose(const char *, int);
static void show_int_val(u_long, const char *, char *, int);
static void show_float_val(float, const char *, char *, int);
static void show_char_val(const char *, const char *, int);
static int str_to_opt(const char *);
static u_long str_to_long(char *, int);
/*
* Control behavior of a FM tuner - set frequency, volume etc
*/
int
main(int argc, char **argv)
{
struct opt_t opt;
char *radiodev = NULL;
int rd = -1;
char optchar;
char *param = NULL;
int show_vars = 0;
int set_param = 0;
int silent = 0;
int optv = 0;
if (argc < 2) {
usage();
exit(1);
}
radiodev = getenv(RADIO_ENV);
if (radiodev == NULL)
radiodev = RADIODEVICE;
while ((optchar = getopt(argc, argv, "af:nw:")) != -1) {
switch (optchar) {
case 'a':
show_vars = 1;
optv = 1;
break;
case 'f':
radiodev = optarg;
optv = 2;
break;
case 'n':
silent = 1;
optv = 1;
break;
case 'w':
set_param = 1;
param = optarg;
optv = 2;
break;
default:
usage();
/* NOTREACHED */
}
argc -= optv;
argv += optv;
}
rd = open(radiodev, O_RDONLY);
if (rd < 0)
err(1, "%s open error", radiodev);
if (ioctl(rd, RIOCGINFO, &ri) < 0)
err(1, "RIOCGINFO");
if (argc > 1)
if (parse_opt(*(argv + 1), &opt)) {
show_verbose(varname[opt.option], silent);
print_value(opt.option);
free(opt.string);
putchar('\n');
}
if (set_param)
if (parse_opt(param, &opt))
do_ioctls(rd, &opt, silent);
if (show_vars)
print_vars(silent);
if (close(rd) < 0)
warn("%s close error", radiodev);
return 0;
}
static void
usage(void)
{
printf("Usage: %s [-f file] [-a] [-n] [-w name=value] [name]\n",
__progname);
}
static void
show_verbose(const char *nick, int silent)
{
if (!silent)
printf("%s=", nick);
}
static void
warn_unsupported(int optval)
{
warnx("driver does not support `%s'", varname[optval]);
}
static void
do_ioctls(int fd, struct opt_t *o, int silent)
{
int oval;
if (fd < 0 || o == NULL)
return;
if (o->option == OPTION_SEARCH && !(ri.caps & RADIO_CAPS_HW_SEARCH)) {
warn_unsupported(o->option);
return;
}
oval = o->option == OPTION_SEARCH ? OPTION_FREQUENCY : o->option;
if (!silent)
printf("%s: ", varname[oval]);
print_value(o->option);
printf(" -> ");
if (o->option == OPTION_SEARCH) {
if (ioctl(fd, RIOCSSRCH, &o->value) < 0) {
warn("RIOCSSRCH");
return;
}
} else {
change_value(*o);
if (ioctl(fd, RIOCSINFO, &ri) < 0) {
warn("RIOCSINFO");
return;
}
}
if (ioctl(fd, RIOCGINFO, &ri) < 0) {
warn("RIOCGINFO");
return;
}
print_value(o->option);
putchar('\n');
}
static void
change_value(const struct opt_t o)
{
int unsupported = 0;
if (o.value == VALUE_NONE)
return;
switch (o.option) {
case OPTION_VOLUME:
update_value(o.sign, (u_long *)&ri.volume, o.value);
break;
case OPTION_FREQUENCY:
update_value(o.sign, (u_long *)&ri.freq, o.value);
break;
case OPTION_REFERENCE:
if (ri.caps & RADIO_CAPS_REFERENCE_FREQ)
update_value(o.sign, (u_long *)&ri.rfreq, o.value);
else
unsupported++;
break;
case OPTION_MONO:
/* FALLTHROUGH */
case OPTION_STEREO:
if (ri.caps & RADIO_CAPS_SET_MONO)
ri.stereo = o.option == OPTION_MONO ? !o.value : o.value;
else
unsupported++;
break;
case OPTION_SENSITIVITY:
if (ri.caps & RADIO_CAPS_LOCK_SENSITIVITY)
update_value(o.sign, (u_long *)&ri.lock, o.value);
else
unsupported++;
break;
case OPTION_MUTE:
ri.mute = o.value;
break;
}
if ( unsupported )
warn_unsupported(o.option);
}
/*
* Convert string to integer representation of a parameter
*/
static int
str_to_opt(const char *topt)
{
int res, toptlen, varlen, len, varsize;
if (topt == NULL || *topt == '\0')
return OPTION_NONE;
varsize = sizeof(varname) / sizeof(varname[0]);
toptlen = strlen(topt);
for (res = 0; res < varsize; res++) {
varlen = strlen(varname[res]);
len = toptlen > varlen ? toptlen : varlen;
if (strncmp(topt, varname[res], len) == 0)
return res;
}
warnx("bad name `%s'", topt);
return OPTION_NONE;
}
static void
update_value(int sign, u_long *value, u_long update)
{
switch (sign) {
case SIGN_NONE:
*value = update;
break;
case SIGN_PLUS:
*value += update;
break;
case SIGN_MINUS:
*value -= update;
break;
}
}
/*
* Convert string to unsigned integer
*/
static u_long
str_to_long(char *str, int optval)
{
u_long val;
if (str == NULL || *str == '\0')
return VALUE_NONE;
if (optval == OPTION_FREQUENCY)
val = (u_long)1000 * atof(str);
else
val = (u_long)strtol(str, (char **)NULL, 10);
return val;
}
/*
* parse string s into struct opt_t
* return true on success, false on failure
*/
static int
parse_opt(char *s, struct opt_t *o) {
const char *badvalue = "bad value `%s'";
char *topt = NULL;
int slen, optlen;
if (s == NULL || *s == '\0' || o == NULL)
return 0;
o->string = NULL;
o->option = OPTION_NONE;
o->value = VALUE_NONE;
o->sign = SIGN_NONE;
slen = strlen(s);
optlen = strcspn(s, "=");
/* Set only o->optval, the rest is missing */
if (slen == optlen) {
o->option = str_to_opt(s);
return o->option == OPTION_NONE ? 0 : 1;
}
if (optlen > slen - 2) {
warnx(badvalue, s);
return 0;
}
slen -= ++optlen;
if ((topt = (char *)malloc(optlen)) == NULL) {
warn("memory allocation error");
return 0;
}
strlcpy(topt, s, optlen);
if ((o->option = str_to_opt(topt)) == OPTION_NONE) {
free(topt);
return 0;
}
o->string = topt;
topt = &s[optlen];
switch (*topt) {
case '+':
case '-':
o->sign = (*topt == '+') ? SIGN_PLUS : SIGN_MINUS;
o->value = str_to_long(&topt[1], o->option);
break;
case 'o':
if (strncmp(topt, offchar,
slen > OFFCHAR_LEN ? slen : OFFCHAR_LEN) == 0)
o->value = 0;
else if (strncmp(topt, onchar,
slen > ONCHAR_LEN ? slen : ONCHAR_LEN) == 0)
o->value = 1;
break;
case 'u':
if (strncmp(topt, "up", slen > 2 ? slen : 2) == 0)
o->value = 1;
break;
case 'd':
if (strncmp(topt, "down", slen > 4 ? slen : 4) == 0)
o->value = 0;
break;
default:
if (*topt > 47 && *topt < 58)
o->value = str_to_long(topt, o->option);
break;
}
if (o->value == VALUE_NONE) {
warnx(badvalue, topt);
return 0;
}
return 1;
}
/*
* Print current value of the parameter.
*/
static void
print_value(int optval)
{
if (optval == OPTION_NONE)
return;
switch (optval) {
case OPTION_SEARCH:
/* FALLTHROUGH */
case OPTION_FREQUENCY:
printf("%.2fMHz", (float)ri.freq / 1000.);
break;
case OPTION_REFERENCE:
printf("%ukHz", ri.rfreq);
break;
case OPTION_SENSITIVITY:
printf("%umkV", ri.lock);
break;
case OPTION_MUTE:
printf(ri.mute ? onchar : offchar);
break;
case OPTION_MONO:
printf(ri.stereo ? offchar : onchar);
break;
case OPTION_STEREO:
printf(ri.stereo ? onchar : offchar);
break;
case OPTION_VOLUME:
default:
printf("%u", ri.volume);
break;
}
}
static void
show_int_val(u_long val, const char *nick, char *append, int silent)
{
show_verbose(nick, silent);
printf("%lu%s\n", val, append);
}
static void
show_float_val(float val, const char *nick, char *append, int silent)
{
show_verbose(nick, silent);
printf("%.2f%s\n", val, append);
}
static void
show_char_val(const char *val, const char *nick, int silent)
{
show_verbose(nick, silent);
printf("%s\n", val);
}
/*
* Print all available parameters
*/
static void
print_vars(int silent)
{
show_int_val(ri.volume, varname[OPTION_VOLUME], "", silent);
show_float_val((float)ri.freq / 1000., varname[OPTION_FREQUENCY],
"MHz", silent);
show_char_val(ri.mute ? onchar : offchar, varname[OPTION_MUTE], silent);
if (ri.caps & RADIO_CAPS_REFERENCE_FREQ)
show_int_val(ri.rfreq, varname[OPTION_REFERENCE], "kHz", silent);
if (ri.caps & RADIO_CAPS_LOCK_SENSITIVITY)
show_int_val(ri.lock, varname[OPTION_SENSITIVITY], "mkV", silent);
if (ri.caps & RADIO_CAPS_DETECT_SIGNAL) {
show_verbose("signal", silent);
printf("%s\n", ri.info & RADIO_INFO_SIGNAL ? onchar : offchar);
}
if (ri.caps & RADIO_CAPS_DETECT_STEREO) {
show_verbose(varname[OPTION_STEREO], silent);
printf("%s\n", ri.info & RADIO_INFO_STEREO ? onchar : offchar);
}
if (!silent)
puts("card capabilities:");
if (ri.caps & RADIO_CAPS_SET_MONO)
puts("\tmanageable mono/stereo");
if (ri.caps & RADIO_CAPS_HW_SEARCH)
puts("\thardware search");
if (ri.caps & RADIO_CAPS_HW_AFC)
puts("\thardware AFC");
}