version 1.2, 2001/10/04 22:43:45 |
version 1.3, 2001/12/05 19:40:46 |
|
|
/* $OpenBSD$ */ |
/* $RuOBSD: radioctl.c,v 1.4 2001/10/20 18:09:10 pva Exp $ */ |
/* $RuOBSD: radioctl.c,v 1.1 2001/10/03 05:53:35 gluk Exp $ */ |
|
|
|
/* |
/* |
* Copyright (c) 2001 Vladimir Popov <jumbo@narod.ru> |
* Copyright (c) 2001 Vladimir Popov <jumbo@narod.ru> |
|
|
*/ |
*/ |
|
|
#include <sys/ioctl.h> |
#include <sys/ioctl.h> |
|
#include "/sys/sys/radioio.h" |
|
#if 0 |
#include <sys/radioio.h> |
#include <sys/radioio.h> |
|
#endif |
|
|
#include <err.h> |
#include <err.h> |
#include <fcntl.h> |
#include <fcntl.h> |
|
|
#define OPTION_NONE ~0u |
#define OPTION_NONE ~0u |
#define VALUE_NONE ~0ul |
#define VALUE_NONE ~0ul |
|
|
|
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; |
extern char *__progname; |
const char *onchar = "on"; |
const char *onchar = "on"; |
#define ONCHAR_LEN 2 |
#define ONCHAR_LEN 2 |
const char *offchar = "off"; |
const char *offchar = "off"; |
#define OFFCHAR_LEN 3 |
#define OFFCHAR_LEN 3 |
|
|
u_long caps; |
static struct radio_info ri; |
|
|
static void usage(void); |
static int parse_opt(char *, struct opt_t *); |
static void print_vars(int, int); |
|
static void write_param(int, char *, int); |
|
static u_int parse_option(const char *); |
|
static u_long get_value(int, u_int); |
|
static void set_value(int, u_int, u_long); |
|
static u_long read_value(char *, u_int); |
|
static void print_value(int, u_int); |
|
static void warn_unsupported(u_int); |
|
static void ext_print(int, u_int, int); |
|
|
|
|
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 |
* Control behavior of a FM tuner - set frequency, volume etc |
*/ |
*/ |
int |
int |
main(int argc, char **argv) |
main(int argc, char **argv) |
{ |
{ |
|
struct opt_t opt; |
|
|
char *radiodev = NULL; |
char *radiodev = NULL; |
|
int rd = -1; |
|
|
char optchar; |
char optchar; |
char *param = NULL; |
char *param = NULL; |
int rd = -1; |
|
int show_vars = 0; |
int show_vars = 0; |
int set_param = 0; |
int set_param = 0; |
int silent = 0; |
int silent = 0; |
|
|
|
int optv = 0; |
|
|
if (argc < 2) { |
if (argc < 2) { |
usage(); |
usage(); |
exit(1); |
exit(1); |
|
|
switch (optchar) { |
switch (optchar) { |
case 'a': |
case 'a': |
show_vars = 1; |
show_vars = 1; |
optind = 1; |
optv = 1; |
break; |
break; |
case 'f': |
case 'f': |
radiodev = optarg; |
radiodev = optarg; |
optind = 2; |
optv = 2; |
break; |
break; |
case 'n': |
case 'n': |
silent = 1; |
silent = 1; |
optind = 1; |
optv = 1; |
break; |
break; |
case 'w': |
case 'w': |
set_param = 1; |
set_param = 1; |
param = optarg; |
param = optarg; |
optind = 2; |
optv = 2; |
break; |
break; |
default: |
default: |
usage(); |
usage(); |
/* NOTREACHED */ |
/* NOTREACHED */ |
} |
} |
|
|
argc -= optind; |
argc -= optv; |
argv += optind; |
argv += optv; |
} |
} |
|
|
rd = open(radiodev, O_RDONLY); |
rd = open(radiodev, O_RDONLY); |
if (rd < 0) |
if (rd < 0) |
err(1, "%s open error", radiodev); |
err(1, "%s open error", radiodev); |
|
|
if (ioctl(rd, RIOCGCAPS, &caps) < 0) |
if (ioctl(rd, RIOCGINFO, &ri) < 0) |
err(1, "RIOCGCAPS"); |
err(1, "RIOCGINFO"); |
|
|
if (argc > 1) |
if (argc > 1) |
ext_print(rd, parse_option(*(argv + 1)), silent); |
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 (set_param) |
write_param(rd, param, silent); |
if (parse_opt(param, &opt)) |
|
do_ioctls(rd, &opt, silent); |
|
|
if (show_vars) |
if (show_vars) |
print_vars(rd, silent); |
print_vars(silent); |
|
|
if (close(rd) < 0) |
if (close(rd) < 0) |
warn("%s close error", radiodev); |
warn("%s close error", radiodev); |
|
|
__progname); |
__progname); |
} |
} |
|
|
/* |
|
* Print all available parameters |
|
*/ |
|
static void |
static void |
print_vars(int fd, int silent) |
show_verbose(const char *nick, int silent) |
{ |
{ |
u_long var; |
|
|
|
ext_print(fd, OPTION_VOLUME, silent); |
|
ext_print(fd, OPTION_FREQUENCY, silent); |
|
ext_print(fd, OPTION_MUTE, silent); |
|
|
|
if (caps & RADIO_CAPS_REFERENCE_FREQ) |
|
ext_print(fd, OPTION_REFERENCE, silent); |
|
if (caps & RADIO_CAPS_LOCK_SENSITIVITY) |
|
ext_print(fd, OPTION_SENSITIVITY, silent); |
|
|
|
if (ioctl(fd, RIOCGINFO, &var) < 0) |
|
warn("RIOCGINFO"); |
|
if (caps & RADIO_CAPS_DETECT_SIGNAL) |
|
if (!silent) |
|
printf("%s=", "signal"); |
|
printf("%s\n", var & RADIO_INFO_SIGNAL ? onchar : offchar); |
|
if (caps & RADIO_CAPS_DETECT_STEREO) { |
|
if (!silent) |
|
printf("%s=", varname[OPTION_STEREO]); |
|
printf("%s\n", var & RADIO_INFO_STEREO ? onchar : offchar); |
|
} |
|
|
|
if (!silent) |
if (!silent) |
puts("card capabilities:"); |
printf("%s=", nick); |
if (caps & RADIO_CAPS_SET_MONO) |
|
puts("\tmanageable mono/stereo"); |
|
if (caps & RADIO_CAPS_HW_SEARCH) |
|
puts("\thardware search"); |
|
if (caps & RADIO_CAPS_HW_AFC) |
|
puts("\thardware AFC"); |
|
} |
} |
|
|
/* |
|
* Set new value of a parameter |
|
*/ |
|
static void |
static void |
write_param(int fd, char *param, int silent) |
warn_unsupported(int optval) |
{ |
{ |
int paramlen = 0; |
warnx("driver does not support `%s'", varname[optval]); |
int namelen = 0; |
} |
char *topt = NULL; |
|
const char *badvalue = "bad value `%s'"; |
|
u_int optval = OPTION_NONE; |
|
u_long var = VALUE_NONE; |
|
u_long addvar = VALUE_NONE; |
|
u_char sign = 0; |
|
|
|
if (param == NULL || *param == '\0') |
static void |
|
do_ioctls(int fd, struct opt_t *o, int silent) |
|
{ |
|
int oval; |
|
|
|
if (fd < 0 || o == NULL) |
return; |
return; |
|
|
paramlen = strlen(param); |
if (o->option == OPTION_SEARCH && !(ri.caps & RADIO_CAPS_HW_SEARCH)) { |
namelen = strcspn(param, "="); |
warn_unsupported(o->option); |
if (namelen > paramlen - 2) { |
|
warnx(badvalue, param); |
|
return; |
return; |
} |
} |
|
|
paramlen -= ++namelen; |
oval = o->option == OPTION_SEARCH ? OPTION_FREQUENCY : o->option; |
|
if (!silent) |
|
printf("%s: ", varname[oval]); |
|
|
if ((topt = (char *)malloc(namelen)) == NULL) { |
print_value(o->option); |
warn("memory allocation error"); |
printf(" -> "); |
return; |
|
|
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; |
|
} |
|
|
} |
} |
strlcpy(topt, param, namelen); |
|
optval = parse_option(topt); |
|
|
|
if (optval == OPTION_NONE) { |
if (ioctl(fd, RIOCGINFO, &ri) < 0) { |
free(topt); |
warn("RIOCGINFO"); |
return; |
return; |
} |
} |
|
|
if (!silent) |
print_value(o->option); |
printf("%s: ", topt); |
putchar('\n'); |
|
} |
|
|
free(topt); |
static void |
|
change_value(const struct opt_t o) |
|
{ |
|
int unsupported = 0; |
|
|
topt = ¶m[namelen]; |
if (o.value == VALUE_NONE) |
switch (*topt) { |
return; |
case '+': |
|
case '-': |
switch (o.option) { |
if ((addvar = read_value(topt + 1, optval)) == VALUE_NONE) |
case OPTION_VOLUME: |
break; |
update_value(o.sign, (u_long *)&ri.volume, o.value); |
if ((var = get_value(fd, optval)) == VALUE_NONE) |
break; |
break; |
case OPTION_FREQUENCY: |
sign++; |
update_value(o.sign, (u_long *)&ri.freq, o.value); |
if (*topt == '+') |
break; |
var += addvar; |
case OPTION_REFERENCE: |
|
if (ri.caps & RADIO_CAPS_REFERENCE_FREQ) |
|
update_value(o.sign, (u_long *)&ri.rfreq, o.value); |
else |
else |
var -= addvar; |
unsupported++; |
break; |
break; |
case 'o': |
case OPTION_MONO: |
if (strncmp(topt, offchar, |
/* FALLTHROUGH */ |
paramlen > OFFCHAR_LEN ? paramlen : OFFCHAR_LEN) == 0) |
case OPTION_STEREO: |
var = 0; |
if (ri.caps & RADIO_CAPS_SET_MONO) |
|
ri.stereo = o.option == OPTION_MONO ? !o.value : o.value; |
else |
else |
if (strncmp(topt, onchar, |
unsupported++; |
paramlen > ONCHAR_LEN ? paramlen : ONCHAR_LEN) == 0) |
|
var = 1; |
|
break; |
break; |
case 'u': |
case OPTION_SENSITIVITY: |
if (strncmp(topt, "up", paramlen > 2 ? paramlen : 2) == 0) |
if (ri.caps & RADIO_CAPS_LOCK_SENSITIVITY) |
var = 1; |
update_value(o.sign, (u_long *)&ri.lock, o.value); |
|
else |
|
unsupported++; |
break; |
break; |
case 'd': |
case OPTION_MUTE: |
if (strncmp(topt, "down", paramlen > 4 ? paramlen : 4) == 0) |
ri.mute = o.value; |
var = 0; |
|
break; |
break; |
default: |
|
if (*topt > 47 && *topt < 58) |
|
var = read_value(topt, optval); |
|
break; |
|
} |
} |
|
|
if (var == VALUE_NONE || (sign && addvar == VALUE_NONE)) { |
if ( unsupported ) |
warnx(badvalue, topt); |
warn_unsupported(o.option); |
return; |
|
} |
|
|
|
print_value(fd, optval); |
|
printf(" -> "); |
|
|
|
set_value(fd, optval, var); |
|
|
|
print_value(fd, optval); |
|
putchar('\n'); |
|
} |
} |
|
|
/* |
/* |
* Convert string to integer representation of a parameter |
* Convert string to integer representation of a parameter |
*/ |
*/ |
static u_int |
static int |
parse_option(const char *topt) |
str_to_opt(const char *topt) |
{ |
{ |
u_int res; |
int res, toptlen, varlen, len, varsize; |
int toptlen, varlen, len, varsize; |
|
|
|
if (topt == NULL || *topt == '\0') |
if (topt == NULL || *topt == '\0') |
return OPTION_NONE; |
return OPTION_NONE; |
|
|
return OPTION_NONE; |
return OPTION_NONE; |
} |
} |
|
|
/* |
|
* Returns current value of parameter optval |
|
*/ |
|
static u_long |
|
get_value(int fd, u_int optval) |
|
{ |
|
u_long var = VALUE_NONE; |
|
|
|
switch (optval) { |
|
case OPTION_VOLUME: |
|
if (ioctl(fd, RIOCGVOLU, &var) < 0) |
|
warn("RIOCGVOLU"); |
|
break; |
|
case OPTION_FREQUENCY: |
|
if (ioctl(fd, RIOCGFREQ, &var) < 0) |
|
warn("RIOCGFREQ"); |
|
break; |
|
case OPTION_REFERENCE: |
|
if (caps & RADIO_CAPS_REFERENCE_FREQ) |
|
if (ioctl(fd, RIOCGREFF, &var) < 0) |
|
warn("RIOCGREFF"); |
|
break; |
|
case OPTION_MONO: |
|
/* FALLTHROUGH */ |
|
case OPTION_STEREO: |
|
if (caps & RADIO_CAPS_SET_MONO) |
|
if (ioctl(fd, RIOCGMONO, &var) < 0) |
|
warn("RIOCGMONO"); |
|
break; |
|
case OPTION_SENSITIVITY: |
|
if (caps & RADIO_CAPS_LOCK_SENSITIVITY) |
|
if (ioctl(fd, RIOCGLOCK, &var) < 0) |
|
warn("RIOCGLOCK"); |
|
break; |
|
case OPTION_MUTE: |
|
if (ioctl(fd, RIOCGMUTE, &var) < 0) |
|
warn("RIOCGMUTE"); |
|
break; |
|
} |
|
|
|
if (var == VALUE_NONE) |
|
warn_unsupported(optval); |
|
|
|
return var; |
|
} |
|
|
|
/* |
|
* Set card parameter optval to value var |
|
*/ |
|
static void |
static void |
set_value(int fd, u_int optval, u_long var) |
update_value(int sign, u_long *value, u_long update) |
{ |
{ |
int unsupported = 0; |
switch (sign) { |
|
case SIGN_NONE: |
if (var == VALUE_NONE) |
*value = update; |
return; |
|
|
|
switch (optval) { |
|
case OPTION_VOLUME: |
|
if (ioctl(fd, RIOCSVOLU, &var) < 0) |
|
warn("RIOCSVOLU"); |
|
break; |
break; |
case OPTION_FREQUENCY: |
case SIGN_PLUS: |
if (ioctl(fd, RIOCSFREQ, &var) < 0) |
*value += update; |
warn("RIOCSFREQ"); |
|
break; |
break; |
case OPTION_REFERENCE: |
case SIGN_MINUS: |
if (caps & RADIO_CAPS_REFERENCE_FREQ) { |
*value -= update; |
if (ioctl(fd, RIOCSREFF, &var) < 0) |
|
warn("RIOCSREFF"); |
|
} else unsupported++; |
|
break; |
break; |
case OPTION_STEREO: |
|
var = !var; |
|
/* FALLTHROUGH */ |
|
case OPTION_MONO: |
|
if (caps & RADIO_CAPS_SET_MONO) { |
|
if (ioctl(fd, RIOCSMONO, &var) < 0) |
|
warn("RIOCSMONO"); |
|
} else unsupported++; |
|
break; |
|
case OPTION_SENSITIVITY: |
|
if (caps & RADIO_CAPS_LOCK_SENSITIVITY) { |
|
if (ioctl(fd, RIOCSLOCK, &var) < 0) |
|
warn("RIOCSLOCK"); |
|
} else unsupported++; |
|
break; |
|
case OPTION_SEARCH: |
|
if (caps & RADIO_CAPS_HW_SEARCH) { |
|
if (ioctl(fd, RIOCSSRCH, &var) < 0) |
|
warn("RIOCSSRCH"); |
|
} else unsupported++; |
|
break; |
|
case OPTION_MUTE: |
|
if (ioctl(fd, RIOCSMUTE, &var) < 0) |
|
warn("RIOCSMUTE"); |
|
break; |
|
} |
} |
|
|
if ( unsupported ) |
|
warn_unsupported(optval); |
|
} |
} |
|
|
/* |
/* |
* Convert string to float or unsigned integer |
* Convert string to unsigned integer |
*/ |
*/ |
static u_long |
static u_long |
read_value(char *str, u_int optval) |
str_to_long(char *str, int optval) |
{ |
{ |
u_long val; |
u_long 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. |
* Print current value of the parameter. |
*/ |
*/ |
static void |
static void |
print_value(int fd, u_int optval) |
print_value(int optval) |
{ |
{ |
u_long var, mhz; |
|
|
|
if (optval == OPTION_NONE) |
if (optval == OPTION_NONE) |
return; |
return; |
|
|
if ( optval == OPTION_SEARCH) |
|
var = get_value(fd, OPTION_FREQUENCY); |
|
else |
|
var = get_value(fd, optval); |
|
|
|
if (var == VALUE_NONE) |
|
return; |
|
|
|
switch (optval) { |
switch (optval) { |
case OPTION_SEARCH: |
case OPTION_SEARCH: |
/* FALLTHROUGH */ |
/* FALLTHROUGH */ |
case OPTION_FREQUENCY: |
case OPTION_FREQUENCY: |
mhz = var / 1000; |
printf("%.2fMHz", (float)ri.freq / 1000.); |
printf("%u.%uMHz", (u_int)mhz, |
|
(u_int)var / 10 - (u_int)mhz * 100); |
|
break; |
break; |
case OPTION_REFERENCE: |
case OPTION_REFERENCE: |
printf("%ukHz", (u_int)var); |
printf("%ukHz", ri.rfreq); |
break; |
break; |
case OPTION_SENSITIVITY: |
case OPTION_SENSITIVITY: |
printf("%umkV", (u_int)var); |
printf("%umkV", ri.lock); |
break; |
break; |
case OPTION_MUTE: |
case OPTION_MUTE: |
/* FALLTHROUGH */ |
printf(ri.mute ? onchar : offchar); |
|
break; |
case OPTION_MONO: |
case OPTION_MONO: |
printf("%s", var ? onchar : offchar); |
printf(ri.stereo ? offchar : onchar); |
break; |
break; |
case OPTION_STEREO: |
case OPTION_STEREO: |
printf("%s", var ? offchar : onchar); |
printf(ri.stereo ? onchar : offchar); |
break; |
break; |
|
case OPTION_VOLUME: |
default: |
default: |
printf("%u", (u_int)var); |
printf("%u", ri.volume); |
break; |
break; |
} |
} |
} |
} |
|
|
static void |
static void |
warn_unsupported(u_int optval) |
show_int_val(u_long val, const char *nick, char *append, int silent) |
{ |
{ |
warnx("driver does not support `%s'", varname[optval]); |
show_verbose(nick, silent); |
|
printf("%lu%s\n", val, append); |
} |
} |
|
|
static void |
static void |
ext_print(int fd, u_int optval, int silent) |
show_float_val(float val, const char *nick, char *append, int silent) |
{ |
{ |
if (optval == OPTION_NONE) |
show_verbose(nick, silent); |
return; |
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) |
if (!silent) |
printf("%s=", varname[optval]); |
puts("card capabilities:"); |
print_value(fd, optval); |
if (ri.caps & RADIO_CAPS_SET_MONO) |
putchar('\n'); |
puts("\tmanageable mono/stereo"); |
|
if (ri.caps & RADIO_CAPS_HW_SEARCH) |
|
puts("\thardware search"); |
|
if (ri.caps & RADIO_CAPS_HW_AFC) |
|
puts("\thardware AFC"); |
} |
} |