2014-07-16 05:05:01 -05:00
|
|
|
/*
|
|
|
|
* Copyright (c) 2010-2011 Alexandre Ratchov <alex@caoua.org>
|
|
|
|
*
|
|
|
|
* Permission to use, copy, modify, and distribute this software for any
|
|
|
|
* purpose with or without fee is hereby granted, provided that the above
|
|
|
|
* copyright notice and this permission notice appear in all copies.
|
|
|
|
*
|
|
|
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
|
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
|
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
|
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
|
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
|
|
* 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
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
#ifdef USE_SUN_MIXER
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/ioctl.h>
|
|
|
|
#include <sys/audioio.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <fcntl.h>
|
2015-02-25 01:37:50 -06:00
|
|
|
#include <limits.h>
|
2014-07-16 05:05:01 -05:00
|
|
|
#include <poll.h>
|
|
|
|
#include <sndio.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include "debug.h"
|
|
|
|
#include "siomix_priv.h"
|
|
|
|
|
|
|
|
struct siomix_sun_hdl {
|
|
|
|
struct siomix_hdl siomix;
|
|
|
|
struct mixer_devinfo *info;
|
|
|
|
struct mixer_ctrl *curval;
|
|
|
|
int fd, ninfo, events;
|
|
|
|
int iclass, oclass, rclass;
|
|
|
|
};
|
|
|
|
|
|
|
|
static void siomix_sun_close(struct siomix_hdl *);
|
|
|
|
static int siomix_sun_nfds(struct siomix_hdl *);
|
|
|
|
static int siomix_sun_pollfd(struct siomix_hdl *, struct pollfd *, int);
|
|
|
|
static int siomix_sun_revents(struct siomix_hdl *, struct pollfd *);
|
|
|
|
static int siomix_sun_setctl(struct siomix_hdl *, unsigned int, unsigned int);
|
|
|
|
static int siomix_sun_onctl(struct siomix_hdl *);
|
|
|
|
static int siomix_sun_ondesc(struct siomix_hdl *);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* operations every device should support
|
|
|
|
*/
|
|
|
|
struct siomix_ops siomix_sun_ops = {
|
|
|
|
siomix_sun_close,
|
|
|
|
siomix_sun_nfds,
|
|
|
|
siomix_sun_pollfd,
|
|
|
|
siomix_sun_revents,
|
|
|
|
siomix_sun_setctl,
|
|
|
|
siomix_sun_onctl,
|
|
|
|
siomix_sun_ondesc
|
|
|
|
};
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
if (*str != '/') {
|
|
|
|
DPRINTF("siomix_sun_open: %s: '/<devnum>' expected\n", str);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
str++;
|
|
|
|
hdl = malloc(sizeof(struct siomix_sun_hdl));
|
|
|
|
if (hdl == NULL)
|
|
|
|
return NULL;
|
|
|
|
_siomix_create(&hdl->siomix, &siomix_sun_ops, mode, nbio);
|
|
|
|
snprintf(path, sizeof(path), "/dev/mixer%s", str);
|
|
|
|
if (mode == (SIOMIX_READ | SIOMIX_WRITE))
|
|
|
|
flags = O_RDWR;
|
|
|
|
else
|
|
|
|
flags = (mode & SIOMIX_WRITE) ? O_WRONLY : O_RDONLY;
|
|
|
|
|
|
|
|
while ((fd = open(path, flags)) < 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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
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
|
|
|
|
siomix_sun_close(struct siomix_hdl *addr)
|
|
|
|
{
|
|
|
|
struct siomix_sun_hdl *hdl = (struct siomix_sun_hdl *)addr;
|
|
|
|
|
|
|
|
close(hdl->fd);
|
|
|
|
free(hdl);
|
|
|
|
}
|
|
|
|
|
2015-03-05 03:12:34 -06:00
|
|
|
/*
|
|
|
|
* parse foo-4:5 and convert it to "foo", 4, 2
|
|
|
|
*/
|
2014-07-16 05:05:01 -05:00
|
|
|
static int
|
2015-03-05 03:12:34 -06:00
|
|
|
copy_ch(struct siomix_sun_hdl *hdl,
|
|
|
|
int icls, char *istr, char *ostr, int *rmin, int *rnum)
|
2014-07-16 05:05:01 -05:00
|
|
|
{
|
|
|
|
size_t len;
|
2015-03-05 03:12:34 -06:00
|
|
|
char *sep, *endp;
|
|
|
|
long cmin, cmax;
|
2014-07-16 05:05:01 -05:00
|
|
|
|
|
|
|
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;
|
|
|
|
|
2015-03-05 03:12:34 -06:00
|
|
|
cmin = strtol(istr, &endp, 10);
|
2014-07-16 05:05:01 -05:00
|
|
|
if (endp != istr) {
|
|
|
|
/*
|
|
|
|
* this a "foo-0:3" style range
|
|
|
|
*/
|
|
|
|
istr = endp;
|
|
|
|
if (*endp == ':') {
|
|
|
|
istr++;
|
2015-03-05 03:12:34 -06:00
|
|
|
cmax = strtol(istr, &endp, 10);
|
2014-07-16 05:05:01 -05:00
|
|
|
if (endp == istr) {
|
|
|
|
DPRINTF("bad range\n");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
istr = endp;
|
2015-03-05 03:12:34 -06:00
|
|
|
} else if (*endp == 0) {
|
|
|
|
cmax = cmin;
|
|
|
|
} else {
|
|
|
|
DPRINTF("unknown range\n");
|
|
|
|
return 0;
|
2014-07-16 05:05:01 -05:00
|
|
|
}
|
2015-03-05 03:12:34 -06:00
|
|
|
*rmin = cmin;
|
|
|
|
*rnum = cmax - cmin + 1;
|
2014-07-16 05:05:01 -05:00
|
|
|
} else {
|
2015-03-05 03:12:34 -06:00
|
|
|
if (strcmp(ostr, "line") == 0) {
|
|
|
|
/* rename make "line-foo" to "foo" */
|
2014-07-16 05:05:01 -05:00
|
|
|
ostr[0] = 0;
|
2015-03-05 03:12:34 -06:00
|
|
|
} else {
|
|
|
|
/* append unknown suffix with '_' */
|
2014-07-16 05:05:01 -05:00
|
|
|
strlcat(ostr, "_", SIOMIX_NAMEMAX);
|
2015-03-05 03:12:34 -06:00
|
|
|
}
|
2014-07-16 05:05:01 -05:00
|
|
|
strlcat(ostr, istr, SIOMIX_NAMEMAX);
|
2015-03-05 03:12:34 -06:00
|
|
|
*rmin = 0;
|
|
|
|
*rnum = 0;
|
2014-07-16 05:05:01 -05:00
|
|
|
}
|
|
|
|
} else {
|
2015-03-05 03:12:34 -06:00
|
|
|
*rmin = 0;
|
|
|
|
*rnum = 0;
|
2014-07-16 05:05:01 -05:00
|
|
|
strlcpy(ostr, istr, SIOMIX_NAMEMAX);
|
|
|
|
}
|
2015-03-05 03:12:34 -06:00
|
|
|
if (icls == -1)
|
2014-07-16 05:05:01 -05:00
|
|
|
return 1;
|
2015-03-06 01:53:40 -06:00
|
|
|
/*
|
2015-03-06 03:39:51 -06:00
|
|
|
* append "_in" and "_out" suffixes, as nowadays
|
2015-03-06 01:53:40 -06:00
|
|
|
* most jacks are bidirectional
|
|
|
|
*/
|
2015-03-06 03:39:51 -06:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2015-03-06 01:53:40 -06:00
|
|
|
/*
|
|
|
|
* record class may conflict with input/output
|
|
|
|
*/
|
|
|
|
if (icls == hdl->rclass) {
|
|
|
|
if (strcmp(ostr, "volume") == 0)
|
2014-07-16 05:05:01 -05:00
|
|
|
strlcpy(ostr, "rec", SIOMIX_NAMEMAX);
|
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2015-03-05 03:12:34 -06:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-07-16 05:05:01 -05:00
|
|
|
static int
|
|
|
|
copyname_num(struct siomix_sun_hdl *hdl,
|
2015-03-05 03:12:34 -06:00
|
|
|
struct mixer_devinfo *info, struct siomix_desc *desc, int *rmin, int *rnum)
|
2014-07-16 05:05:01 -05:00
|
|
|
{
|
|
|
|
size_t len;
|
|
|
|
char *sep, *istr;
|
|
|
|
|
|
|
|
sep = strchr(info->label.name, '_');
|
|
|
|
if (sep) {
|
2015-06-03 23:12:38 -05:00
|
|
|
strlcpy(desc->func, "mix", SIOMIX_NAMEMAX);
|
2014-07-16 05:05:01 -05:00
|
|
|
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;
|
2015-03-05 03:12:34 -06:00
|
|
|
if (!copy_ch(hdl, info->mixer_class, istr,
|
|
|
|
desc->chan1.str, rmin, rnum))
|
2014-07-16 05:05:01 -05:00
|
|
|
return 0;
|
|
|
|
} else {
|
2015-06-03 23:12:38 -05:00
|
|
|
strlcpy(desc->func, "level", SIOMIX_NAMEMAX);
|
2014-07-16 05:05:01 -05:00
|
|
|
desc->type = SIOMIX_NUM;
|
|
|
|
istr = info->label.name;
|
2015-03-05 03:12:34 -06:00
|
|
|
if (!copy_ch(hdl, info->mixer_class, istr,
|
|
|
|
desc->chan0.str, rmin, rnum))
|
2014-07-16 05:05:01 -05:00
|
|
|
return 0;
|
|
|
|
desc->chan1.str[0] = '\0';
|
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
copyname_enum(struct siomix_sun_hdl *hdl,
|
2015-03-05 03:12:34 -06:00
|
|
|
struct mixer_devinfo *info, struct siomix_desc *desc, int *rmin, int *rnum)
|
2014-07-16 05:05:01 -05:00
|
|
|
{
|
|
|
|
char istr[SIOMIX_NAMEMAX], *sep;
|
2015-03-05 03:12:34 -06:00
|
|
|
size_t len;
|
2014-07-16 05:05:01 -05:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
2015-06-03 23:12:38 -05:00
|
|
|
strlcpy(desc->func, info->label.name, SIOMIX_NAMEMAX);
|
2014-07-16 05:05:01 -05:00
|
|
|
while (info->prev >= 0)
|
|
|
|
info = hdl->info + info->prev;
|
2015-03-05 03:12:34 -06:00
|
|
|
if (!copy_ch(hdl, info->mixer_class, info->label.name,
|
|
|
|
desc->chan0.str, rmin, rnum))
|
2014-07-16 05:05:01 -05:00
|
|
|
return 0;
|
2015-03-05 03:12:34 -06:00
|
|
|
desc->chan1.str[0] = 0;
|
2014-07-16 05:05:01 -05:00
|
|
|
} else {
|
2015-06-03 23:12:38 -05:00
|
|
|
strlcpy(desc->func, sep + 1, SIOMIX_NAMEMAX);
|
2014-07-16 05:05:01 -05:00
|
|
|
len = sep - info->label.name;
|
|
|
|
if (len >= SIOMIX_NAMEMAX - 1)
|
|
|
|
return 0;
|
|
|
|
memcpy(istr, info->label.name, len);
|
|
|
|
istr[len] = '\0';
|
2015-03-05 03:12:34 -06:00
|
|
|
if (!copy_ch(hdl, info->mixer_class, istr,
|
|
|
|
desc->chan0.str, rmin, rnum))
|
2014-07-16 05:05:01 -05:00
|
|
|
return 0;
|
2015-03-05 03:12:34 -06:00
|
|
|
desc->chan1.str[0] = 0;
|
2014-07-16 05:05:01 -05:00
|
|
|
}
|
|
|
|
/*
|
|
|
|
* certain cards expose adc[0-1].source and adc[2-3].source
|
|
|
|
* as different types, which we forbid.
|
|
|
|
*/
|
2015-06-03 23:12:38 -05:00
|
|
|
if (strcmp(desc->func, "source") == 0) {
|
2014-07-16 05:05:01 -05:00
|
|
|
if (info->type == AUDIO_MIXER_SET)
|
2015-06-03 23:12:38 -05:00
|
|
|
strlcpy(desc->func, "sources", SIOMIX_NAMEMAX);
|
2014-07-16 05:05:01 -05:00
|
|
|
}
|
|
|
|
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)
|
2015-03-05 03:12:34 -06:00
|
|
|
goto make_sw;
|
2014-07-16 05:05:01 -05:00
|
|
|
if (strcmp(v0, "unplugged") == 0 && strcmp(v1, "plugged") == 0) {
|
2015-06-03 23:12:38 -05:00
|
|
|
strlcpy(desc->func,
|
2014-07-16 05:05:01 -05:00
|
|
|
info->un.e.member[1].label.name,
|
|
|
|
SIOMIX_NAMEMAX);
|
2015-03-05 03:12:34 -06:00
|
|
|
goto make_sw;
|
2014-07-16 05:05:01 -05:00
|
|
|
}
|
|
|
|
return 0;
|
2015-03-05 03:12:34 -06:00
|
|
|
make_sw:
|
|
|
|
desc->chan1.str[0] = 0;
|
|
|
|
desc->chan1.opt[0] = 0;
|
|
|
|
desc->type = SIOMIX_SW;
|
|
|
|
return 1;
|
2014-07-16 05:05:01 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
2015-03-05 03:12:34 -06:00
|
|
|
char *ostr;
|
|
|
|
int i, j, v, cmin, cnum;
|
2014-07-16 05:05:01 -05:00
|
|
|
|
|
|
|
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++) {
|
2015-03-05 03:12:34 -06:00
|
|
|
DPRINTF("psarsing \"%s\"\n", info->label.name);
|
2014-07-16 05:05:01 -05:00
|
|
|
switch (info->type) {
|
|
|
|
case AUDIO_MIXER_VALUE:
|
|
|
|
desc.addr = i * 32;
|
2015-03-05 03:12:34 -06:00
|
|
|
if (!copyname_num(hdl, info, &desc, &cmin, &cnum))
|
2014-07-16 05:05:01 -05:00
|
|
|
return 0;
|
2015-03-05 03:12:34 -06:00
|
|
|
make_opt(desc.chan0.opt, cmin, cnum);
|
|
|
|
desc.chan1.opt[0] = 0;
|
2014-07-16 05:05:01 -05:00
|
|
|
for (j = 0; j < info->un.v.num_channels; j++) {
|
|
|
|
v = hdl->curval[i].un.value.level[j] *
|
|
|
|
127 / 255;
|
2015-03-05 03:12:34 -06:00
|
|
|
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);
|
|
|
|
}
|
2014-07-16 05:05:01 -05:00
|
|
|
_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;
|
2015-03-05 03:12:34 -06:00
|
|
|
if (!copyname_enum(hdl, info, &desc, &cmin, &cnum))
|
2014-07-16 05:05:01 -05:00
|
|
|
return 0;
|
|
|
|
if (enum_to_sw(info, &desc)) {
|
2015-03-05 03:12:34 -06:00
|
|
|
make_opt(desc.chan0.opt, cmin, cnum);
|
2014-07-16 05:05:01 -05:00
|
|
|
_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++) {
|
2015-03-05 03:12:34 -06:00
|
|
|
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))
|
2014-07-16 05:05:01 -05:00
|
|
|
return 0;
|
2015-03-05 03:12:34 -06:00
|
|
|
make_opt(desc.chan1.opt, cmin, cnum);
|
2014-07-16 05:05:01 -05:00
|
|
|
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;
|
2015-03-05 03:12:34 -06:00
|
|
|
if (!copyname_enum(hdl, info, &desc, &cmin, &cnum))
|
2014-07-16 05:05:01 -05:00
|
|
|
return 0;
|
2015-03-05 03:12:34 -06:00
|
|
|
make_opt(desc.chan0.opt, cmin, cnum);
|
2014-07-16 05:05:01 -05:00
|
|
|
desc.type = SIOMIX_LIST;
|
|
|
|
for (j = 0; j < info->un.s.num_mem; j++) {
|
2015-03-05 03:12:34 -06:00
|
|
|
if (!copy_ch(hdl, info->mixer_class,
|
2014-07-16 05:05:01 -05:00
|
|
|
info->un.s.member[j].label.name,
|
2015-03-05 03:12:34 -06:00
|
|
|
desc.chan1.str, &cmin, &cnum))
|
2014-07-16 05:05:01 -05:00
|
|
|
return 0;
|
2015-03-05 03:12:34 -06:00
|
|
|
make_opt(desc.chan1.opt, cmin, cnum);
|
2014-07-16 05:05:01 -05:00
|
|
|
_siomix_ondesc_cb(&hdl->siomix, &desc,
|
|
|
|
mask_to_bit(info, j,
|
|
|
|
hdl->curval[i].un.mask));
|
|
|
|
desc.addr++;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
info++;
|
|
|
|
}
|
|
|
|
_siomix_ondesc_cb(&hdl->siomix, NULL, 0);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
siomix_sun_onctl(struct siomix_hdl *addr)
|
|
|
|
{
|
|
|
|
//struct siomix_sun_hdl *hdl = (struct siomix_sun_hdl *)addr;
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
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");
|
|
|
|
hdl->siomix.eof = 1;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
siomix_sun_nfds(struct siomix_hdl *addr)
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
siomix_sun_pollfd(struct siomix_hdl *addr, struct pollfd *pfd, int events)
|
|
|
|
{
|
|
|
|
struct siomix_sun_hdl *hdl = (struct siomix_sun_hdl *)addr;
|
|
|
|
|
|
|
|
hdl->events = events;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
siomix_sun_revents(struct siomix_hdl *addr, struct pollfd *pfd)
|
|
|
|
{
|
|
|
|
struct siomix_sun_hdl *hdl = (struct siomix_sun_hdl *)addr;
|
|
|
|
|
|
|
|
return hdl->events & POLLOUT;
|
|
|
|
}
|
|
|
|
#endif
|