/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */ #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it weren't */ #endif #define _(s) gettext(s) #define MAXLINE 1024 #define AUDIO_CTRL_STEREO_LEFT(v) ((uint8_t)((v) & 0xff)) #define AUDIO_CTRL_STEREO_RIGHT(v) ((uint8_t)(((v) >> 8) & 0xff)) #define AUDIO_CTRL_STEREO_VAL(l, r) (((l) & 0xff) | (((r) & 0xff) << 8)) /* * These are borrowed from sys/audio/audio_common.h, where the values * are protected by _KERNEL. */ #define AUDIO_MN_TYPE_NBITS (4) #define AUDIO_MN_TYPE_MASK ((1U << AUDIO_MN_TYPE_NBITS) - 1) #define AUDIO_MINOR_MIXER (0) /* * Column display information * All are related to the types enumerated in col_t and any change should be * reflected in the corresponding indices and offsets for all the variables * accordingly. Most tweaks to the display can be done by adjusting the * values here. */ /* types of columns displayed */ typedef enum { COL_DV = 0, COL_NM, COL_VAL, COL_SEL} col_t; /* corresponding sizes of columns; does not include trailing null */ #define COL_DV_SZ 16 #define COL_NM_SZ 24 #define COL_VAL_SZ 10 #define COL_SEL_SZ 20 #define COL_MAX_SZ 64 /* corresponding sizes of columns, indexed by col_t value */ static int col_sz[] = { COL_DV_SZ, COL_NM_SZ, COL_VAL_SZ, COL_SEL_SZ }; /* used by callers of the printing function */ typedef struct col_prt { char *col_dv; char *col_nm; char *col_val; char *col_sel; } col_prt_t; /* columns displayed in order with vopt = 0 */ static int col_dpy[] = {COL_NM, COL_VAL}; static int col_dpy_len = sizeof (col_dpy) / sizeof (*col_dpy); /* tells the printing function what members to use; follows col_dpy[] */ static size_t col_dpy_prt[] = { offsetof(col_prt_t, col_nm), offsetof(col_prt_t, col_val), }; /* columns displayed in order with vopt = 1 */ static int col_dpy_vopt[] = { COL_DV, COL_NM, COL_VAL, COL_SEL}; static int col_dpy_vopt_len = sizeof (col_dpy_vopt) / sizeof (*col_dpy_vopt); /* tells the printing function what members to use; follows col_dpy_vopt[] */ static size_t col_dpy_prt_vopt[] = { offsetof(col_prt_t, col_dv), offsetof(col_prt_t, col_nm), offsetof(col_prt_t, col_val), offsetof(col_prt_t, col_sel) }; /* columns displayed in order with tofile = 1 */ static int col_dpy_tofile[] = { COL_NM, COL_VAL}; static int col_dpy_tofile_len = sizeof (col_dpy_tofile) / sizeof (*col_dpy_tofile); /* tells the printing function what members to use; follows col_dpy_tofile[] */ static size_t col_dpy_prt_tofile[] = { offsetof(col_prt_t, col_nm), offsetof(col_prt_t, col_val) }; /* * mixer and control accounting */ typedef struct cinfo { oss_mixext ci; oss_mixer_enuminfo *enump; } cinfo_t; typedef struct device { oss_card_info card; oss_mixerinfo mixer; int cmax; cinfo_t *controls; int mfd; dev_t devt; struct device *nextp; } device_t; static device_t *devices = NULL; /*PRINTFLIKE1*/ static void msg(char *fmt, ...) { va_list ap; va_start(ap, fmt); (void) vprintf(fmt, ap); va_end(ap); } /*PRINTFLIKE1*/ static void warn(char *fmt, ...) { va_list ap; va_start(ap, fmt); (void) vfprintf(stderr, fmt, ap); va_end(ap); } static void free_device(device_t *d) { int i; device_t **dpp; dpp = &devices; while ((*dpp) && ((*dpp) != d)) { dpp = &((*dpp)->nextp); } if (*dpp) { *dpp = d->nextp; } for (i = 0; i < d->cmax; i++) { if (d->controls[i].enump != NULL) free(d->controls[i].enump); } if (d->mfd >= 0) (void) close(d->mfd); free(d); } static void free_devices(void) { device_t *d = devices; while ((d = devices) != NULL) { free_device(d); } devices = NULL; } /* * adds to the end of global devices and returns a pointer to the new entry */ static device_t * alloc_device(void) { device_t *p; device_t *d = calloc(1, sizeof (*d)); d->card.card = -1; d->mixer.dev = -1; d->mfd = -1; if (devices == NULL) { devices = d; } else { for (p = devices; p->nextp != NULL; p = p->nextp) {} p->nextp = d; } return (d); } /* * cinfop->enump needs to be present * idx should be: >= 0 to < cinfop->ci.maxvalue */ static char * get_enum_str(cinfo_t *cinfop, int idx) { int sz = sizeof (*cinfop->ci.enum_present) * 8; if (cinfop->ci.enum_present[idx / sz] & (1 << (idx % sz))) return (cinfop->enump->strings + cinfop->enump->strindex[idx]); return (NULL); } /* * caller fills in d->mixer.devnode; func fills in the rest */ static int get_device_info(device_t *d) { int fd = -1; int i; cinfo_t *ci; if ((fd = open(d->mixer.devnode, O_RDWR)) < 0) { perror(_("Error opening device")); return (errno); } d->mfd = fd; d->cmax = -1; if (ioctl(fd, SNDCTL_MIX_NREXT, &d->cmax) < 0) { perror(_("Error getting control count")); return (errno); } d->controls = calloc(d->cmax, sizeof (*d->controls)); for (i = 0; i < d->cmax; i++) { ci = &d->controls[i]; ci->ci.dev = -1; ci->ci.ctrl = i; if (ioctl(fd, SNDCTL_MIX_EXTINFO, &ci->ci) < 0) { perror(_("Error getting control info")); return (errno); } if (ci->ci.type == MIXT_ENUM) { ci->enump = calloc(1, sizeof (*ci->enump)); ci->enump->dev = -1; ci->enump->ctrl = ci->ci.ctrl; if (ioctl(fd, SNDCTL_MIX_ENUMINFO, ci->enump) < 0) { perror(_("Error getting enum info")); return (errno); } } } return (0); } static int load_devices(void) { int rv = -1; int fd = -1; int i; oss_sysinfo si; device_t *d; if (devices != NULL) { /* already loaded */ return (0); } if ((fd = open("/dev/mixer", O_RDWR)) < 0) { rv = errno; warn(_("Error opening mixer\n")); goto OUT; } if (ioctl(fd, SNDCTL_SYSINFO, &si) < 0) { rv = errno; perror(_("Error getting system information")); goto OUT; } for (i = 0; i < si.nummixers; i++) { struct stat sbuf; d = alloc_device(); d->mixer.dev = i; if (ioctl(fd, SNDCTL_MIXERINFO, &d->mixer) != 0) { continue; } d->card.card = d->mixer.card_number; if ((ioctl(fd, SNDCTL_CARDINFO, &d->card) != 0) || (stat(d->mixer.devnode, &sbuf) != 0) || ((sbuf.st_mode & S_IFCHR) == 0)) { warn(_("Device present: %s\n"), d->mixer.devnode); free_device(d); continue; } d->devt = makedev(major(sbuf.st_rdev), minor(sbuf.st_rdev) & ~(AUDIO_MN_TYPE_MASK)); if ((rv = get_device_info(d)) != 0) { free_device(d); goto OUT; } } rv = 0; OUT: if (fd >= 0) (void) close(fd); return (rv); } static int ctype_valid(int type) { switch (type) { case MIXT_ONOFF: case MIXT_ENUM: case MIXT_MONOSLIDER: case MIXT_STEREOSLIDER: return (1); default: return (0); } } static void print_control_line(FILE *sfp, col_prt_t *colp, int vopt) { int i; size_t *col_prtp; int *col_dpyp; int col_cnt; int col_type; int width; char *colstr; char cbuf[COL_MAX_SZ + 1]; char line[128]; char *colsep = " "; if (sfp != NULL) { col_prtp = col_dpy_prt_tofile; col_dpyp = col_dpy_tofile; col_cnt = col_dpy_tofile_len; } else if (vopt) { col_prtp = col_dpy_prt_vopt; col_dpyp = col_dpy_vopt; col_cnt = col_dpy_vopt_len; } else { col_prtp = col_dpy_prt; col_dpyp = col_dpy; col_cnt = col_dpy_len; } line[0] = '\0'; for (i = 0; i < col_cnt; i++) { col_type = col_dpyp[i]; width = col_sz[col_type]; colstr = *(char **)(((size_t)colp) + col_prtp[i]); (void) snprintf(cbuf, sizeof (cbuf), "%- *s", width > 0 ? width : 1, (colstr == NULL) ? "" : colstr); (void) strlcat(line, cbuf, sizeof (line)); if (i < col_cnt - 1) (void) strlcat(line, colsep, sizeof (line)); } (void) fprintf(sfp ? sfp : stdout, "%s\n", line); } static void print_header(FILE *sfp, int vopt) { col_prt_t col; if (sfp) { col.col_nm = _("#CONTROL"); col.col_val = _("VALUE"); } else { col.col_dv = _("DEVICE"); col.col_nm = _("CONTROL"); col.col_val = _("VALUE"); col.col_sel = _("POSSIBLE"); } print_control_line(sfp, &col, vopt); } static int print_control(FILE *sfp, device_t *d, cinfo_t *cinfop, int vopt) { int mfd = d->mfd; char *devnm = d->card.shortname; oss_mixer_value cval; char *str; int i; int idx = -1; int rv = -1; char valbuf[COL_VAL_SZ + 1]; char selbuf[COL_SEL_SZ + 1]; col_prt_t col; cval.dev = -1; cval.ctrl = cinfop->ci.ctrl; if (ctype_valid(cinfop->ci.type)) { if (ioctl(mfd, SNDCTL_MIX_READ, &cval) < 0) { rv = errno; perror(_("Error reading control\n")); return (rv); } } else { return (0); } /* * convert the control value into a string */ switch (cinfop->ci.type) { case MIXT_ONOFF: (void) snprintf(valbuf, sizeof (valbuf), "%s", cval.value ? _("on") : _("off")); break; case MIXT_MONOSLIDER: (void) snprintf(valbuf, sizeof (valbuf), "%d", cval.value & 0xff); break; case MIXT_STEREOSLIDER: (void) snprintf(valbuf, sizeof (valbuf), "%d:%d", (int)AUDIO_CTRL_STEREO_LEFT(cval.value), (int)AUDIO_CTRL_STEREO_RIGHT(cval.value)); break; case MIXT_ENUM: str = get_enum_str(cinfop, cval.value); if (str == NULL) { warn(_("Bad enum index %d for control '%s'\n"), cval.value, cinfop->ci.extname); return (EINVAL); } (void) snprintf(valbuf, sizeof (valbuf), "%s", str); break; default: return (0); } /* * possible control values (range/selection) */ switch (cinfop->ci.type) { case MIXT_ONOFF: (void) snprintf(selbuf, sizeof (selbuf), _("on,off")); break; case MIXT_MONOSLIDER: (void) snprintf(selbuf, sizeof (selbuf), "%d-%d", cinfop->ci.minvalue, cinfop->ci.maxvalue); break; case MIXT_STEREOSLIDER: (void) snprintf(selbuf, sizeof (selbuf), "%d-%d:%d-%d", cinfop->ci.minvalue, cinfop->ci.maxvalue, cinfop->ci.minvalue, cinfop->ci.maxvalue); break; case MIXT_ENUM: /* * display the first choice on the same line, then display * the rest on multiple lines */ selbuf[0] = 0; for (i = 0; i < cinfop->ci.maxvalue; i++) { str = get_enum_str(cinfop, i); if (str == NULL) continue; if ((strlen(str) + 1 + strlen(selbuf)) >= sizeof (selbuf)) { break; } if (strlen(selbuf)) { (void) strlcat(selbuf, ",", sizeof (selbuf)); } (void) strlcat(selbuf, str, sizeof (selbuf)); } idx = i; break; default: (void) snprintf(selbuf, sizeof (selbuf), "-"); } col.col_dv = devnm; col.col_nm = strlen(cinfop->ci.extname) ? cinfop->ci.extname : cinfop->ci.id; while (strchr(col.col_nm, '_') != NULL) { col.col_nm = strchr(col.col_nm, '_') + 1; } col.col_val = valbuf; col.col_sel = selbuf; print_control_line(sfp, &col, vopt); /* non-verbose mode prints don't display the enum values */ if ((!vopt) || (sfp != NULL)) { return (0); } /* print leftover enum value selections */ while ((idx >= 0) && (idx < cinfop->ci.maxvalue)) { selbuf[0] = 0; for (i = idx; i < cinfop->ci.maxvalue; i++) { str = get_enum_str(cinfop, i); if (str == NULL) continue; if ((strlen(str) + 1 + strlen(selbuf)) >= sizeof (selbuf)) { break; } if (strlen(selbuf)) { (void) strlcat(selbuf, ",", sizeof (selbuf)); } (void) strlcat(selbuf, str, sizeof (selbuf)); } idx = i; col.col_dv = NULL; col.col_nm = NULL; col.col_val = NULL; col.col_sel = selbuf; print_control_line(sfp, &col, vopt); } return (0); } static int set_device_control(device_t *d, cinfo_t *cinfop, char *wstr, int vopt) { int mfd = d->mfd; oss_mixer_value cval; int wlen = strlen(wstr); int lval, rval; char *lstr, *rstr; char *str; int i; int rv = -1; cval.dev = -1; cval.ctrl = cinfop->ci.ctrl; cval.value = 0; switch (cinfop->ci.type) { case MIXT_ONOFF: cval.value = (strncmp(_("on"), wstr, wlen) == 0) ? 1 : 0; break; case MIXT_MONOSLIDER: cval.value = atoi(wstr); break; case MIXT_STEREOSLIDER: lstr = wstr; rstr = strchr(wstr, ':'); if (rstr != NULL) { *rstr = '\0'; rstr++; rval = atoi(rstr); lval = atoi(lstr); rstr--; *rstr = ':'; } else { lval = atoi(lstr); rval = lval; } cval.value = AUDIO_CTRL_STEREO_VAL(lval, rval); break; case MIXT_ENUM: for (i = 0; i < cinfop->ci.maxvalue; i++) { str = get_enum_str(cinfop, i); if (str == NULL) continue; if (strncmp(wstr, str, wlen) == 0) { cval.value = i; break; } } if (i >= cinfop->ci.maxvalue) { warn(_("Invalid enumeration value\n")); return (EINVAL); } break; default: warn(_("Unsupported control type: %d\n"), cinfop->ci.type); return (EINVAL); } if (vopt) { msg(_("%s: '%s' set to '%s'\n"), d->card.shortname, cinfop->ci.extname, wstr); } if (ioctl(mfd, SNDCTL_MIX_WRITE, &cval) < 0) { rv = errno; perror(_("Error writing control")); return (rv); } rv = 0; return (rv); } static void help(void) { #define HELP_STR _( \ "audioctl list-devices\n" \ " list all audio devices\n" \ "\n" \ "audioctl show-device [ -v ] [ -d ]\n" \ " display information about an audio device\n" \ "\n" \ "audioctl show-control [ -v ] [ -d ] [ ... ]\n" \ " get the value of a specific control (all if not specified)\n" \ "\n" \ "audioctl set-control [ -v ] [ -d ] \n" \ " set the value of a specific control\n" \ "\n" \ "audioctl save-controls [ -d ] [ -f ] \n" \ " save all control settings for the device to a file\n" \ "\n" \ "audioctl load-controls [ -d ] \n" \ " restore previously saved control settings to device\n" \ "\n" \ "audioctl help\n" \ " show this message.\n") (void) fprintf(stderr, HELP_STR); } dev_t device_devt(char *name) { struct stat sbuf; if ((stat(name, &sbuf) != 0) || ((sbuf.st_mode & S_IFCHR) == 0)) { /* Not a device node! */ return (0); } return (makedev(major(sbuf.st_rdev), minor(sbuf.st_rdev) & ~(AUDIO_MN_TYPE_MASK))); } static device_t * find_device(char *name) { dev_t devt; device_t *d; /* * User may have specified: * * /dev/dsp[] * /dev/mixer[] * /dev/audio[9] * /dev/audioctl[] * /dev/sound/{,ctl,dsp,mixer} * /dev/sound/:{,ctl,dsp,mixer} * * We can canonicalize these by looking at the dev_t though. */ if (load_devices() != 0) { return (NULL); } if (name == NULL) name = getenv("AUDIODEV"); if ((name == NULL) || (strcmp(name, "/dev/mixer") == 0)) { /* /dev/mixer node doesn't point to real hw */ name = "/dev/dsp"; } if (*name == '/') { /* if we have a full path, convert to the devt */ if ((devt = device_devt(name)) == 0) { warn(_("No such audio device.\n")); return (NULL); } name = NULL; } for (d = devices; d != NULL; d = d->nextp) { oss_card_info *card = &d->card; if ((name) && (strcmp(name, card->shortname) == 0)) { return (d); } if (devt == d->devt) { return (d); } } warn(_("No such audio device.\n")); return (NULL); } int do_list_devices(int argc, char **argv) { int optc; int verbose = 0; device_t *d; while ((optc = getopt(argc, argv, "v")) != EOF) { switch (optc) { case 'v': verbose++; break; default: help(); return (-1); } } argc -= optind; argv += optind; if (argc != 0) { help(); return (-1); } if (load_devices() != 0) { return (-1); } for (d = devices; d != NULL; d = d->nextp) { if ((d->mixer.enabled == 0) && (!verbose)) continue; if (verbose) { msg(_("%s (%s)\n"), d->card.shortname, d->mixer.devnode); } else { msg(_("%s\n"), d->card.shortname); } } return (0); } int do_show_device(int argc, char **argv) { int optc; char *devname = NULL; device_t *d; while ((optc = getopt(argc, argv, "d:v")) != EOF) { switch (optc) { case 'd': devname = optarg; break; case 'v': break; default: help(); return (-1); } } argc -= optind; argv += optind; if (argc != 0) { help(); return (-1); } if ((d = find_device(devname)) == NULL) { return (ENODEV); } msg(_("Device: %s\n"), d->mixer.devnode); msg(_(" Name = %s\n"), d->card.shortname); msg(_(" Config = %s\n"), d->card.longname); if (strlen(d->card.hw_info)) { msg(_(" HW Info = %s"), d->card.hw_info); } return (0); } int do_show_control(int argc, char **argv) { int optc; int rval = 0; int verbose = 0; device_t *d; char *devname = NULL; int i; int j; int rv; char *n; cinfo_t *cinfop; while ((optc = getopt(argc, argv, "d:v")) != EOF) { switch (optc) { case 'd': devname = optarg; break; case 'v': verbose++; break; default: help(); return (-1); } } argc -= optind; argv += optind; if ((d = find_device(devname)) == NULL) { return (ENODEV); } print_header(NULL, verbose); if (argc == 0) { /* do them all! */ for (i = 0; i < d->cmax; i++) { cinfop = &d->controls[i]; rv = print_control(NULL, d, cinfop, verbose); rval = rval ? rval : rv; } return (rval); } for (i = 0; i < argc; i++) { for (j = 0; j < d->cmax; j++) { cinfop = &d->controls[j]; n = strrchr(cinfop->ci.extname, '_'); n = n ? n + 1 : cinfop->ci.extname; if (strcmp(argv[i], n) == 0) { rv = print_control(NULL, d, cinfop, verbose); rval = rval ? rval : rv; break; } } /* Didn't find requested control */ if (j == d->cmax) { warn(_("No such control: %s\n"), argv[i]); rval = rval ? rval : ENODEV; } } return (rval); } int do_set_control(int argc, char **argv) { int optc; int rval = 0; int verbose = 0; device_t *d; char *devname = NULL; char *cname; char *value; int i; int found; int rv; char *n; cinfo_t *cinfop; while ((optc = getopt(argc, argv, "d:v")) != EOF) { switch (optc) { case 'd': devname = optarg; break; case 'v': verbose = 1; break; default: help(); return (-1); } } argc -= optind; argv += optind; if (argc != 2) { help(); return (-1); } cname = argv[0]; value = argv[1]; if ((d = find_device(devname)) == NULL) { return (ENODEV); } for (i = 0, found = 0; i < d->cmax; i++) { cinfop = &d->controls[i]; n = strrchr(cinfop->ci.extname, '_'); n = n ? n + 1 : cinfop->ci.extname; if (strcmp(cname, n) != 0) { continue; } found = 1; rv = set_device_control(d, cinfop, value, verbose); rval = rval ? rval : rv; } if (!found) { warn(_("No such control: %s\n"), cname); } return (rval); } int do_save_controls(int argc, char **argv) { int optc; int rval = 0; device_t *d; char *devname = NULL; char *fname; int i; int rv; cinfo_t *cinfop; FILE *fp; int fd; int mode; mode = O_WRONLY | O_CREAT | O_EXCL; while ((optc = getopt(argc, argv, "d:f")) != EOF) { switch (optc) { case 'd': devname = optarg; break; case 'f': mode &= ~O_EXCL; mode |= O_TRUNC; break; default: help(); return (-1); } } argc -= optind; argv += optind; if (argc != 1) { help(); return (-1); } fname = argv[0]; if ((d = find_device(devname)) == NULL) { return (ENODEV); } if ((fd = open(fname, mode, 0666)) < 0) { perror(_("Failed to create file")); return (errno); } if ((fp = fdopen(fd, "w")) == NULL) { perror(_("Unable to open file\n")); (void) close(fd); (void) unlink(fname); return (errno); } (void) fprintf(fp, "# Device: %s\n", d->mixer.devnode); (void) fprintf(fp, "# Name = %s\n", d->card.shortname); (void) fprintf(fp, "# Config = %s\n", d->card.longname); if (strlen(d->card.hw_info)) { (void) fprintf(fp, "# HW Info = %s", d->card.hw_info); } (void) fprintf(fp, "#\n"); print_header(fp, 0); for (i = 0; i < d->cmax; i++) { cinfop = &d->controls[i]; rv = print_control(fp, d, cinfop, 0); rval = rval ? rval : rv; } (void) fclose(fp); return (rval); } int do_load_controls(int argc, char **argv) { int optc; int rval = 0; device_t *d; char *devname = NULL; char *fname; char *cname; char *value; int i; int rv; cinfo_t *cinfop; FILE *fp; char linebuf[MAXLINE]; int lineno = 0; int found; while ((optc = getopt(argc, argv, "d:")) != EOF) { switch (optc) { case 'd': devname = optarg; break; default: help(); return (-1); } } argc -= optind; argv += optind; if (argc != 1) { help(); return (-1); } fname = argv[0]; if ((d = find_device(devname)) == NULL) { return (ENODEV); } if ((fp = fopen(fname, "r")) == NULL) { perror(_("Unable to open file")); return (errno); } while (fgets(linebuf, sizeof (linebuf), fp) != NULL) { lineno++; if (linebuf[strlen(linebuf) - 1] != '\n') { warn(_("Warning: line too long at line %d\n"), lineno); /* read in the rest of the line and discard it */ while (fgets(linebuf, sizeof (linebuf), fp) != NULL && (linebuf[strlen(linebuf) - 1] != '\n')) { continue; } continue; } /* we have a good line ... */ cname = strtok(linebuf, " \t\n"); /* skip comments and blank lines */ if ((cname == NULL) || (cname[0] == '#')) { continue; } value = strtok(NULL, " \t\n"); if ((value == NULL) || (*cname == 0)) { warn(_("Warning: missing value at line %d\n"), lineno); continue; } for (i = 0, found = 0; i < d->cmax; i++) { /* save and restore requires an exact match */ cinfop = &d->controls[i]; if (strcmp(cinfop->ci.extname, cname) != 0) { continue; } found = 1; rv = set_device_control(d, cinfop, value, 0); rval = rval ? rval : rv; } if (!found) { warn(_("No such control: %s\n"), cname); } } (void) fclose(fp); return (rval); } int mixer_walker(di_devlink_t dlink, void *arg) { const char *link; int num; int fd; int verbose = *(int *)arg; int num_offset; num_offset = sizeof ("/dev/mixer") - 1; link = di_devlink_path(dlink); if ((link == NULL) || (strncmp(link, "/dev/mixer", num_offset) != 0) || (!isdigit(link[num_offset]))) { return (DI_WALK_CONTINUE); } num = atoi(link + num_offset); if ((fd = open(link, O_RDWR)) < 0) { if (verbose) { if (errno == ENOENT) { msg(_("Device %s not present.\n"), link); } else { msg(_("Unable to open device %s: %s\n"), link, strerror(errno)); } } return (DI_WALK_CONTINUE); } if (verbose) { msg(_("Initializing link %s: "), link); } if (ioctl(fd, SNDCTL_SUN_SEND_NUMBER, &num) != 0) { if (verbose) { msg(_("failed: %s\n"), strerror(errno)); } } else { if (verbose) { msg(_("done.\n")); } } (void) close(fd); return (DI_WALK_CONTINUE); } int do_init_devices(int argc, char **argv) { int optc; di_devlink_handle_t dlh; int verbose = 0; while ((optc = getopt(argc, argv, "v")) != EOF) { switch (optc) { case 'v': verbose = 1; break; default: help(); return (-1); } } argc -= optind; argv += optind; if (argc != 0) { help(); return (-1); } dlh = di_devlink_init(NULL, 0); if (dlh == NULL) { perror(_("Unable to initialize devlink handle")); return (-1); } if (di_devlink_walk(dlh, "^mixer", NULL, 0, &verbose, mixer_walker) != 0) { perror(_("Unable to walk devlinks")); return (-1); } return (0); } int main(int argc, char **argv) { int rv = 0; int opt; (void) setlocale(LC_ALL, ""); (void) textdomain(TEXT_DOMAIN); while ((opt = getopt(argc, argv, "h")) != EOF) { switch (opt) { case 'h': help(); rv = 0; goto OUT; default: rv = EINVAL; break; } } if (rv) { goto OUT; } argc -= optind; argv += optind; if (argc < 1) { help(); rv = EINVAL; } else if (strcmp(argv[0], "help") == 0) { help(); rv = 0; } else if (strcmp(argv[0], "list-devices") == 0) { rv = do_list_devices(argc, argv); } else if (strcmp(argv[0], "show-device") == 0) { rv = do_show_device(argc, argv); } else if (strcmp(argv[0], "show-control") == 0) { rv = do_show_control(argc, argv); } else if (strcmp(argv[0], "set-control") == 0) { rv = do_set_control(argc, argv); } else if (strcmp(argv[0], "load-controls") == 0) { rv = do_load_controls(argc, argv); } else if (strcmp(argv[0], "save-controls") == 0) { rv = do_save_controls(argc, argv); } else if (strcmp(argv[0], "init-devices") == 0) { rv = do_init_devices(argc, argv); } else { help(); rv = EINVAL; } OUT: free_devices(); return (rv); }