4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
22 * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
35 #include <sys/types.h>
37 #include <sys/mkdev.h>
41 #include <sys/param.h>
42 #include <sys/soundcard.h>
43 #include <libdevinfo.h>
45 #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
46 #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it weren't */
49 #define _(s) gettext(s)
53 #define AUDIO_CTRL_STEREO_LEFT(v) ((uint8_t)((v) & 0xff))
54 #define AUDIO_CTRL_STEREO_RIGHT(v) ((uint8_t)(((v) >> 8) & 0xff))
55 #define AUDIO_CTRL_STEREO_VAL(l, r) (((l) & 0xff) | (((r) & 0xff) << 8))
58 * These are borrowed from sys/audio/audio_common.h, where the values
59 * are protected by _KERNEL.
61 #define AUDIO_MN_TYPE_NBITS (4)
62 #define AUDIO_MN_TYPE_MASK ((1U << AUDIO_MN_TYPE_NBITS) - 1)
63 #define AUDIO_MINOR_MIXER (0)
67 * Column display information
68 * All are related to the types enumerated in col_t and any change should be
69 * reflected in the corresponding indices and offsets for all the variables
70 * accordingly. Most tweaks to the display can be done by adjusting the
74 /* types of columns displayed */
75 typedef enum { COL_DV
= 0, COL_NM
, COL_VAL
, COL_SEL
} col_t
;
77 /* corresponding sizes of columns; does not include trailing null */
84 /* corresponding sizes of columns, indexed by col_t value */
85 static int col_sz
[] = {
86 COL_DV_SZ
, COL_NM_SZ
, COL_VAL_SZ
, COL_SEL_SZ
89 /* used by callers of the printing function */
90 typedef struct col_prt
{
97 /* columns displayed in order with vopt = 0 */
98 static int col_dpy
[] = {COL_NM
, COL_VAL
};
99 static int col_dpy_len
= sizeof (col_dpy
) / sizeof (*col_dpy
);
101 /* tells the printing function what members to use; follows col_dpy[] */
102 static size_t col_dpy_prt
[] = {
103 offsetof(col_prt_t
, col_nm
),
104 offsetof(col_prt_t
, col_val
),
107 /* columns displayed in order with vopt = 1 */
108 static int col_dpy_vopt
[] = { COL_DV
, COL_NM
, COL_VAL
, COL_SEL
};
109 static int col_dpy_vopt_len
= sizeof (col_dpy_vopt
) / sizeof (*col_dpy_vopt
);
111 /* tells the printing function what members to use; follows col_dpy_vopt[] */
112 static size_t col_dpy_prt_vopt
[] = {
113 offsetof(col_prt_t
, col_dv
),
114 offsetof(col_prt_t
, col_nm
),
115 offsetof(col_prt_t
, col_val
),
116 offsetof(col_prt_t
, col_sel
)
119 /* columns displayed in order with tofile = 1 */
120 static int col_dpy_tofile
[] = { COL_NM
, COL_VAL
};
121 static int col_dpy_tofile_len
= sizeof (col_dpy_tofile
) /
122 sizeof (*col_dpy_tofile
);
124 /* tells the printing function what members to use; follows col_dpy_tofile[] */
125 static size_t col_dpy_prt_tofile
[] = {
126 offsetof(col_prt_t
, col_nm
),
127 offsetof(col_prt_t
, col_val
)
132 * mixer and control accounting
135 typedef struct cinfo
{
137 oss_mixer_enuminfo
*enump
;
140 typedef struct device
{
150 struct device
*nextp
;
153 static device_t
*devices
= NULL
;
162 (void) vprintf(fmt
, ap
);
173 (void) vfprintf(stderr
, fmt
, ap
);
178 free_device(device_t
*d
)
184 while ((*dpp
) && ((*dpp
) != d
)) {
185 dpp
= &((*dpp
)->nextp
);
190 for (i
= 0; i
< d
->cmax
; i
++) {
191 if (d
->controls
[i
].enump
!= NULL
)
192 free(d
->controls
[i
].enump
);
196 (void) close(d
->mfd
);
204 device_t
*d
= devices
;
206 while ((d
= devices
) != NULL
) {
215 * adds to the end of global devices and returns a pointer to the new entry
221 device_t
*d
= calloc(1, sizeof (*d
));
227 if (devices
== NULL
) {
230 for (p
= devices
; p
->nextp
!= NULL
; p
= p
->nextp
) {}
239 * cinfop->enump needs to be present
240 * idx should be: >= 0 to < cinfop->ci.maxvalue
243 get_enum_str(cinfo_t
*cinfop
, int idx
)
245 int sz
= sizeof (*cinfop
->ci
.enum_present
) * 8;
247 if (cinfop
->ci
.enum_present
[idx
/ sz
] & (1 << (idx
% sz
)))
248 return (cinfop
->enump
->strings
+ cinfop
->enump
->strindex
[idx
]);
255 * caller fills in d->mixer.devnode; func fills in the rest
258 get_device_info(device_t
*d
)
264 if ((fd
= open(d
->mixer
.devnode
, O_RDWR
)) < 0) {
265 perror(_("Error opening device"));
271 if (ioctl(fd
, SNDCTL_MIX_NREXT
, &d
->cmax
) < 0) {
272 perror(_("Error getting control count"));
276 d
->controls
= calloc(d
->cmax
, sizeof (*d
->controls
));
278 for (i
= 0; i
< d
->cmax
; i
++) {
279 ci
= &d
->controls
[i
];
284 if (ioctl(fd
, SNDCTL_MIX_EXTINFO
, &ci
->ci
) < 0) {
285 perror(_("Error getting control info"));
289 if (ci
->ci
.type
== MIXT_ENUM
) {
290 ci
->enump
= calloc(1, sizeof (*ci
->enump
));
292 ci
->enump
->ctrl
= ci
->ci
.ctrl
;
294 if (ioctl(fd
, SNDCTL_MIX_ENUMINFO
, ci
->enump
) < 0) {
295 perror(_("Error getting enum info"));
314 if (devices
!= NULL
) {
319 if ((fd
= open("/dev/mixer", O_RDWR
)) < 0) {
321 warn(_("Error opening mixer\n"));
325 if (ioctl(fd
, SNDCTL_SYSINFO
, &si
) < 0) {
327 perror(_("Error getting system information"));
331 for (i
= 0; i
< si
.nummixers
; i
++) {
338 if (ioctl(fd
, SNDCTL_MIXERINFO
, &d
->mixer
) != 0) {
342 d
->card
.card
= d
->mixer
.card_number
;
344 if ((ioctl(fd
, SNDCTL_CARDINFO
, &d
->card
) != 0) ||
345 (stat(d
->mixer
.devnode
, &sbuf
) != 0) ||
346 ((sbuf
.st_mode
& S_IFCHR
) == 0)) {
347 warn(_("Device present: %s\n"), d
->mixer
.devnode
);
351 d
->devt
= makedev(major(sbuf
.st_rdev
),
352 minor(sbuf
.st_rdev
) & ~(AUDIO_MN_TYPE_MASK
));
354 if ((rv
= get_device_info(d
)) != 0) {
370 ctype_valid(int type
)
375 case MIXT_MONOSLIDER
:
376 case MIXT_STEREOSLIDER
:
385 print_control_line(FILE *sfp
, col_prt_t
*colp
, int vopt
)
394 char cbuf
[COL_MAX_SZ
+ 1];
399 col_prtp
= col_dpy_prt_tofile
;
400 col_dpyp
= col_dpy_tofile
;
401 col_cnt
= col_dpy_tofile_len
;
403 col_prtp
= col_dpy_prt_vopt
;
404 col_dpyp
= col_dpy_vopt
;
405 col_cnt
= col_dpy_vopt_len
;
407 col_prtp
= col_dpy_prt
;
409 col_cnt
= col_dpy_len
;
414 for (i
= 0; i
< col_cnt
; i
++) {
415 col_type
= col_dpyp
[i
];
416 width
= col_sz
[col_type
];
417 colstr
= *(char **)(((size_t)colp
) + col_prtp
[i
]);
419 (void) snprintf(cbuf
, sizeof (cbuf
), "%- *s",
420 width
> 0 ? width
: 1,
421 (colstr
== NULL
) ? "" : colstr
);
423 (void) strlcat(line
, cbuf
, sizeof (line
));
425 (void) strlcat(line
, colsep
, sizeof (line
));
428 (void) fprintf(sfp
? sfp
: stdout
, "%s\n", line
);
433 print_header(FILE *sfp
, int vopt
)
438 col
.col_nm
= _("#CONTROL");
439 col
.col_val
= _("VALUE");
441 col
.col_dv
= _("DEVICE");
442 col
.col_nm
= _("CONTROL");
443 col
.col_val
= _("VALUE");
444 col
.col_sel
= _("POSSIBLE");
446 print_control_line(sfp
, &col
, vopt
);
451 print_control(FILE *sfp
, device_t
*d
, cinfo_t
*cinfop
, int vopt
)
454 char *devnm
= d
->card
.shortname
;
455 oss_mixer_value cval
;
460 char valbuf
[COL_VAL_SZ
+ 1];
461 char selbuf
[COL_SEL_SZ
+ 1];
465 cval
.ctrl
= cinfop
->ci
.ctrl
;
467 if (ctype_valid(cinfop
->ci
.type
)) {
468 if (ioctl(mfd
, SNDCTL_MIX_READ
, &cval
) < 0) {
470 perror(_("Error reading control\n"));
478 * convert the control value into a string
480 switch (cinfop
->ci
.type
) {
482 (void) snprintf(valbuf
, sizeof (valbuf
), "%s",
483 cval
.value
? _("on") : _("off"));
486 case MIXT_MONOSLIDER
:
487 (void) snprintf(valbuf
, sizeof (valbuf
), "%d",
491 case MIXT_STEREOSLIDER
:
492 (void) snprintf(valbuf
, sizeof (valbuf
), "%d:%d",
493 (int)AUDIO_CTRL_STEREO_LEFT(cval
.value
),
494 (int)AUDIO_CTRL_STEREO_RIGHT(cval
.value
));
498 str
= get_enum_str(cinfop
, cval
.value
);
500 warn(_("Bad enum index %d for control '%s'\n"),
501 cval
.value
, cinfop
->ci
.extname
);
505 (void) snprintf(valbuf
, sizeof (valbuf
), "%s", str
);
513 * possible control values (range/selection)
515 switch (cinfop
->ci
.type
) {
517 (void) snprintf(selbuf
, sizeof (selbuf
), _("on,off"));
520 case MIXT_MONOSLIDER
:
521 (void) snprintf(selbuf
, sizeof (selbuf
), "%d-%d",
522 cinfop
->ci
.minvalue
, cinfop
->ci
.maxvalue
);
524 case MIXT_STEREOSLIDER
:
525 (void) snprintf(selbuf
, sizeof (selbuf
), "%d-%d:%d-%d",
526 cinfop
->ci
.minvalue
, cinfop
->ci
.maxvalue
,
527 cinfop
->ci
.minvalue
, cinfop
->ci
.maxvalue
);
532 * display the first choice on the same line, then display
533 * the rest on multiple lines
536 for (i
= 0; i
< cinfop
->ci
.maxvalue
; i
++) {
537 str
= get_enum_str(cinfop
, i
);
541 if ((strlen(str
) + 1 + strlen(selbuf
)) >=
545 if (strlen(selbuf
)) {
546 (void) strlcat(selbuf
, ",", sizeof (selbuf
));
549 (void) strlcat(selbuf
, str
, sizeof (selbuf
));
555 (void) snprintf(selbuf
, sizeof (selbuf
), "-");
559 col
.col_nm
= strlen(cinfop
->ci
.extname
) ?
560 cinfop
->ci
.extname
: cinfop
->ci
.id
;
561 while (strchr(col
.col_nm
, '_') != NULL
) {
562 col
.col_nm
= strchr(col
.col_nm
, '_') + 1;
564 col
.col_val
= valbuf
;
565 col
.col_sel
= selbuf
;
566 print_control_line(sfp
, &col
, vopt
);
568 /* non-verbose mode prints don't display the enum values */
569 if ((!vopt
) || (sfp
!= NULL
)) {
573 /* print leftover enum value selections */
574 while ((idx
>= 0) && (idx
< cinfop
->ci
.maxvalue
)) {
576 for (i
= idx
; i
< cinfop
->ci
.maxvalue
; i
++) {
577 str
= get_enum_str(cinfop
, i
);
581 if ((strlen(str
) + 1 + strlen(selbuf
)) >=
585 if (strlen(selbuf
)) {
586 (void) strlcat(selbuf
, ",", sizeof (selbuf
));
589 (void) strlcat(selbuf
, str
, sizeof (selbuf
));
595 col
.col_sel
= selbuf
;
596 print_control_line(sfp
, &col
, vopt
);
604 set_device_control(device_t
*d
, cinfo_t
*cinfop
, char *wstr
, int vopt
)
607 oss_mixer_value cval
;
608 int wlen
= strlen(wstr
);
616 cval
.ctrl
= cinfop
->ci
.ctrl
;
619 switch (cinfop
->ci
.type
) {
621 cval
.value
= (strncmp(_("on"), wstr
, wlen
) == 0) ? 1 : 0;
624 case MIXT_MONOSLIDER
:
625 cval
.value
= atoi(wstr
);
628 case MIXT_STEREOSLIDER
:
630 rstr
= strchr(wstr
, ':');
645 cval
.value
= AUDIO_CTRL_STEREO_VAL(lval
, rval
);
649 for (i
= 0; i
< cinfop
->ci
.maxvalue
; i
++) {
650 str
= get_enum_str(cinfop
, i
);
654 if (strncmp(wstr
, str
, wlen
) == 0) {
660 if (i
>= cinfop
->ci
.maxvalue
) {
661 warn(_("Invalid enumeration value\n"));
667 warn(_("Unsupported control type: %d\n"), cinfop
->ci
.type
);
672 msg(_("%s: '%s' set to '%s'\n"), d
->card
.shortname
,
673 cinfop
->ci
.extname
, wstr
);
676 if (ioctl(mfd
, SNDCTL_MIX_WRITE
, &cval
) < 0) {
678 perror(_("Error writing control"));
690 #define HELP_STR _( \
691 "audioctl list-devices\n" \
692 " list all audio devices\n" \
694 "audioctl show-device [ -v ] [ -d <device> ]\n" \
695 " display information about an audio device\n" \
697 "audioctl show-control [ -v ] [ -d <device> ] [ <control> ... ]\n" \
698 " get the value of a specific control (all if not specified)\n" \
700 "audioctl set-control [ -v ] [ -d <device> ] <control> <value>\n" \
701 " set the value of a specific control\n" \
703 "audioctl save-controls [ -d <device> ] [ -f ] <file>\n" \
704 " save all control settings for the device to a file\n" \
706 "audioctl load-controls [ -d <device> ] <file>\n" \
707 " restore previously saved control settings to device\n" \
710 " show this message.\n")
712 (void) fprintf(stderr
, HELP_STR
);
716 device_devt(char *name
)
720 if ((stat(name
, &sbuf
) != 0) ||
721 ((sbuf
.st_mode
& S_IFCHR
) == 0)) {
722 /* Not a device node! */
726 return (makedev(major(sbuf
.st_rdev
),
727 minor(sbuf
.st_rdev
) & ~(AUDIO_MN_TYPE_MASK
)));
731 find_device(char *name
)
737 * User may have specified:
742 * /dev/audioctl[<num>]
743 * /dev/sound/<num>{,ctl,dsp,mixer}
744 * /dev/sound/<driver>:<num>{,ctl,dsp,mixer}
746 * We can canonicalize these by looking at the dev_t though.
749 if (load_devices() != 0) {
754 name
= getenv("AUDIODEV");
756 if ((name
== NULL
) ||
757 (strcmp(name
, "/dev/mixer") == 0)) {
758 /* /dev/mixer node doesn't point to real hw */
763 /* if we have a full path, convert to the devt */
764 if ((devt
= device_devt(name
)) == 0) {
765 warn(_("No such audio device.\n"));
771 for (d
= devices
; d
!= NULL
; d
= d
->nextp
) {
772 oss_card_info
*card
= &d
->card
;
774 if ((name
) && (strcmp(name
, card
->shortname
) == 0)) {
777 if (devt
== d
->devt
) {
782 warn(_("No such audio device.\n"));
787 do_list_devices(int argc
, char **argv
)
793 while ((optc
= getopt(argc
, argv
, "v")) != EOF
) {
810 if (load_devices() != 0) {
814 for (d
= devices
; d
!= NULL
; d
= d
->nextp
) {
816 if ((d
->mixer
.enabled
== 0) && (!verbose
))
820 msg(_("%s (%s)\n"), d
->card
.shortname
,
823 msg(_("%s\n"), d
->card
.shortname
);
831 do_show_device(int argc
, char **argv
)
834 char *devname
= NULL
;
837 while ((optc
= getopt(argc
, argv
, "d:v")) != EOF
) {
856 if ((d
= find_device(devname
)) == NULL
) {
860 msg(_("Device: %s\n"), d
->mixer
.devnode
);
861 msg(_(" Name = %s\n"), d
->card
.shortname
);
862 msg(_(" Config = %s\n"), d
->card
.longname
);
864 if (strlen(d
->card
.hw_info
)) {
865 msg(_(" HW Info = %s"), d
->card
.hw_info
);
872 do_show_control(int argc
, char **argv
)
878 char *devname
= NULL
;
885 while ((optc
= getopt(argc
, argv
, "d:v")) != EOF
) {
901 if ((d
= find_device(devname
)) == NULL
) {
905 print_header(NULL
, verbose
);
908 for (i
= 0; i
< d
->cmax
; i
++) {
910 cinfop
= &d
->controls
[i
];
911 rv
= print_control(NULL
, d
, cinfop
, verbose
);
912 rval
= rval
? rval
: rv
;
917 for (i
= 0; i
< argc
; i
++) {
918 for (j
= 0; j
< d
->cmax
; j
++) {
919 cinfop
= &d
->controls
[j
];
920 n
= strrchr(cinfop
->ci
.extname
, '_');
921 n
= n
? n
+ 1 : cinfop
->ci
.extname
;
922 if (strcmp(argv
[i
], n
) == 0) {
923 rv
= print_control(NULL
, d
, cinfop
, verbose
);
924 rval
= rval
? rval
: rv
;
928 /* Didn't find requested control */
930 warn(_("No such control: %s\n"), argv
[i
]);
931 rval
= rval
? rval
: ENODEV
;
939 do_set_control(int argc
, char **argv
)
945 char *devname
= NULL
;
954 while ((optc
= getopt(argc
, argv
, "d:v")) != EOF
) {
977 if ((d
= find_device(devname
)) == NULL
) {
981 for (i
= 0, found
= 0; i
< d
->cmax
; i
++) {
982 cinfop
= &d
->controls
[i
];
983 n
= strrchr(cinfop
->ci
.extname
, '_');
984 n
= n
? n
+ 1 : cinfop
->ci
.extname
;
985 if (strcmp(cname
, n
) != 0) {
989 rv
= set_device_control(d
, cinfop
, value
, verbose
);
990 rval
= rval
? rval
: rv
;
993 warn(_("No such control: %s\n"), cname
);
1000 do_save_controls(int argc
, char **argv
)
1005 char *devname
= NULL
;
1014 mode
= O_WRONLY
| O_CREAT
| O_EXCL
;
1016 while ((optc
= getopt(argc
, argv
, "d:f")) != EOF
) {
1039 if ((d
= find_device(devname
)) == NULL
) {
1043 if ((fd
= open(fname
, mode
, 0666)) < 0) {
1044 perror(_("Failed to create file"));
1048 if ((fp
= fdopen(fd
, "w")) == NULL
) {
1049 perror(_("Unable to open file\n"));
1051 (void) unlink(fname
);
1055 (void) fprintf(fp
, "# Device: %s\n", d
->mixer
.devnode
);
1056 (void) fprintf(fp
, "# Name = %s\n", d
->card
.shortname
);
1057 (void) fprintf(fp
, "# Config = %s\n", d
->card
.longname
);
1059 if (strlen(d
->card
.hw_info
)) {
1060 (void) fprintf(fp
, "# HW Info = %s", d
->card
.hw_info
);
1062 (void) fprintf(fp
, "#\n");
1064 print_header(fp
, 0);
1066 for (i
= 0; i
< d
->cmax
; i
++) {
1067 cinfop
= &d
->controls
[i
];
1068 rv
= print_control(fp
, d
, cinfop
, 0);
1069 rval
= rval
? rval
: rv
;
1078 do_load_controls(int argc
, char **argv
)
1083 char *devname
= NULL
;
1091 char linebuf
[MAXLINE
];
1095 while ((optc
= getopt(argc
, argv
, "d:")) != EOF
) {
1114 if ((d
= find_device(devname
)) == NULL
) {
1118 if ((fp
= fopen(fname
, "r")) == NULL
) {
1119 perror(_("Unable to open file"));
1123 while (fgets(linebuf
, sizeof (linebuf
), fp
) != NULL
) {
1125 if (linebuf
[strlen(linebuf
) - 1] != '\n') {
1126 warn(_("Warning: line too long at line %d\n"), lineno
);
1127 /* read in the rest of the line and discard it */
1128 while (fgets(linebuf
, sizeof (linebuf
), fp
) != NULL
&&
1129 (linebuf
[strlen(linebuf
) - 1] != '\n')) {
1135 /* we have a good line ... */
1136 cname
= strtok(linebuf
, " \t\n");
1137 /* skip comments and blank lines */
1138 if ((cname
== NULL
) || (cname
[0] == '#')) {
1141 value
= strtok(NULL
, " \t\n");
1142 if ((value
== NULL
) || (*cname
== 0)) {
1143 warn(_("Warning: missing value at line %d\n"), lineno
);
1147 for (i
= 0, found
= 0; i
< d
->cmax
; i
++) {
1148 /* save and restore requires an exact match */
1149 cinfop
= &d
->controls
[i
];
1150 if (strcmp(cinfop
->ci
.extname
, cname
) != 0) {
1154 rv
= set_device_control(d
, cinfop
, value
, 0);
1155 rval
= rval
? rval
: rv
;
1158 warn(_("No such control: %s\n"), cname
);
1167 mixer_walker(di_devlink_t dlink
, void *arg
)
1172 int verbose
= *(int *)arg
;
1175 num_offset
= sizeof ("/dev/mixer") - 1;
1177 link
= di_devlink_path(dlink
);
1179 if ((link
== NULL
) ||
1180 (strncmp(link
, "/dev/mixer", num_offset
) != 0) ||
1181 (!isdigit(link
[num_offset
]))) {
1182 return (DI_WALK_CONTINUE
);
1185 num
= atoi(link
+ num_offset
);
1186 if ((fd
= open(link
, O_RDWR
)) < 0) {
1188 if (errno
== ENOENT
) {
1189 msg(_("Device %s not present.\n"), link
);
1191 msg(_("Unable to open device %s: %s\n"),
1192 link
, strerror(errno
));
1195 return (DI_WALK_CONTINUE
);
1199 msg(_("Initializing link %s: "), link
);
1201 if (ioctl(fd
, SNDCTL_SUN_SEND_NUMBER
, &num
) != 0) {
1203 msg(_("failed: %s\n"), strerror(errno
));
1211 return (DI_WALK_CONTINUE
);
1215 do_init_devices(int argc
, char **argv
)
1218 di_devlink_handle_t dlh
;
1221 while ((optc
= getopt(argc
, argv
, "v")) != EOF
) {
1239 dlh
= di_devlink_init(NULL
, 0);
1241 perror(_("Unable to initialize devlink handle"));
1245 if (di_devlink_walk(dlh
, "^mixer", NULL
, 0, &verbose
,
1246 mixer_walker
) != 0) {
1247 perror(_("Unable to walk devlinks"));
1254 main(int argc
, char **argv
)
1259 (void) setlocale(LC_ALL
, "");
1260 (void) textdomain(TEXT_DOMAIN
);
1262 while ((opt
= getopt(argc
, argv
, "h")) != EOF
) {
1284 } else if (strcmp(argv
[0], "help") == 0) {
1287 } else if (strcmp(argv
[0], "list-devices") == 0) {
1288 rv
= do_list_devices(argc
, argv
);
1289 } else if (strcmp(argv
[0], "show-device") == 0) {
1290 rv
= do_show_device(argc
, argv
);
1291 } else if (strcmp(argv
[0], "show-control") == 0) {
1292 rv
= do_show_control(argc
, argv
);
1293 } else if (strcmp(argv
[0], "set-control") == 0) {
1294 rv
= do_set_control(argc
, argv
);
1295 } else if (strcmp(argv
[0], "load-controls") == 0) {
1296 rv
= do_load_controls(argc
, argv
);
1297 } else if (strcmp(argv
[0], "save-controls") == 0) {
1298 rv
= do_save_controls(argc
, argv
);
1299 } else if (strcmp(argv
[0], "init-devices") == 0) {
1300 rv
= do_init_devices(argc
, argv
);