mirror of https://github.com/ericonr/sndio.git
remove labels, use the stream name instead. Disallow digits in
stream names and use the "unit" integer instead.
This commit is contained in:
parent
10066d5fa1
commit
043bb0950a
|
@ -18,8 +18,6 @@ Usage: configure [options]
|
|||
--disable-alsa disable alsa audio & midi backends
|
||||
--enable-sun enable sun audio backend [$sun]
|
||||
--disable-sun disable sun audio backend
|
||||
--enable-sun-mixer enable sun audio mixer backend [$sun_mixer]
|
||||
--disable-sun-mixer disable sun audio mixer backend
|
||||
--enable-rmidi enable character device midi backend [$rmidi]
|
||||
--disable-rmidi disable character device midi backend
|
||||
--enable-xvolkeys build xvolkeys utility (requires X) [$xvolkeys]
|
||||
|
@ -34,7 +32,6 @@ prefix=/usr/local # where to install sndio
|
|||
so="libsndio.so.\${MAJ}.\${MIN}" # shared libs to build
|
||||
alsa=no # do we want alsa support ?
|
||||
sun=no # do we want sun support ?
|
||||
sun_mixer=no # do we want sun mixer support ?
|
||||
rmidi=no # do we want support for raw char dev ?
|
||||
precision=16 # aucat/sndiod arithmetic precision
|
||||
user=_sndio # non-privileged user for sndio daemon
|
||||
|
@ -107,12 +104,6 @@ for i; do
|
|||
--disable-sun)
|
||||
sun=no
|
||||
shift;;
|
||||
--enable-sun-mixer)
|
||||
sun_mixer=yes
|
||||
shift;;
|
||||
--disable-sun-mixer)
|
||||
sun_mixer=no
|
||||
shift;;
|
||||
--enable-rmidi)
|
||||
rmidi=yes
|
||||
shift;;
|
||||
|
@ -166,14 +157,7 @@ fi
|
|||
# if using Sun API, add corresponding parameters
|
||||
#
|
||||
if [ $sun = yes ]; then
|
||||
defs="$defs -DUSE_SUN"
|
||||
fi
|
||||
|
||||
#
|
||||
# if using Sun mixer API, add corresponding parameters
|
||||
#
|
||||
if [ $sun_mixer = yes ]; then
|
||||
defs="$defs -DUSE_SUN_MIXER"
|
||||
defs="$defs -DUSE_SUN -DUSE_SUN_MIXER"
|
||||
fi
|
||||
|
||||
#
|
||||
|
@ -215,7 +199,6 @@ user..................... $user
|
|||
precision................ $precision
|
||||
alsa..................... $alsa
|
||||
sun...................... $sun
|
||||
sun mixer................ $sun_mixer
|
||||
rmidi.................... $rmidi
|
||||
xvolkeys................. $xvolkeys
|
||||
|
||||
|
|
|
@ -46,7 +46,7 @@ MAN7 = sndio.7
|
|||
# libraries to build and install
|
||||
#
|
||||
MAJ = 6
|
||||
MIN = 0
|
||||
MIN = 1
|
||||
SO = @so@
|
||||
|
||||
all: ${SO}
|
||||
|
|
|
@ -115,7 +115,8 @@ struct amsg {
|
|||
*/
|
||||
struct amsg_mix_chan {
|
||||
char str[AMSG_MIX_NAMEMAX]; /* stream name */
|
||||
char opt[AMSG_MIX_NAMEMAX]; /* stream name */
|
||||
int16_t unit; /* stream number */
|
||||
uint8_t __pad[2];
|
||||
};
|
||||
|
||||
/*
|
||||
|
|
|
@ -86,7 +86,6 @@ _siomix_create(struct siomix_hdl *hdl, struct siomix_ops *ops,
|
|||
hdl->ctl_cb = NULL;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
_siomix_psleep(struct siomix_hdl *hdl, int event)
|
||||
{
|
||||
|
@ -172,11 +171,12 @@ _siomix_ondesc_cb(struct siomix_hdl *hdl,
|
|||
struct siomix_desc *desc, unsigned int val)
|
||||
{
|
||||
if (desc) {
|
||||
DPRINTF("%u -> %s[%s].%s=%s[%s]\n",
|
||||
DPRINTF("_siomix_ondesc_cb: %u -> %s[%d].%s=%s[%d]:%d\n",
|
||||
desc->addr,
|
||||
desc->chan0.str, desc->chan0.opt,
|
||||
desc->chan0.str, desc->chan0.unit,
|
||||
desc->func,
|
||||
desc->chan1.str, desc->chan1.opt);
|
||||
desc->chan1.str, desc->chan1.unit,
|
||||
val);
|
||||
}
|
||||
if (hdl->desc_cb)
|
||||
hdl->desc_cb(hdl->desc_arg, desc, val);
|
||||
|
@ -185,6 +185,7 @@ _siomix_ondesc_cb(struct siomix_hdl *hdl,
|
|||
void
|
||||
_siomix_onctl_cb(struct siomix_hdl *hdl, unsigned int addr, unsigned int val)
|
||||
{
|
||||
DPRINTF("_siomix_onctl_cb: %u -> %u\n", addr, val);
|
||||
if (hdl->ctl_cb)
|
||||
hdl->ctl_cb(hdl->ctl_arg, addr, val);
|
||||
}
|
||||
|
|
|
@ -75,9 +75,9 @@ siomix_aucat_rdata(struct siomix_aucat_hdl *hdl)
|
|||
c = hdl->tmp;
|
||||
while (size >= sizeof(struct amsg_mix_desc)) {
|
||||
strlcpy(desc.chan0.str, c->chan0.str, SIOMIX_NAMEMAX);
|
||||
strlcpy(desc.chan0.opt, c->chan0.opt, SIOMIX_NAMEMAX);
|
||||
desc.chan0.unit = ntohs(c->chan0.unit);
|
||||
strlcpy(desc.chan1.str, c->chan1.str, SIOMIX_NAMEMAX);
|
||||
strlcpy(desc.chan1.opt, c->chan1.opt, SIOMIX_NAMEMAX);
|
||||
desc.chan1.unit = ntohs(c->chan1.unit);
|
||||
strlcpy(desc.func, c->func, SIOMIX_NAMEMAX);
|
||||
desc.type = c->type;
|
||||
desc.addr = ntohs(c->addr);
|
||||
|
|
|
@ -108,9 +108,9 @@ It's called once with a NULL argument to indicate that the full
|
|||
description was sent and that the caller has a consistent
|
||||
representation of the mixer.
|
||||
.Pp
|
||||
Then, whenever a mixer control description changes (currently only
|
||||
label strings may change, see below), the call-back is invoked with the
|
||||
updated information followed by a call with a NULL argument.
|
||||
Then, whenever a mixer control description changes, the call-back is
|
||||
invoked with the updated information followed by a call with a NULL
|
||||
argument.
|
||||
.Pp
|
||||
Controls are described by the
|
||||
.Va siomix_ondesc
|
||||
|
@ -118,7 +118,7 @@ stucture as follows:
|
|||
.Bd -literal
|
||||
struct siomix_chan {
|
||||
char str[SIOMIX_NAMEMAX]; /* stream name */
|
||||
char opt[SIOMIX_NAMEMAX]; /* optional channel */
|
||||
int unit; /* optional number */
|
||||
};
|
||||
|
||||
struct siomix_desc {
|
||||
|
@ -127,9 +127,8 @@ struct siomix_desc {
|
|||
#define SIOMIX_SW 3 /* on/off switch */
|
||||
#define SIOMIX_VEC 4 /* element of array of numbers */
|
||||
#define SIOMIX_LIST 5 /* element of array of switches */
|
||||
#define SIOMIX_LABEL 6 /* string attached to chan0 */
|
||||
unsigned int type; /* one of above */
|
||||
char func[SIOMIX_NAMEMAX]; /* parameter group name */
|
||||
char func[SIOMIX_NAMEMAX]; /* function name */
|
||||
struct siomix_chan chan0; /* affected channels */
|
||||
struct siomix_chan chan1; /* dito for vec, and list */
|
||||
};
|
||||
|
@ -145,7 +144,7 @@ The
|
|||
.Va type
|
||||
attribute indicates what the structure describes.
|
||||
Possible types are:
|
||||
.Bl -tag -width "SIOMIX_LABEL"
|
||||
.Bl -tag -width "SIOMIX_LIST"
|
||||
.It SIOMIX_NUM
|
||||
A continuous control in the 0..SIOMIX_INTMAX range.
|
||||
For instance the volume of the speaker.
|
||||
|
@ -160,18 +159,6 @@ from the line input to the speaker.
|
|||
An element of an array of on/off switches.
|
||||
For instance the line-in position of the
|
||||
speaker source selector.
|
||||
.It SIOMIX_LABEL
|
||||
A label attached to the channel.
|
||||
In mixers exposed by
|
||||
.Xr sndiod 1 ,
|
||||
they correspond to program names.
|
||||
The label string is stored in the
|
||||
.Va chan1
|
||||
attribute and is not unique.
|
||||
Labels may dynamically change, but their
|
||||
.Va addr
|
||||
attribute remains the same and can be used to figure out which
|
||||
label is changing.
|
||||
.El
|
||||
.Pp
|
||||
The
|
||||
|
|
|
@ -13,14 +13,12 @@
|
|||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
/*
|
||||
*
|
||||
* TODO
|
||||
* - fix ac97 based mixers
|
||||
*
|
||||
* the way the sun mixer is designed doesn't let us representing
|
||||
* it easily with the siomix api. For now expose only few
|
||||
* white-listed controls the same way as we do in kernel
|
||||
* for the wskbd volume keys.
|
||||
*/
|
||||
|
||||
#ifdef USE_SUN_MIXER
|
||||
#include <sys/types.h>
|
||||
#include <sys/ioctl.h>
|
||||
|
@ -37,12 +35,24 @@
|
|||
#include "debug.h"
|
||||
#include "siomix_priv.h"
|
||||
|
||||
#define SUN_TO_SIOMIX(v) (((v) * 127 + 127) / 255)
|
||||
#define SIOMIX_TO_SUN(v) (((v) * 255 + 63) / 127)
|
||||
|
||||
struct wskbd_vol
|
||||
{
|
||||
int nch; /* channels in the level control */
|
||||
int level_idx; /* index of the level control */
|
||||
int level_val[8]; /* current value */
|
||||
int mute_idx; /* index of the mute control */
|
||||
int mute_val; /* per channel state of mute control */
|
||||
int base_addr;
|
||||
char *name;
|
||||
};
|
||||
|
||||
struct siomix_sun_hdl {
|
||||
struct siomix_hdl siomix;
|
||||
struct mixer_devinfo *info;
|
||||
struct mixer_ctrl *curval;
|
||||
int fd, ninfo, events;
|
||||
int iclass, oclass, rclass;
|
||||
struct wskbd_vol spkr, mic;
|
||||
int fd, events;
|
||||
};
|
||||
|
||||
static void siomix_sun_close(struct siomix_hdl *);
|
||||
|
@ -66,13 +76,197 @@ struct siomix_ops siomix_sun_ops = {
|
|||
siomix_sun_ondesc
|
||||
};
|
||||
|
||||
static int
|
||||
initmute(struct siomix_sun_hdl *hdl, struct mixer_devinfo *info)
|
||||
{
|
||||
struct mixer_devinfo mi;
|
||||
|
||||
mi.index = info->next;
|
||||
for (mi.index = info->next; mi.index != -1; mi.index = mi.next) {
|
||||
if (ioctl(hdl->fd, AUDIO_MIXER_DEVINFO, &mi) < 0)
|
||||
break;
|
||||
if (strcmp(mi.label.name, AudioNmute) == 0)
|
||||
return mi.index;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int
|
||||
initvol(struct siomix_sun_hdl *hdl, struct wskbd_vol *vol, char *cn, char *dn)
|
||||
{
|
||||
struct mixer_devinfo dev, cls;
|
||||
|
||||
for (dev.index = 0; ; dev.index++) {
|
||||
if (ioctl(hdl->fd, AUDIO_MIXER_DEVINFO, &dev) < 0)
|
||||
break;
|
||||
if (dev.type != AUDIO_MIXER_VALUE)
|
||||
continue;
|
||||
cls.index = dev.mixer_class;
|
||||
if (ioctl(hdl->fd, AUDIO_MIXER_DEVINFO, &cls) < 0)
|
||||
break;
|
||||
if (strcmp(cls.label.name, cn) == 0 &&
|
||||
strcmp(dev.label.name, dn) == 0) {
|
||||
vol->nch = dev.un.v.num_channels;
|
||||
vol->level_idx = dev.index;
|
||||
vol->mute_idx = initmute(hdl, &dev);
|
||||
DPRINTF("using %s.%s, %d channels, %s\n",
|
||||
cn, dn, vol->nch,
|
||||
vol->mute_idx >= 0 ? "mute" : "no mute");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
vol->level_idx = vol->mute_idx = -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
init(struct siomix_sun_hdl *hdl)
|
||||
{
|
||||
static struct {
|
||||
char *cn, *dn;
|
||||
} spkr_names[] = {
|
||||
{AudioCoutputs, AudioNmaster},
|
||||
{AudioCinputs, AudioNdac},
|
||||
{AudioCoutputs, AudioNdac},
|
||||
{AudioCoutputs, AudioNoutput}
|
||||
}, mic_names[] = {
|
||||
{AudioCrecord, AudioNrecord},
|
||||
{AudioCrecord, AudioNvolume},
|
||||
{AudioCinputs, AudioNrecord},
|
||||
{AudioCinputs, AudioNvolume},
|
||||
{AudioCinputs, AudioNinput}
|
||||
};
|
||||
int i;
|
||||
|
||||
for (i = 0; i < sizeof(spkr_names) / sizeof(spkr_names[0]); i++) {
|
||||
if (initvol(hdl, &hdl->spkr,
|
||||
spkr_names[i].cn, spkr_names[i].dn)) {
|
||||
hdl->spkr.name = "spkr";
|
||||
hdl->spkr.base_addr = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (i = 0; i < sizeof(mic_names) / sizeof(mic_names[0]); i++) {
|
||||
if (initvol(hdl, &hdl->mic,
|
||||
mic_names[i].cn, mic_names[i].dn)) {
|
||||
hdl->mic.name = "mic";
|
||||
hdl->mic.base_addr = 64;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
setvol(struct siomix_sun_hdl *hdl, struct wskbd_vol *vol, int addr, int val)
|
||||
{
|
||||
struct mixer_ctrl ctrl;
|
||||
int i;
|
||||
|
||||
addr -= vol->base_addr;
|
||||
if (vol->level_idx >= 0 && addr >= 0 && addr < vol->nch) {
|
||||
if (vol->level_val[addr] == val) {
|
||||
DPRINTF("level %d, no change\n", val);
|
||||
return 0;
|
||||
}
|
||||
vol->level_val[addr] = val;
|
||||
ctrl.dev = vol->level_idx;
|
||||
ctrl.type = AUDIO_MIXER_VALUE;
|
||||
ctrl.un.value.num_channels = vol->nch;
|
||||
for (i = 0; i < vol->nch; i++) {
|
||||
ctrl.un.value.level[i] =
|
||||
SIOMIX_TO_SUN(vol->level_val[i]);
|
||||
}
|
||||
DPRINTF("vol %d setting to %d\n", addr, vol->level_val[addr]);
|
||||
if (ioctl(hdl->fd, AUDIO_MIXER_WRITE, &ctrl) < 0) {
|
||||
DPRINTF("level write failed\n");
|
||||
return 0;
|
||||
}
|
||||
_siomix_onctl_cb(&hdl->siomix, vol->base_addr + i, val);
|
||||
return 1;
|
||||
}
|
||||
|
||||
addr -= 32;
|
||||
if (vol->mute_idx >= 0 && addr >= 0 && addr < vol->nch) {
|
||||
val = val ? 1 : 0;
|
||||
if (vol->mute_val == val) {
|
||||
DPRINTF("mute %d, no change\n", val);
|
||||
return 0;
|
||||
}
|
||||
vol->mute_val = val;
|
||||
ctrl.dev = vol->mute_idx;
|
||||
ctrl.type = AUDIO_MIXER_ENUM;
|
||||
ctrl.un.ord = val;
|
||||
DPRINTF("mute setting to %d\n", val);
|
||||
if (ioctl(hdl->fd, AUDIO_MIXER_WRITE, &ctrl) < 0) {
|
||||
DPERROR("mute write\n");
|
||||
return 0;
|
||||
}
|
||||
for (i = 0; i < vol->nch; i++) {
|
||||
_siomix_onctl_cb(&hdl->siomix,
|
||||
vol->base_addr + 32 + i, val);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
scanvol(struct siomix_sun_hdl *hdl, struct wskbd_vol *vol)
|
||||
{
|
||||
struct siomix_desc desc;
|
||||
struct mixer_ctrl ctrl;
|
||||
int i, val;
|
||||
|
||||
if (vol->level_idx >= 0) {
|
||||
ctrl.dev = vol->level_idx;
|
||||
ctrl.type = AUDIO_MIXER_VALUE;
|
||||
ctrl.un.value.num_channels = vol->nch;
|
||||
if (ioctl(hdl->fd, AUDIO_MIXER_READ, &ctrl) < 0) {
|
||||
DPRINTF("mute read failed\n");
|
||||
return 0;
|
||||
}
|
||||
desc.type = SIOMIX_NUM;
|
||||
desc.chan1.str[0] = 0;
|
||||
desc.chan1.unit = -1;
|
||||
strlcpy(desc.func, "level", SIOMIX_NAMEMAX);
|
||||
strlcpy(desc.chan0.str, vol->name, SIOMIX_NAMEMAX);
|
||||
for (i = 0; i < vol->nch; i++) {
|
||||
desc.chan0.unit = i;
|
||||
desc.addr = vol->base_addr + i;
|
||||
val = SUN_TO_SIOMIX(ctrl.un.value.level[i]);
|
||||
vol->level_val[i] = val;
|
||||
_siomix_ondesc_cb(&hdl->siomix, &desc, val);
|
||||
}
|
||||
}
|
||||
if (vol->mute_idx >= 0) {
|
||||
ctrl.dev = vol->mute_idx;
|
||||
ctrl.type = AUDIO_MIXER_ENUM;
|
||||
if (ioctl(hdl->fd, AUDIO_MIXER_READ, &ctrl) < 0) {
|
||||
DPRINTF("mute read failed\n");
|
||||
return 0;
|
||||
}
|
||||
desc.type = SIOMIX_SW;
|
||||
desc.chan1.str[0] = 0;
|
||||
desc.chan1.unit = -1;
|
||||
strlcpy(desc.func, "mute", SIOMIX_NAMEMAX);
|
||||
strlcpy(desc.chan0.str, vol->name, SIOMIX_NAMEMAX);
|
||||
val = ctrl.un.ord ? 1 : 0;
|
||||
vol->mute_val = val;
|
||||
for (i = 0; i < vol->nch; i++) {
|
||||
desc.chan0.unit = i;
|
||||
desc.addr = vol->base_addr + 32 + i;
|
||||
_siomix_ondesc_cb(&hdl->siomix, &desc, val);
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
struct siomix_hdl *
|
||||
_siomix_sun_open(const char *str, unsigned int mode, int nbio)
|
||||
{
|
||||
struct siomix_sun_hdl *hdl;
|
||||
int i, fd, flags;
|
||||
char path[PATH_MAX];
|
||||
struct mixer_devinfo mi;
|
||||
int flags;
|
||||
|
||||
if (*str != '/') {
|
||||
DPRINTF("siomix_sun_open: %s: '/<devnum>' expected\n", str);
|
||||
|
@ -88,56 +282,15 @@ _siomix_sun_open(const char *str, unsigned int mode, int nbio)
|
|||
flags = O_RDWR;
|
||||
else
|
||||
flags = (mode & SIOMIX_WRITE) ? O_WRONLY : O_RDONLY;
|
||||
|
||||
while ((fd = open(path, flags)) < 0) {
|
||||
while ((hdl->fd = open(path, flags | O_CLOEXEC)) < 0) {
|
||||
if (errno == EINTR)
|
||||
continue;
|
||||
DPERROR(path);
|
||||
goto bad_free;
|
||||
}
|
||||
if (fcntl(fd, F_SETFD, FD_CLOEXEC) < 0) {
|
||||
DPERROR("FD_CLOEXEC");
|
||||
goto bad_close;
|
||||
}
|
||||
hdl->iclass = hdl->oclass = hdl->rclass = -1;
|
||||
hdl->fd = fd;
|
||||
|
||||
/*
|
||||
* count the number of mixer knobs, and fetch the full mixer
|
||||
* description
|
||||
*/
|
||||
for (mi.index = 0; ; mi.index++) {
|
||||
if (ioctl(fd, AUDIO_MIXER_DEVINFO, &mi) < 0)
|
||||
break;
|
||||
}
|
||||
hdl->ninfo = mi.index;
|
||||
hdl->info = malloc(hdl->ninfo * sizeof(struct mixer_devinfo));
|
||||
if (hdl->info == NULL) {
|
||||
DPERROR("malloc");
|
||||
goto bad_close;
|
||||
}
|
||||
hdl->curval = malloc(hdl->ninfo * sizeof(struct mixer_ctrl));
|
||||
if (hdl->curval == NULL) {
|
||||
DPERROR("malloc");
|
||||
goto bad_freeinfo;
|
||||
}
|
||||
for (i = 0; i < hdl->ninfo; i++) {
|
||||
hdl->info[i].index = i;
|
||||
if (ioctl(fd, AUDIO_MIXER_DEVINFO, &hdl->info[i]) < 0) {
|
||||
DPERROR("AUDIO_MIXER_DEVINFO");
|
||||
goto bad_freeval;
|
||||
}
|
||||
free(hdl);
|
||||
return NULL;
|
||||
}
|
||||
init(hdl);
|
||||
return (struct siomix_hdl *)hdl;
|
||||
bad_freeval:
|
||||
free(hdl->curval);
|
||||
bad_freeinfo:
|
||||
free(hdl->info);
|
||||
bad_close:
|
||||
close(fd);
|
||||
bad_free:
|
||||
free(hdl);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -149,345 +302,15 @@ siomix_sun_close(struct siomix_hdl *addr)
|
|||
free(hdl);
|
||||
}
|
||||
|
||||
/*
|
||||
* parse foo-4:5 and convert it to "foo", 4, 2
|
||||
*/
|
||||
static int
|
||||
copy_ch(struct siomix_sun_hdl *hdl,
|
||||
int icls, char *istr, char *ostr, int *rmin, int *rnum)
|
||||
{
|
||||
size_t len;
|
||||
char *sep, *endp;
|
||||
long cmin, cmax;
|
||||
|
||||
ostr[0] = 0;
|
||||
|
||||
sep = strchr(istr, '-');
|
||||
if (sep) {
|
||||
len = sep - istr;
|
||||
if (len >= SIOMIX_NAMEMAX - 1)
|
||||
return 0;
|
||||
memcpy(ostr, istr, len);
|
||||
ostr[len] = 0;
|
||||
istr = sep + 1;
|
||||
|
||||
cmin = strtol(istr, &endp, 10);
|
||||
if (endp != istr) {
|
||||
/*
|
||||
* this a "foo-0:3" style range
|
||||
*/
|
||||
istr = endp;
|
||||
if (*endp == ':') {
|
||||
istr++;
|
||||
cmax = strtol(istr, &endp, 10);
|
||||
if (endp == istr) {
|
||||
DPRINTF("bad range\n");
|
||||
return 0;
|
||||
}
|
||||
istr = endp;
|
||||
} else if (*endp == 0) {
|
||||
cmax = cmin;
|
||||
} else {
|
||||
DPRINTF("unknown range\n");
|
||||
return 0;
|
||||
}
|
||||
*rmin = cmin;
|
||||
*rnum = cmax - cmin + 1;
|
||||
} else {
|
||||
if (strcmp(ostr, "line") == 0) {
|
||||
/* rename make "line-foo" to "foo" */
|
||||
ostr[0] = 0;
|
||||
} else {
|
||||
/* append unknown suffix with '_' */
|
||||
strlcat(ostr, "_", SIOMIX_NAMEMAX);
|
||||
}
|
||||
strlcat(ostr, istr, SIOMIX_NAMEMAX);
|
||||
*rmin = 0;
|
||||
*rnum = 0;
|
||||
}
|
||||
} else {
|
||||
*rmin = 0;
|
||||
*rnum = 0;
|
||||
strlcpy(ostr, istr, SIOMIX_NAMEMAX);
|
||||
}
|
||||
if (icls == -1)
|
||||
return 1;
|
||||
/*
|
||||
* append "_in" and "_out" suffixes, as nowadays
|
||||
* most jacks are bidirectional
|
||||
*/
|
||||
if (strcmp(ostr, "mic") == 0 ||
|
||||
strcmp(ostr, "spkr") == 0 ||
|
||||
strcmp(ostr, "hp") == 0 ||
|
||||
strcmp(ostr, "line") == 0) {
|
||||
if (icls == hdl->iclass)
|
||||
strlcat(ostr, "_in", SIOMIX_NAMEMAX);
|
||||
if (icls == hdl->oclass)
|
||||
strlcat(ostr, "_out", SIOMIX_NAMEMAX);
|
||||
}
|
||||
|
||||
/*
|
||||
* record class may conflict with input/output
|
||||
*/
|
||||
if (icls == hdl->rclass) {
|
||||
if (strcmp(ostr, "volume") == 0)
|
||||
strlcpy(ostr, "rec", SIOMIX_NAMEMAX);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void
|
||||
make_opt(char *opt, int min, int num)
|
||||
{
|
||||
switch (num) {
|
||||
case 0:
|
||||
opt[0] = 0;
|
||||
break;
|
||||
case 1:
|
||||
snprintf(opt, SIOMIX_NAMEMAX, "%u", min);
|
||||
break;
|
||||
default:
|
||||
snprintf(opt, SIOMIX_NAMEMAX, "%u-%u", min, min + num - 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
copyname_num(struct siomix_sun_hdl *hdl,
|
||||
struct mixer_devinfo *info, struct siomix_desc *desc, int *rmin, int *rnum)
|
||||
{
|
||||
size_t len;
|
||||
char *sep, *istr;
|
||||
|
||||
sep = strchr(info->label.name, '_');
|
||||
if (sep) {
|
||||
strlcpy(desc->func, "mix", SIOMIX_NAMEMAX);
|
||||
desc->type = SIOMIX_VEC;
|
||||
len = sep - info->label.name;
|
||||
if (len >= SIOMIX_NAMEMAX - 1)
|
||||
return 0;
|
||||
memcpy(desc->chan0.str, info->label.name, len);
|
||||
desc->chan0.str[len] = 0;
|
||||
istr = sep + 1;
|
||||
if (!copy_ch(hdl, info->mixer_class, istr,
|
||||
desc->chan1.str, rmin, rnum))
|
||||
return 0;
|
||||
} else {
|
||||
strlcpy(desc->func, "level", SIOMIX_NAMEMAX);
|
||||
desc->type = SIOMIX_NUM;
|
||||
istr = info->label.name;
|
||||
if (!copy_ch(hdl, info->mixer_class, istr,
|
||||
desc->chan0.str, rmin, rnum))
|
||||
return 0;
|
||||
desc->chan1.str[0] = '\0';
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
copyname_enum(struct siomix_sun_hdl *hdl,
|
||||
struct mixer_devinfo *info, struct siomix_desc *desc, int *rmin, int *rnum)
|
||||
{
|
||||
char istr[SIOMIX_NAMEMAX], *sep;
|
||||
size_t len;
|
||||
|
||||
sep = strrchr(info->label.name, '.');
|
||||
if (sep == NULL)
|
||||
sep = strrchr(info->label.name, '_');
|
||||
if (sep == NULL) {
|
||||
if (info->prev < 0) {
|
||||
fprintf(stderr, "no separator\n");
|
||||
return 0;
|
||||
}
|
||||
strlcpy(desc->func, info->label.name, SIOMIX_NAMEMAX);
|
||||
while (info->prev >= 0)
|
||||
info = hdl->info + info->prev;
|
||||
if (!copy_ch(hdl, info->mixer_class, info->label.name,
|
||||
desc->chan0.str, rmin, rnum))
|
||||
return 0;
|
||||
desc->chan1.str[0] = 0;
|
||||
} else {
|
||||
strlcpy(desc->func, sep + 1, SIOMIX_NAMEMAX);
|
||||
len = sep - info->label.name;
|
||||
if (len >= SIOMIX_NAMEMAX - 1)
|
||||
return 0;
|
||||
memcpy(istr, info->label.name, len);
|
||||
istr[len] = '\0';
|
||||
if (!copy_ch(hdl, info->mixer_class, istr,
|
||||
desc->chan0.str, rmin, rnum))
|
||||
return 0;
|
||||
desc->chan1.str[0] = 0;
|
||||
}
|
||||
/*
|
||||
* certain cards expose adc[0-1].source and adc[2-3].source
|
||||
* as different types, which we forbid.
|
||||
*/
|
||||
if (strcmp(desc->func, "source") == 0) {
|
||||
if (info->type == AUDIO_MIXER_SET)
|
||||
strlcpy(desc->func, "sources", SIOMIX_NAMEMAX);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
ord_to_num(struct mixer_devinfo *info, int ord)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < info->un.e.num_mem; i++) {
|
||||
if (ord == info->un.e.member[i].ord)
|
||||
return i;
|
||||
}
|
||||
DPRINTF("mixer bug: order not found\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
mask_to_bit(struct mixer_devinfo *info, int index, int val)
|
||||
{
|
||||
int mask;
|
||||
|
||||
mask = info->un.s.member[index].mask;
|
||||
if ((mask & val) == mask)
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
enum_to_sw(struct mixer_devinfo *info, struct siomix_desc *desc)
|
||||
{
|
||||
char *v0, *v1;
|
||||
|
||||
if (info->un.e.num_mem != 2)
|
||||
return 0;
|
||||
v0 = info->un.e.member[ord_to_num(info, 0)].label.name;
|
||||
v1 = info->un.e.member[ord_to_num(info, 1)].label.name;
|
||||
if (strcmp(v0, "off") == 0 && strcmp(v1, "on") == 0)
|
||||
goto make_sw;
|
||||
if (strcmp(v0, "unplugged") == 0 && strcmp(v1, "plugged") == 0) {
|
||||
strlcpy(desc->func,
|
||||
info->un.e.member[1].label.name,
|
||||
SIOMIX_NAMEMAX);
|
||||
goto make_sw;
|
||||
}
|
||||
return 0;
|
||||
make_sw:
|
||||
desc->chan1.str[0] = 0;
|
||||
desc->chan1.opt[0] = 0;
|
||||
desc->type = SIOMIX_SW;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
siomix_sun_ondesc(struct siomix_hdl *addr)
|
||||
{
|
||||
struct siomix_sun_hdl *hdl = (struct siomix_sun_hdl *)addr;
|
||||
struct mixer_devinfo *info;
|
||||
struct mixer_ctrl *ctrl;
|
||||
struct siomix_desc desc;
|
||||
char *ostr;
|
||||
int i, j, v, cmin, cnum;
|
||||
|
||||
for (i = 0; i < hdl->ninfo; i++) {
|
||||
info = hdl->info + i;
|
||||
ctrl = hdl->curval + i;
|
||||
ctrl->dev = i;
|
||||
ctrl->type = info->type;
|
||||
if (ctrl->type == AUDIO_MIXER_CLASS) {
|
||||
if (strcmp(info->label.name, "inputs") == 0)
|
||||
hdl->iclass = i;
|
||||
if (strcmp(info->label.name, "outputs") == 0)
|
||||
hdl->oclass = i;
|
||||
if (strcmp(info->label.name, "record") == 0)
|
||||
hdl->rclass = i;
|
||||
continue;
|
||||
}
|
||||
if (ctrl->type == AUDIO_MIXER_VALUE)
|
||||
ctrl->un.value.num_channels = info->un.v.num_channels;
|
||||
if (ioctl(hdl->fd, AUDIO_MIXER_READ, ctrl) < 0) {
|
||||
DPERROR("AUDIO_MIXER_READ");
|
||||
hdl->siomix.eof = 1;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
info = hdl->info;
|
||||
for (i = 0; i < hdl->ninfo; i++) {
|
||||
DPRINTF("psarsing \"%s\"\n", info->label.name);
|
||||
switch (info->type) {
|
||||
case AUDIO_MIXER_VALUE:
|
||||
desc.addr = i * 32;
|
||||
if (!copyname_num(hdl, info, &desc, &cmin, &cnum))
|
||||
return 0;
|
||||
make_opt(desc.chan0.opt, cmin, cnum);
|
||||
desc.chan1.opt[0] = 0;
|
||||
for (j = 0; j < info->un.v.num_channels; j++) {
|
||||
v = hdl->curval[i].un.value.level[j] *
|
||||
127 / 255;
|
||||
ostr = desc.type == SIOMIX_NUM ?
|
||||
desc.chan0.opt : desc.chan1.opt;
|
||||
if (info->un.v.num_channels > 1) {
|
||||
make_opt(ostr, cmin + j, 1);
|
||||
} else {
|
||||
make_opt(ostr, cmin, cnum);
|
||||
}
|
||||
_siomix_ondesc_cb(&hdl->siomix, &desc, v);
|
||||
desc.addr++;
|
||||
}
|
||||
break;
|
||||
case AUDIO_MIXER_ENUM:
|
||||
desc.addr = i * 32;
|
||||
if (info->un.e.num_mem <= 1)
|
||||
break;
|
||||
if (!copyname_enum(hdl, info, &desc, &cmin, &cnum))
|
||||
return 0;
|
||||
if (enum_to_sw(info, &desc)) {
|
||||
make_opt(desc.chan0.opt, cmin, cnum);
|
||||
_siomix_ondesc_cb(&hdl->siomix, &desc,
|
||||
ord_to_num(info, hdl->curval[i].un.ord));
|
||||
break;
|
||||
}
|
||||
for (j = 0; j < info->un.e.num_mem; j++) {
|
||||
if (!copyname_enum(hdl, info,
|
||||
&desc, &cmin, &cnum))
|
||||
return 0;
|
||||
make_opt(desc.chan0.opt, cmin, cnum);
|
||||
if (!copy_ch(hdl, info->mixer_class,
|
||||
info->un.e.member[j].label.name,
|
||||
desc.chan1.str, &cmin, &cnum))
|
||||
return 0;
|
||||
make_opt(desc.chan1.opt, cmin, cnum);
|
||||
desc.type = SIOMIX_LIST;
|
||||
v = (j == ord_to_num(info,
|
||||
hdl->curval[i].un.ord)) ? 1 : 0;
|
||||
_siomix_ondesc_cb(&hdl->siomix, &desc, v);
|
||||
desc.addr++;
|
||||
}
|
||||
break;
|
||||
case AUDIO_MIXER_SET:
|
||||
desc.addr = i * 32;
|
||||
if (info->un.s.num_mem == 0)
|
||||
break;
|
||||
if (!copyname_enum(hdl, info, &desc, &cmin, &cnum))
|
||||
return 0;
|
||||
make_opt(desc.chan0.opt, cmin, cnum);
|
||||
desc.type = SIOMIX_LIST;
|
||||
for (j = 0; j < info->un.s.num_mem; j++) {
|
||||
if (!copy_ch(hdl, info->mixer_class,
|
||||
info->un.s.member[j].label.name,
|
||||
desc.chan1.str, &cmin, &cnum))
|
||||
return 0;
|
||||
make_opt(desc.chan1.opt, cmin, cnum);
|
||||
_siomix_ondesc_cb(&hdl->siomix, &desc,
|
||||
mask_to_bit(info, j,
|
||||
hdl->curval[i].un.mask));
|
||||
desc.addr++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
info++;
|
||||
if (!scanvol(hdl, &hdl->spkr) ||
|
||||
!scanvol(hdl, &hdl->mic)) {
|
||||
hdl->siomix.eof = 1;
|
||||
return 0;
|
||||
}
|
||||
_siomix_ondesc_cb(&hdl->siomix, NULL, 0);
|
||||
return 1;
|
||||
|
@ -496,8 +319,6 @@ siomix_sun_ondesc(struct siomix_hdl *addr)
|
|||
static int
|
||||
siomix_sun_onctl(struct siomix_hdl *addr)
|
||||
{
|
||||
//struct siomix_sun_hdl *hdl = (struct siomix_sun_hdl *)addr;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -505,43 +326,9 @@ static int
|
|||
siomix_sun_setctl(struct siomix_hdl *arg, unsigned int addr, unsigned int val)
|
||||
{
|
||||
struct siomix_sun_hdl *hdl = (struct siomix_sun_hdl *)arg;
|
||||
struct mixer_ctrl *ctrl;
|
||||
struct mixer_devinfo *info;
|
||||
int base, offs, oldv;
|
||||
|
||||
DPRINTF("siomix_sun_setctl: %d set to %d\n", addr, val);
|
||||
base = addr / 32;
|
||||
offs = addr % 32;
|
||||
ctrl = hdl->curval + base;
|
||||
info = hdl->info + base;
|
||||
|
||||
switch (ctrl->type) {
|
||||
case AUDIO_MIXER_VALUE:
|
||||
oldv = ctrl->un.value.level[offs];
|
||||
ctrl->un.value.level[offs] = (val * 255 + 63) / 127;
|
||||
break;
|
||||
case AUDIO_MIXER_ENUM:
|
||||
if (val == 0)
|
||||
return 1;
|
||||
oldv = ord_to_num(info, ctrl->un.ord);
|
||||
if (oldv == offs)
|
||||
return 1;
|
||||
_siomix_onctl_cb(&hdl->siomix, 32 * base + oldv, 0);
|
||||
ctrl->un.ord = info->un.e.member[offs].ord;
|
||||
break;
|
||||
case AUDIO_MIXER_SET:
|
||||
if (val)
|
||||
ctrl->un.mask |= info->un.s.member[offs].mask;
|
||||
else
|
||||
ctrl->un.mask &= ~info->un.s.member[offs].mask;
|
||||
break;
|
||||
default:
|
||||
DPRINTF("siomix_sun_setctl: wrong addr %d\n", addr);
|
||||
hdl->siomix.eof = 1;
|
||||
return 0;
|
||||
}
|
||||
if (ioctl(hdl->fd, AUDIO_MIXER_WRITE, ctrl) < 0) {
|
||||
DPERROR("siomix_sun_setctl");
|
||||
if (!setvol(hdl, &hdl->spkr, addr, val) ||
|
||||
!setvol(hdl, &hdl->mic, addr, val)) {
|
||||
hdl->siomix.eof = 1;
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -98,7 +98,7 @@ struct sio_cap {
|
|||
*/
|
||||
struct siomix_chan {
|
||||
char str[SIOMIX_NAMEMAX]; /* stream name */
|
||||
char opt[SIOMIX_NAMEMAX]; /* optional (sub-)stream name */
|
||||
int unit; /* optional stream number */
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -110,9 +110,8 @@ struct siomix_desc {
|
|||
#define SIOMIX_SW 3 /* on/off switch (0 or 1) */
|
||||
#define SIOMIX_VEC 4 /* number, element of vector */
|
||||
#define SIOMIX_LIST 5 /* switch, element of a list */
|
||||
#define SIOMIX_LABEL 6 /* label attached to chan0 */
|
||||
unsigned int type; /* one of above */
|
||||
char func[SIOMIX_NAMEMAX]; /* function name or label */
|
||||
char func[SIOMIX_NAMEMAX]; /* function name */
|
||||
struct siomix_chan chan0; /* affected channels */
|
||||
struct siomix_chan chan1; /* dito for SIOMIX_{VEC,LIST} */
|
||||
};
|
||||
|
|
|
@ -50,7 +50,7 @@ Dump data received from
|
|||
.Pa rmidi/0
|
||||
to stderr:
|
||||
.Bd -literal -offset indent
|
||||
$ midicat -di rmidi/0
|
||||
$ midicat -di rmidi/0 >/dev/null
|
||||
.Ed
|
||||
.Pp
|
||||
Send data from
|
||||
|
|
|
@ -50,9 +50,6 @@ Use this
|
|||
mixer device.
|
||||
.It Fl m
|
||||
Monitor and display mixer changes.
|
||||
.It Fl n
|
||||
Don't hide program names associated to audio streams (by
|
||||
default they are displayed as comments).
|
||||
.It Fl i
|
||||
Display characteristics of requested parameters
|
||||
instead of their values.
|
||||
|
@ -66,7 +63,8 @@ parameter to hide details that are not essential.
|
|||
If no commands are specified all valid parameters are displayed on
|
||||
.Em stdout .
|
||||
Unless
|
||||
.Fl d
|
||||
.Fl d ,
|
||||
.Fl m ,
|
||||
or
|
||||
.Fl i
|
||||
are used, displayed parameters are valid commands.
|
||||
|
@ -80,20 +78,19 @@ parameters respectively:
|
|||
.Pp
|
||||
On the left-hand side, the affected substream is specified
|
||||
by the stream name followed by an optional
|
||||
channel range.
|
||||
channel.
|
||||
Examples of left-hand side terms:
|
||||
.Pp
|
||||
.Dl spkr.level
|
||||
.Dl spkr[2-5].level
|
||||
.Dl spkr[6].level
|
||||
.Dl spkr6.level
|
||||
.Pp
|
||||
There are three parameter types: numbers, selectors and vectors.
|
||||
There are 4 parameter types: switches, numbers, selectors, and vectors.
|
||||
.Pp
|
||||
Numbers are specified in decimal and follow the same semantics
|
||||
as MIDI controllers.
|
||||
Values are in the 0..127 range and 64 is the neutral state (if applicable).
|
||||
Two-state controls (switches) take any value in the 0..63 range or
|
||||
in the 64..127 range typically to the
|
||||
Two-state controls (switches) take either 0 or 1 as value,
|
||||
typically corresponding to the
|
||||
.Em off
|
||||
and
|
||||
.Em on
|
||||
|
@ -108,7 +105,7 @@ is used instead of a number, then the switch is toggled.
|
|||
Examples:
|
||||
.Pp
|
||||
.Dl spkr.level=85
|
||||
.Dl spkr[4-5].level=+10
|
||||
.Dl spkr.level=+10
|
||||
.Dl spkr.mute=0
|
||||
.Dl spkr.mute=!
|
||||
.Pp
|
||||
|
@ -120,8 +117,8 @@ If no channel range is specified, the same
|
|||
range as the stream specified on the left-hand side is used.
|
||||
For instance the following are equivalent:
|
||||
.Pp
|
||||
.Dl record[1].source=mic
|
||||
.Dl record[1].source=mic[1]
|
||||
.Dl record1.source=mic
|
||||
.Dl record1.source=mic1
|
||||
.Pp
|
||||
.Pp Vectors
|
||||
Vectors are arrays of numbers.
|
||||
|
@ -152,5 +149,5 @@ stream to zero.
|
|||
.Pp
|
||||
The following commands are equivalent:
|
||||
.Pp
|
||||
.Dl $ sndioctl record[0].source=mic[0] record[1].source=mic[1]
|
||||
.Dl $ sndioctl record0.source=mic0 record1.source=mic1
|
||||
.Dl $ sndioctl record.source=mic
|
||||
|
|
|
@ -24,7 +24,6 @@
|
|||
|
||||
#define IS_IDENT(c) (((c) >= 'a' && (c) <= 'z') || \
|
||||
((c) >= 'A' && (c) <= 'Z') || \
|
||||
((c) >= '0' && (c) <= '9') || \
|
||||
((c) == '_'))
|
||||
|
||||
struct info {
|
||||
|
@ -44,21 +43,21 @@ struct info {
|
|||
int cmpdesc(struct siomix_desc *, struct siomix_desc *);
|
||||
int isdiag(struct info *);
|
||||
struct info *selpos(struct info *);
|
||||
struct info *vecent(struct info *, char *, char *);
|
||||
struct info *vecent(struct info *, char *, int);
|
||||
struct info *nextgrp(struct info *);
|
||||
struct info *nextpar(struct info *);
|
||||
struct info *firstent(struct info *, char *);
|
||||
struct info *nextent(struct info *, int);
|
||||
int matchpar(struct info *, char *, char *);
|
||||
int matchent(struct info *, char *, char *);
|
||||
int matchpar(struct info *, char *, int);
|
||||
int matchent(struct info *, char *, int);
|
||||
int ismono(struct info *);
|
||||
void print_chan(struct siomix_chan *, int);
|
||||
void print_desc(struct info *, int);
|
||||
void print_val(struct info *, int);
|
||||
void print_par(struct info *, int);
|
||||
void print_par(struct info *, int, char *);
|
||||
int parse_name(char **, char *);
|
||||
int parse_dec(char **, unsigned *);
|
||||
int parse_chan(char **, char *, char *);
|
||||
int parse_chan(char **, char *, int *);
|
||||
int parse_modeval(char **, int *, unsigned *);
|
||||
void dump(void);
|
||||
int cmd(char *);
|
||||
|
@ -69,7 +68,7 @@ void onctl(void *, unsigned, unsigned);
|
|||
|
||||
struct siomix_hdl *hdl;
|
||||
struct info *infolist;
|
||||
int n_flag = 0, i_flag = 0, v_flag = 0, m_flag = 0;
|
||||
int i_flag = 0, v_flag = 0, m_flag = 0;
|
||||
|
||||
/*
|
||||
* compare two siomix_desc structures, used to sort infolist
|
||||
|
@ -88,7 +87,7 @@ cmpdesc(struct siomix_desc *d1, struct siomix_desc *d2)
|
|||
res = strcmp(d1->func, d2->func);
|
||||
if (res != 0)
|
||||
return res;
|
||||
res = strcmp(d1->chan0.opt, d2->chan0.opt);
|
||||
res = d1->chan0.unit - d2->chan0.unit;
|
||||
if (d1->type == SIOMIX_VEC ||
|
||||
d1->type == SIOMIX_LIST) {
|
||||
if (res != 0)
|
||||
|
@ -96,7 +95,7 @@ cmpdesc(struct siomix_desc *d1, struct siomix_desc *d2)
|
|||
res = strcmp(d1->chan1.str, d2->chan1.str);
|
||||
if (res != 0)
|
||||
return res;
|
||||
res = strcmp(d1->chan1.opt, d2->chan1.opt);
|
||||
res = d1->chan1.unit - d2->chan1.unit;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
@ -107,10 +106,9 @@ cmpdesc(struct siomix_desc *d1, struct siomix_desc *d2)
|
|||
int
|
||||
isdiag(struct info *e)
|
||||
{
|
||||
if (strlen(e->desc.chan0.opt) == 0 ||
|
||||
strlen(e->desc.chan1.opt) == 0)
|
||||
if (e->desc.chan0.unit < 0 || e->desc.chan1.unit < 0)
|
||||
return 1;
|
||||
return strcmp(e->desc.chan1.opt, e->desc.chan0.opt) == 0;
|
||||
return e->desc.chan1.unit == e->desc.chan0.unit;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -132,11 +130,11 @@ selpos(struct info *i)
|
|||
* find the selector or vector entry with the given name and channels
|
||||
*/
|
||||
struct info *
|
||||
vecent(struct info *i, char *vstr, char *vopt)
|
||||
vecent(struct info *i, char *vstr, int vunit)
|
||||
{
|
||||
while (i != NULL) {
|
||||
if ((strcmp(i->desc.chan1.str, vstr) == 0) &&
|
||||
(strlen(vopt) == 0 || strcmp(i->desc.chan1.opt, vopt) == 0))
|
||||
(vunit < 0 || i->desc.chan1.unit == vunit))
|
||||
break;
|
||||
i = i->next;
|
||||
}
|
||||
|
@ -167,16 +165,18 @@ nextgrp(struct info *i)
|
|||
struct info *
|
||||
nextpar(struct info *i)
|
||||
{
|
||||
char *str, *opt, *func;
|
||||
char *str, *func;
|
||||
int unit;
|
||||
|
||||
func = i->desc.func;
|
||||
str = i->desc.chan0.str;
|
||||
opt = i->desc.chan0.opt;
|
||||
unit = i->desc.chan0.unit;
|
||||
for (i = i->next; i != NULL; i = i->next) {
|
||||
if (strcmp(i->desc.chan0.str, str) != 0 ||
|
||||
strcmp(i->desc.func, func) != 0)
|
||||
break;
|
||||
if (strcmp(i->desc.chan0.opt, opt) != 0)
|
||||
/* XXX: need to check for -1 ? */
|
||||
if (i->desc.chan0.unit != unit)
|
||||
return i;
|
||||
}
|
||||
return NULL;
|
||||
|
@ -213,18 +213,19 @@ firstent(struct info *g, char *vstr)
|
|||
struct info *
|
||||
nextent(struct info *i, int mono)
|
||||
{
|
||||
char *str, *opt, *func;
|
||||
char *str, *func;
|
||||
int unit;
|
||||
|
||||
func = i->desc.func;
|
||||
str = i->desc.chan0.str;
|
||||
opt = i->desc.chan0.opt;
|
||||
unit = i->desc.chan0.unit;
|
||||
for (i = i->next; i != NULL; i = i->next) {
|
||||
if (strcmp(i->desc.chan0.str, str) != 0 ||
|
||||
strcmp(i->desc.func, func) != 0)
|
||||
return NULL;
|
||||
if (mono)
|
||||
return i;
|
||||
if (strcmp(i->desc.chan0.opt, opt) == 0)
|
||||
if (i->desc.chan0.unit == unit)
|
||||
return i;
|
||||
}
|
||||
return NULL;
|
||||
|
@ -234,17 +235,17 @@ nextent(struct info *i, int mono)
|
|||
* return true if parameter matches the given name and channel range
|
||||
*/
|
||||
int
|
||||
matchpar(struct info *i, char *astr, char *aopt)
|
||||
matchpar(struct info *i, char *astr, int aunit)
|
||||
{
|
||||
if (strcmp(i->desc.chan0.str, astr) != 0)
|
||||
return 0;
|
||||
if (strlen(aopt) == 0)
|
||||
if (aunit < 0)
|
||||
return 1;
|
||||
else if (strlen(i->desc.chan0.opt) == 0) {
|
||||
fprintf(stderr, "opt used for parameter with no opt\n");
|
||||
else if (i->desc.chan0.unit < 0) {
|
||||
fprintf(stderr, "unit used for parameter with no unit\n");
|
||||
exit(1);
|
||||
}
|
||||
return strcmp(i->desc.chan0.opt, aopt) == 0;
|
||||
return i->desc.chan0.unit == aunit;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -252,17 +253,17 @@ matchpar(struct info *i, char *astr, char *aopt)
|
|||
* channel range
|
||||
*/
|
||||
int
|
||||
matchent(struct info *i, char *vstr, char *vopt)
|
||||
matchent(struct info *i, char *vstr, int vunit)
|
||||
{
|
||||
if (strcmp(i->desc.chan1.str, vstr) != 0)
|
||||
return 0;
|
||||
if (strlen(vopt) == 0)
|
||||
if (vunit < 0)
|
||||
return 1;
|
||||
else if (strlen(i->desc.chan1.opt) == 0) {
|
||||
fprintf(stderr, "opt used for parameter with no opt\n");
|
||||
else if (i->desc.chan1.unit < 0) {
|
||||
fprintf(stderr, "unit used for parameter with no unit\n");
|
||||
exit(1);
|
||||
}
|
||||
return strcmp(i->desc.chan1.opt, vopt) == 0;
|
||||
return i->desc.chan1.unit == vunit;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -277,8 +278,6 @@ ismono(struct info *g)
|
|||
|
||||
p1 = g;
|
||||
switch (g->desc.type) {
|
||||
case SIOMIX_LABEL:
|
||||
break;
|
||||
case SIOMIX_NUM:
|
||||
case SIOMIX_SW:
|
||||
for (p2 = g; p2 != NULL; p2 = nextpar(p2)) {
|
||||
|
@ -296,7 +295,7 @@ ismono(struct info *g)
|
|||
} else {
|
||||
e1 = vecent(p1,
|
||||
e2->desc.chan1.str,
|
||||
p1->desc.chan0.opt);
|
||||
p1->desc.chan0.unit);
|
||||
if (e1 == NULL)
|
||||
continue;
|
||||
if (e1->curval != e2->curval)
|
||||
|
@ -316,8 +315,8 @@ void
|
|||
print_chan(struct siomix_chan *c, int mono)
|
||||
{
|
||||
printf("%s", c->str);
|
||||
if (!mono && strlen(c->opt) > 0) {
|
||||
printf("[%s]", c->opt);
|
||||
if (!mono && c->unit >= 0) {
|
||||
printf("%d", c->unit);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -364,10 +363,6 @@ print_val(struct info *p, int mono)
|
|||
int more;
|
||||
|
||||
switch (p->desc.type) {
|
||||
case SIOMIX_LABEL:
|
||||
printf("%s", p->desc.chan1.str);
|
||||
//print_chan(&e->desc.chan1, mono);
|
||||
break;
|
||||
case SIOMIX_NUM:
|
||||
case SIOMIX_SW:
|
||||
printf("%u", p->curval);
|
||||
|
@ -395,34 +390,16 @@ print_val(struct info *p, int mono)
|
|||
* print ``<parameter>=<value>'' string (including '\n')
|
||||
*/
|
||||
void
|
||||
print_par(struct info *p, int mono)
|
||||
print_par(struct info *p, int mono, char *comment)
|
||||
{
|
||||
struct info *i;
|
||||
int more;
|
||||
|
||||
print_chan(&p->desc.chan0, mono);
|
||||
printf(".%s=", p->desc.func);
|
||||
if (i_flag)
|
||||
print_desc(p, mono);
|
||||
else
|
||||
print_val(p, mono);
|
||||
|
||||
/* append a comment with the labels (if any) */
|
||||
if (!n_flag && p->desc.type != SIOMIX_LABEL) {
|
||||
more = 0;
|
||||
for (i = infolist; i != NULL; i = i->next) {
|
||||
if (i->desc.type != SIOMIX_LABEL)
|
||||
continue;
|
||||
if (strcmp(i->desc.chan0.str, p->desc.chan0.str) == 0 &&
|
||||
strcmp(i->desc.chan0.opt, p->desc.chan0.opt) == 0) {
|
||||
if (!more) {
|
||||
printf("\t#");
|
||||
more = 1;
|
||||
}
|
||||
printf(" %s=%s", i->desc.func, i->desc.chan1.str);
|
||||
}
|
||||
}
|
||||
}
|
||||
print_val(p, mono);
|
||||
if (comment)
|
||||
printf(" # %s", comment);
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
|
@ -436,7 +413,7 @@ parse_name(char **line, char *name)
|
|||
unsigned len = 0;
|
||||
|
||||
if (!IS_IDENT(*p)) {
|
||||
fprintf(stderr, "letter/digit expected near '%s'\n", p);
|
||||
fprintf(stderr, "letter expected near '%s'\n", p);
|
||||
return 0;
|
||||
}
|
||||
while (IS_IDENT(*p)) {
|
||||
|
@ -483,25 +460,19 @@ parse_dec(char **line, unsigned *num)
|
|||
* parse a sub-stream, eg. "spkr[4-7]"
|
||||
*/
|
||||
int
|
||||
parse_chan(char **line, char *str, char *opt)
|
||||
parse_chan(char **line, char *str, int *unit)
|
||||
{
|
||||
char *p = *line;
|
||||
|
||||
if (!parse_name(&p, str))
|
||||
return 0;
|
||||
if (*p != '[') {
|
||||
*opt = 0;
|
||||
if (*p < '0' || *p > '9') {
|
||||
*unit = -1;
|
||||
*line = p;
|
||||
return 1;
|
||||
}
|
||||
p++;
|
||||
if (!parse_name(&p, opt))
|
||||
if (!parse_dec(&p, unit))
|
||||
return 0;
|
||||
if (*p != ']') {
|
||||
fprintf(stderr, "']' expected near '%s'\n", p);
|
||||
return 0;
|
||||
}
|
||||
p++;
|
||||
*line = p;
|
||||
return 1;
|
||||
}
|
||||
|
@ -554,9 +525,6 @@ dump(void)
|
|||
printf(".%s", i->desc.func);
|
||||
printf("=");
|
||||
switch (i->desc.type) {
|
||||
case SIOMIX_LABEL:
|
||||
print_chan(&i->desc.chan1, 0);
|
||||
break;
|
||||
case SIOMIX_NUM:
|
||||
case SIOMIX_SW:
|
||||
printf("* (%u)", i->curval);
|
||||
|
@ -579,11 +547,11 @@ cmd(char *line)
|
|||
char *pos = line;
|
||||
struct info *i, *e, *g;
|
||||
char func[SIOMIX_NAMEMAX], astr[SIOMIX_NAMEMAX], vstr[SIOMIX_NAMEMAX];
|
||||
char aopt[SIOMIX_NAMEMAX], vopt[SIOMIX_NAMEMAX];
|
||||
int aunit, vunit;
|
||||
unsigned val, npar = 0, nent = 0;
|
||||
int comma, mode;
|
||||
|
||||
if (!parse_chan(&pos, astr, aopt))
|
||||
if (!parse_chan(&pos, astr, &aunit))
|
||||
return 0;
|
||||
if (*pos != '.') {
|
||||
fprintf(stderr, "'.' expected near '%s'\n", pos);
|
||||
|
@ -621,7 +589,7 @@ cmd(char *line)
|
|||
if (!parse_modeval(&pos, &mode, &val))
|
||||
return 0;
|
||||
for (i = g; i != NULL; i = nextpar(i)) {
|
||||
if (!matchpar(i, astr, aopt))
|
||||
if (!matchpar(i, astr, aunit))
|
||||
continue;
|
||||
i->mode = mode;
|
||||
i->newval = val;
|
||||
|
@ -631,7 +599,7 @@ cmd(char *line)
|
|||
case SIOMIX_VEC:
|
||||
case SIOMIX_LIST:
|
||||
for (i = g; i != NULL; i = nextpar(i)) {
|
||||
if (!matchpar(i, astr, aopt))
|
||||
if (!matchpar(i, astr, aunit))
|
||||
continue;
|
||||
for (e = i; e != NULL; e = nextent(e, 0)) {
|
||||
e->newval = 0;
|
||||
|
@ -648,7 +616,7 @@ cmd(char *line)
|
|||
break;
|
||||
pos++;
|
||||
}
|
||||
if (!parse_chan(&pos, vstr, vopt))
|
||||
if (!parse_chan(&pos, vstr, &vunit))
|
||||
return 0;
|
||||
if (*pos == ':') {
|
||||
pos++;
|
||||
|
@ -660,10 +628,10 @@ cmd(char *line)
|
|||
}
|
||||
nent = 0;
|
||||
for (i = g; i != NULL; i = nextpar(i)) {
|
||||
if (!matchpar(i, astr, aopt))
|
||||
if (!matchpar(i, astr, aunit))
|
||||
continue;
|
||||
for (e = i; e != NULL; e = nextent(e, 0)) {
|
||||
if (matchent(e, vstr, vopt)) {
|
||||
if (matchent(e, vstr, vunit)) {
|
||||
e->newval = val;
|
||||
e->mode = mode;
|
||||
nent++;
|
||||
|
@ -671,8 +639,9 @@ cmd(char *line)
|
|||
}
|
||||
}
|
||||
if (nent == 0) {
|
||||
fprintf(stderr, "%s[%s]: invalid value\n", vstr, vopt);
|
||||
print_par(g, 0);
|
||||
/* XXX: use print_chan()-like routine */
|
||||
fprintf(stderr, "%s[%d]: invalid value\n", vstr, vunit);
|
||||
print_par(g, 0, NULL);
|
||||
exit(1);
|
||||
}
|
||||
comma = 1;
|
||||
|
@ -747,15 +716,15 @@ list(void)
|
|||
if (i_flag) {
|
||||
if (v_flag) {
|
||||
for (p = g; p != NULL; p = nextpar(p))
|
||||
print_par(p, 0);
|
||||
print_par(p, 0, NULL);
|
||||
} else
|
||||
print_par(g, 1);
|
||||
print_par(g, 1, NULL);
|
||||
} else {
|
||||
if (v_flag || !ismono(g)) {
|
||||
for (p = g; p != NULL; p = nextpar(p))
|
||||
print_par(p, 0);
|
||||
print_par(p, 0, NULL);
|
||||
} else
|
||||
print_par(g, 1);
|
||||
print_par(g, 1, NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -774,21 +743,27 @@ ondesc(void *arg, struct siomix_desc *d, int curval)
|
|||
if (d == NULL)
|
||||
return;
|
||||
|
||||
/*
|
||||
* delete control
|
||||
*/
|
||||
for (pi = &infolist; (i = *pi) != NULL; pi = &i->next) {
|
||||
if (d->addr == i->desc.addr) {
|
||||
if (m_flag)
|
||||
print_par(i, 0, "deleted");
|
||||
*pi = i->next;
|
||||
free(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* find the right position to insert the new widget
|
||||
*/
|
||||
for (pi = &infolist; (i = *pi) != NULL; pi = &i->next) {
|
||||
cmp = cmpdesc(d, &i->desc);
|
||||
if (cmp == 0) {
|
||||
/* label is updated */
|
||||
if (i->desc.type == SIOMIX_LABEL) {
|
||||
memcpy(i->desc.chan1.str, d->chan1.str,
|
||||
SIOMIX_NAMEMAX);
|
||||
print_par(i, 0);
|
||||
return;
|
||||
}
|
||||
if (cmp == 0) {
|
||||
fprintf(stderr, "fatal: duplicate mixer knob:\n");
|
||||
print_par(i, 0);
|
||||
print_par(i, 0, "duplicate");
|
||||
exit(1);
|
||||
}
|
||||
if (cmp < 0)
|
||||
|
@ -805,6 +780,8 @@ ondesc(void *arg, struct siomix_desc *d, int curval)
|
|||
i->mode = MODE_IGNORE;
|
||||
i->next = *pi;
|
||||
*pi = i;
|
||||
if (m_flag)
|
||||
print_par(i, 0, "added");
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -815,13 +792,12 @@ onctl(void *arg, unsigned addr, unsigned val)
|
|||
{
|
||||
struct info *i;
|
||||
|
||||
if (v_flag >= 1)
|
||||
fprintf(stderr, "onctl (%d, %d)\n", addr, val);
|
||||
for (i = infolist; i != NULL; i = i->next) {
|
||||
if (i->ctladdr != addr)
|
||||
continue;
|
||||
i->curval = val;
|
||||
print_par(i, 0);
|
||||
if (m_flag)
|
||||
print_par(i, 0, "changed");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -834,7 +810,7 @@ main(int argc, char **argv)
|
|||
struct pollfd *pfds;
|
||||
int nfds, revents;
|
||||
|
||||
while ((c = getopt(argc, argv, "df:imnv")) != -1) {
|
||||
while ((c = getopt(argc, argv, "df:imv")) != -1) {
|
||||
switch (c) {
|
||||
case 'd':
|
||||
d_flag = 1;
|
||||
|
@ -848,11 +824,8 @@ main(int argc, char **argv)
|
|||
case 'm':
|
||||
m_flag = 1;
|
||||
break;
|
||||
case 'n':
|
||||
n_flag = 1;
|
||||
break;
|
||||
case 'v':
|
||||
v_flag = 1;
|
||||
v_flag++;
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "usage: sndioctl "
|
||||
|
@ -883,11 +856,8 @@ main(int argc, char **argv)
|
|||
dump();
|
||||
} else {
|
||||
if (argc == 0) {
|
||||
for (g = infolist; g != NULL; g = nextgrp(g)) {
|
||||
if (g->desc.type == SIOMIX_LABEL && !n_flag)
|
||||
continue;
|
||||
for (g = infolist; g != NULL; g = nextgrp(g))
|
||||
g->mode = MODE_PRINT;
|
||||
}
|
||||
} else {
|
||||
for (i = 0; i < argc; i++) {
|
||||
if (!cmd(argv[i]))
|
||||
|
|
44
sndiod/dev.c
44
sndiod/dev.c
|
@ -1148,9 +1148,6 @@ dev_open(struct dev *d)
|
|||
c = dev_addctl(d, CTL_NUM,
|
||||
d->ctl_addr + CTLADDR_SLOT_LEVEL(i),
|
||||
"prog", unit, "level", NULL, -1, d->slot[i].vol);
|
||||
dev_addctl(d, CTL_LABEL,
|
||||
d->ctl_addr + CTLADDR_SLOT_LABEL(i),
|
||||
"prog", unit, "name", d->slot[i].name, -1, 0);
|
||||
}
|
||||
unit = dev_makeunit(d, "sndiod");
|
||||
c = dev_addctl(d, CTL_NUM,
|
||||
|
@ -2048,9 +2045,6 @@ ctl_log(struct ctl *c)
|
|||
ctl_chan_log(&c->chan1);
|
||||
log_puts(":");
|
||||
log_putu(c->curval);
|
||||
break;
|
||||
case CTL_LABEL:
|
||||
ctl_chan_log(&c->chan1);
|
||||
}
|
||||
log_puts(" at ");
|
||||
log_putu(c->addr);
|
||||
|
@ -2097,9 +2091,7 @@ dev_addctl(struct dev *d, int type, int addr,
|
|||
strlcpy(c->func, func, CTL_NAMEMAX);
|
||||
strlcpy(c->chan0.str, str0, CTL_NAMEMAX);
|
||||
c->chan0.unit = unit0;
|
||||
if (c->type == CTL_LABEL ||
|
||||
c->type == CTL_VEC ||
|
||||
c->type == CTL_LIST) {
|
||||
if (c->type == CTL_VEC || c->type == CTL_LIST) {
|
||||
strlcpy(c->chan1.str, str1, CTL_NAMEMAX);
|
||||
c->chan1.unit = unit1;
|
||||
} else
|
||||
|
@ -2122,6 +2114,32 @@ dev_addctl(struct dev *d, int type, int addr,
|
|||
return c;
|
||||
}
|
||||
|
||||
void
|
||||
dev_rmctl(struct dev *d, int addr)
|
||||
{
|
||||
struct ctl *c, **pc;
|
||||
|
||||
pc = &d->ctl_list;
|
||||
for (;;) {
|
||||
c = *pc;
|
||||
if (c == NULL)
|
||||
return;
|
||||
if (c->addr == addr)
|
||||
break;
|
||||
pc = &c->next;
|
||||
}
|
||||
#ifdef DEBUG
|
||||
if (log_level >= 3) {
|
||||
dev_log(d);
|
||||
log_puts(": removing ");
|
||||
ctl_log(c);
|
||||
log_puts("\n");
|
||||
}
|
||||
#endif
|
||||
*pc = c->next;
|
||||
xfree(c);
|
||||
}
|
||||
|
||||
int
|
||||
dev_setctl(struct dev *d, int addr, int val, unsigned int mask)
|
||||
{
|
||||
|
@ -2187,7 +2205,7 @@ dev_label(struct dev *d, int i)
|
|||
struct ctl *c;
|
||||
int addr;
|
||||
|
||||
addr = d->ctl_addr + CTLADDR_SLOT_LABEL(i);
|
||||
addr = d->ctl_addr + CTLADDR_SLOT_LEVEL(i);
|
||||
c = d->ctl_list;
|
||||
for (;;) {
|
||||
if (c == NULL)
|
||||
|
@ -2196,9 +2214,9 @@ dev_label(struct dev *d, int i)
|
|||
break;
|
||||
c = c->next;
|
||||
}
|
||||
if (strcmp(c->chan1.str, d->slot[i].name) == 0)
|
||||
return;
|
||||
strlcpy(c->chan1.str, d->slot[i].name, CTL_NAMEMAX);
|
||||
if (strcmp(c->chan0.str, d->slot[i].name) == 0 && c->chan0.unit == i)
|
||||
return;
|
||||
strlcpy(c->chan0.str, d->slot[i].name, CTL_NAMEMAX);
|
||||
c->desc_mask = ~0;
|
||||
}
|
||||
|
||||
|
|
|
@ -23,8 +23,7 @@
|
|||
#include "dev_siomix.h"
|
||||
|
||||
#define CTLADDR_SLOT_LEVEL(n) (n)
|
||||
#define CTLADDR_SLOT_LABEL(n) (DEV_NSLOT + n)
|
||||
#define CTLADDR_MASTER (DEV_NSLOT + DEV_NSLOT)
|
||||
#define CTLADDR_MASTER (DEV_NSLOT)
|
||||
|
||||
/*
|
||||
* audio stream state structure
|
||||
|
@ -110,7 +109,6 @@ struct ctl {
|
|||
#define CTL_SW 3 /* on/off switch, only bit 7 counts */
|
||||
#define CTL_VEC 4 /* number, element of vector */
|
||||
#define CTL_LIST 5 /* switch, element of a list */
|
||||
#define CTL_LABEL 6 /* attach string to stream */
|
||||
unsigned int type; /* one of above */
|
||||
unsigned int addr; /* control address */
|
||||
#define CTL_NAMEMAX 16 /* max name lenght */
|
||||
|
@ -290,5 +288,6 @@ int dev_nctl(struct dev *);
|
|||
void dev_label(struct dev *, int);
|
||||
struct ctl *dev_addctl(struct dev *, int, int,
|
||||
char *, int, char *, char *, int, int);
|
||||
void dev_rmctl(struct dev *, int);
|
||||
|
||||
#endif /* !defined(DEV_H) */
|
||||
|
|
|
@ -52,24 +52,14 @@ void
|
|||
dev_siomix_ondesc(void *arg, struct siomix_desc *desc, int val)
|
||||
{
|
||||
struct dev *d = arg;
|
||||
struct ctl *c;
|
||||
|
||||
if (desc == NULL)
|
||||
return;
|
||||
|
||||
for (c = d->ctl_list; c != NULL; c = c->next) {
|
||||
if (c->addr != desc->addr)
|
||||
continue;
|
||||
if (c->type != SIOMIX_LABEL)
|
||||
continue;
|
||||
ctl_log(c);
|
||||
log_puts(": label -> ");
|
||||
log_puts(desc->chan0.str);
|
||||
log_puts("\n");
|
||||
strlcpy(c->chan0.str, desc->chan0.str, CTL_NAMEMAX);
|
||||
c->desc_mask = ~0;
|
||||
return;
|
||||
}
|
||||
/*
|
||||
* XXX: can't we just replace the contents ?
|
||||
*/
|
||||
dev_rmctl(d, desc->addr);
|
||||
dev_addctl(d, desc->type, desc->addr,
|
||||
desc->chan0.str, desc->chan0.unit, desc->func,
|
||||
desc->chan1.str, desc->chan1.unit, val);
|
||||
|
|
Loading…
Reference in New Issue