mirror of
https://gitee.com/openharmony/third_party_alsa-lib
synced 2024-11-27 01:31:08 +00:00
more simple mixer - basic abstraction - work
- midlayer cleanups and simplification - probably broke the "none" abstraction code somehow (not intensively tested midlayer changes) - trying to implement ac97 module - far from finished - common code should be moved to alsa-lib as core for other modules - perhaps simple_abst.c can be based on this common code, too
This commit is contained in:
parent
3656a66397
commit
ce67d5389b
@ -99,7 +99,9 @@ snd_mixer_elem_t *snd_mixer_first_elem(snd_mixer_t *mixer);
|
||||
snd_mixer_elem_t *snd_mixer_last_elem(snd_mixer_t *mixer);
|
||||
int snd_mixer_handle_events(snd_mixer_t *mixer);
|
||||
int snd_mixer_attach(snd_mixer_t *mixer, const char *name);
|
||||
int snd_mixer_attach_hctl(snd_mixer_t *mixer, snd_hctl_t *hctl);
|
||||
int snd_mixer_detach(snd_mixer_t *mixer, const char *name);
|
||||
int snd_mixer_detach_hctl(snd_mixer_t *mixer, snd_hctl_t *hctl);
|
||||
int snd_mixer_get_hctl(snd_mixer_t *mixer, const char *name, snd_hctl_t **hctl);
|
||||
int snd_mixer_poll_descriptors_count(snd_mixer_t *mixer);
|
||||
int snd_mixer_poll_descriptors(snd_mixer_t *mixer, struct pollfd *pfds, unsigned int space);
|
||||
@ -266,18 +268,18 @@ int snd_mixer_selem_set_playback_switch(snd_mixer_elem_t *elem, snd_mixer_selem_
|
||||
int snd_mixer_selem_set_capture_switch(snd_mixer_elem_t *elem, snd_mixer_selem_channel_id_t channel, int value);
|
||||
int snd_mixer_selem_set_playback_switch_all(snd_mixer_elem_t *elem, int value);
|
||||
int snd_mixer_selem_set_capture_switch_all(snd_mixer_elem_t *elem, int value);
|
||||
void snd_mixer_selem_get_playback_volume_range(snd_mixer_elem_t *elem,
|
||||
long *min, long *max);
|
||||
void snd_mixer_selem_get_playback_dB_range(snd_mixer_elem_t *elem,
|
||||
long *min, long *max);
|
||||
void snd_mixer_selem_set_playback_volume_range(snd_mixer_elem_t *elem,
|
||||
long min, long max);
|
||||
void snd_mixer_selem_get_capture_volume_range(snd_mixer_elem_t *elem,
|
||||
int snd_mixer_selem_get_playback_volume_range(snd_mixer_elem_t *elem,
|
||||
long *min, long *max);
|
||||
void snd_mixer_selem_get_capture_dB_range(snd_mixer_elem_t *elem,
|
||||
int snd_mixer_selem_get_playback_dB_range(snd_mixer_elem_t *elem,
|
||||
long *min, long *max);
|
||||
void snd_mixer_selem_set_capture_volume_range(snd_mixer_elem_t *elem,
|
||||
int snd_mixer_selem_set_playback_volume_range(snd_mixer_elem_t *elem,
|
||||
long min, long max);
|
||||
int snd_mixer_selem_get_capture_volume_range(snd_mixer_elem_t *elem,
|
||||
long *min, long *max);
|
||||
int snd_mixer_selem_get_capture_dB_range(snd_mixer_elem_t *elem,
|
||||
long *min, long *max);
|
||||
int snd_mixer_selem_set_capture_volume_range(snd_mixer_elem_t *elem,
|
||||
long min, long max);
|
||||
|
||||
int snd_mixer_selem_is_enumerated(snd_mixer_elem_t *elem);
|
||||
int snd_mixer_selem_get_enum_items(snd_mixer_elem_t *elem);
|
||||
|
@ -69,6 +69,13 @@ typedef struct _sm_selem {
|
||||
unsigned int capture_group;
|
||||
} sm_selem_t;
|
||||
|
||||
typedef struct _sm_class_basic {
|
||||
char *device;
|
||||
snd_ctl_t *ctl;
|
||||
snd_hctl_t *hctl;
|
||||
snd_ctl_card_info_t *info;
|
||||
} sm_class_basic_t;
|
||||
|
||||
struct sm_elem_ops {
|
||||
int (*is)(snd_mixer_elem_t *elem, int dir, int cmd, int val);
|
||||
int (*get_range)(snd_mixer_elem_t *elem, int dir, long *min, long *max);
|
||||
@ -78,11 +85,8 @@ struct sm_elem_ops {
|
||||
int (*get_dB)(snd_mixer_elem_t *elem, int dir, snd_mixer_selem_channel_id_t channel, long *value);
|
||||
int (*set_volume)(snd_mixer_elem_t *elem, int dir, snd_mixer_selem_channel_id_t channel, long value);
|
||||
int (*set_dB)(snd_mixer_elem_t *elem, int dir, snd_mixer_selem_channel_id_t channel, long value, int xdir);
|
||||
int (*set_volume_all)(snd_mixer_elem_t *elem, int dir, long value);
|
||||
int (*set_dB_all)(snd_mixer_elem_t *elem, int dir, long value, int xdir);
|
||||
int (*get_switch)(snd_mixer_elem_t *elem, int dir, snd_mixer_selem_channel_id_t channel, int *value);
|
||||
int (*set_switch)(snd_mixer_elem_t *elem, int dir, snd_mixer_selem_channel_id_t channel, int value);
|
||||
int (*set_switch_all)(snd_mixer_elem_t *elem, int dir, int value);
|
||||
int (*enum_item_name)(snd_mixer_elem_t *elem, unsigned int item, size_t maxlen, char *buf);
|
||||
int (*get_enum_item)(snd_mixer_elem_t *elem, snd_mixer_selem_channel_id_t channel, unsigned int *itemp);
|
||||
int (*set_enum_item)(snd_mixer_elem_t *elem, snd_mixer_selem_channel_id_t channel, unsigned int item);
|
||||
@ -90,6 +94,8 @@ struct sm_elem_ops {
|
||||
|
||||
int snd_mixer_selem_compare(const snd_mixer_elem_t *c1, const snd_mixer_elem_t *c2);
|
||||
|
||||
int snd_mixer_sbasic_info(const snd_mixer_class_t *class, sm_class_basic_t *info);
|
||||
|
||||
/** \} */
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
@ -224,6 +224,8 @@ ALSA_1.0.10 {
|
||||
|
||||
snd_mixer_get_hctl;
|
||||
snd_mixer_elem_get_private;
|
||||
snd_mixer_attach_hctl;
|
||||
snd_mixer_detach_hctl;
|
||||
|
||||
snd_mixer_class_register;
|
||||
snd_mixer_add_elem;
|
||||
@ -253,6 +255,7 @@ ALSA_1.0.10 {
|
||||
snd_mixer_selem_set_playback_dB_all;
|
||||
snd_mixer_selem_set_capture_dB_all;
|
||||
snd_mixer_selem_compare;
|
||||
snd_mixer_sbasic_info;
|
||||
|
||||
snd_ctl_ext_create;
|
||||
snd_ctl_ext_delete;
|
||||
|
@ -190,24 +190,42 @@ static int hctl_event_handler(snd_hctl_t *hctl, unsigned int mask,
|
||||
|
||||
|
||||
/**
|
||||
* \brief Attach an HCTL to an opened mixer
|
||||
* \brief Attach an HCTL specified with the CTL device name to an opened mixer
|
||||
* \param mixer Mixer handle
|
||||
* \param name HCTL name (see #snd_hctl_open)
|
||||
* \return 0 on success otherwise a negative error code
|
||||
*/
|
||||
int snd_mixer_attach(snd_mixer_t *mixer, const char *name)
|
||||
{
|
||||
snd_mixer_slave_t *slave;
|
||||
snd_hctl_t *hctl;
|
||||
int err;
|
||||
|
||||
err = snd_hctl_open(&hctl, name, 0);
|
||||
if (err < 0)
|
||||
return err;
|
||||
err = snd_mixer_attach_hctl(mixer, hctl);
|
||||
if (err < 0) {
|
||||
snd_hctl_close(hctl);
|
||||
return err;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Attach an HCTL to an opened mixer
|
||||
* \param mixer Mixer handle
|
||||
* \param name HCTL name (see #snd_hctl_open)
|
||||
* \return 0 on success otherwise a negative error code
|
||||
*/
|
||||
int snd_mixer_attach_hctl(snd_mixer_t *mixer, snd_hctl_t *hctl)
|
||||
{
|
||||
snd_mixer_slave_t *slave;
|
||||
int err;
|
||||
|
||||
assert(hctl);
|
||||
slave = calloc(1, sizeof(*slave));
|
||||
if (slave == NULL)
|
||||
return -ENOMEM;
|
||||
err = snd_hctl_open(&hctl, name, 0);
|
||||
if (err < 0) {
|
||||
free(slave);
|
||||
return err;
|
||||
}
|
||||
err = snd_hctl_nonblock(hctl, 1);
|
||||
if (err < 0) {
|
||||
snd_hctl_close(hctl);
|
||||
@ -243,6 +261,29 @@ int snd_mixer_detach(snd_mixer_t *mixer, const char *name)
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Detach a previously attached HCTL to an opened mixer freeing all related resources
|
||||
* \param mixer Mixer handle
|
||||
* \param hctl HCTL previously attached
|
||||
* \return 0 on success otherwise a negative error code
|
||||
*
|
||||
* Note: The hctl handle is not closed!
|
||||
*/
|
||||
int snd_mixer_detach_hctl(snd_mixer_t *mixer, snd_hctl_t *hctl)
|
||||
{
|
||||
struct list_head *pos;
|
||||
list_for_each(pos, &mixer->slaves) {
|
||||
snd_mixer_slave_t *s;
|
||||
s = list_entry(pos, snd_mixer_slave_t, list);
|
||||
if (hctl == s->hctl) {
|
||||
list_del(pos);
|
||||
free(s);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Obtain a HCTL pointer associated to given name
|
||||
* \param mixer Mixer handle
|
||||
|
@ -63,7 +63,15 @@ int snd_mixer_selem_register(snd_mixer_t *mixer,
|
||||
}
|
||||
if (options == NULL ||
|
||||
(options->ver == 1 && options->abstract == SND_MIXER_SABSTRACT_NONE)) {
|
||||
return snd_mixer_simple_none_register(mixer, options, classp);
|
||||
int err = snd_mixer_simple_none_register(mixer, options, classp);
|
||||
if (err < 0)
|
||||
return err;
|
||||
if (options != NULL) {
|
||||
err = snd_mixer_attach(mixer, options->device);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
return 0;
|
||||
} else if (options->ver == 1) {
|
||||
if (options->abstract == SND_MIXER_SABSTRACT_BASIC)
|
||||
return snd_mixer_simple_basic_register(mixer, options, classp);
|
||||
@ -71,6 +79,35 @@ int snd_mixer_selem_register(snd_mixer_t *mixer,
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
#define CHECK_BASIC(xelem) \
|
||||
{ \
|
||||
assert(xelem); \
|
||||
assert((xelem)->type == SND_MIXER_ELEM_SIMPLE); \
|
||||
}
|
||||
|
||||
#define CHECK_DIR(xelem, xwhat) \
|
||||
{ \
|
||||
unsigned int xcaps = ((sm_selem_t *)(elem)->private_data)->caps; \
|
||||
if (! (xcaps & (xwhat))) \
|
||||
return -EINVAL; \
|
||||
}
|
||||
|
||||
#define CHECK_DIR_CHN(xelem, xwhat, xjoin, xchannel) \
|
||||
{ \
|
||||
unsigned int xcaps = ((sm_selem_t *)(elem)->private_data)->caps; \
|
||||
if (! (xcaps & (xwhat))) \
|
||||
return -EINVAL; \
|
||||
if (xcaps & (xjoin)) \
|
||||
xchannel = 0; \
|
||||
}
|
||||
|
||||
#define CHECK_ENUM(xelem) \
|
||||
if (!((sm_selem_t *)(elem)->private_data)->caps & SM_CAP_ENUM) \
|
||||
return -EINVAL;
|
||||
|
||||
#define COND_CAPS(xelem, what) \
|
||||
!!(((sm_selem_t *)(elem)->private_data)->caps & (what))
|
||||
|
||||
#ifndef DOC_HIDDEN
|
||||
int snd_mixer_selem_compare(const snd_mixer_elem_t *c1, const snd_mixer_elem_t *c2)
|
||||
{
|
||||
@ -116,8 +153,8 @@ void snd_mixer_selem_get_id(snd_mixer_elem_t *elem,
|
||||
snd_mixer_selem_id_t *id)
|
||||
{
|
||||
sm_selem_t *s;
|
||||
assert(elem && id);
|
||||
assert(elem->type == SND_MIXER_ELEM_SIMPLE);
|
||||
assert(id);
|
||||
CHECK_BASIC(elem);
|
||||
s = elem->private_data;
|
||||
*id = *s->id;
|
||||
}
|
||||
@ -130,8 +167,7 @@ void snd_mixer_selem_get_id(snd_mixer_elem_t *elem,
|
||||
const char *snd_mixer_selem_get_name(snd_mixer_elem_t *elem)
|
||||
{
|
||||
sm_selem_t *s;
|
||||
assert(elem);
|
||||
assert(elem->type == SND_MIXER_ELEM_SIMPLE);
|
||||
CHECK_BASIC(elem);
|
||||
s = elem->private_data;
|
||||
return s->id->name;
|
||||
}
|
||||
@ -144,8 +180,7 @@ const char *snd_mixer_selem_get_name(snd_mixer_elem_t *elem)
|
||||
unsigned int snd_mixer_selem_get_index(snd_mixer_elem_t *elem)
|
||||
{
|
||||
sm_selem_t *s;
|
||||
assert(elem);
|
||||
assert(elem->type == SND_MIXER_ELEM_SIMPLE);
|
||||
CHECK_BASIC(elem);
|
||||
s = elem->private_data;
|
||||
return s->id->index;
|
||||
}
|
||||
@ -157,11 +192,8 @@ unsigned int snd_mixer_selem_get_index(snd_mixer_elem_t *elem)
|
||||
*/
|
||||
int snd_mixer_selem_has_common_volume(snd_mixer_elem_t *elem)
|
||||
{
|
||||
sm_selem_t *s;
|
||||
assert(elem);
|
||||
assert(elem->type == SND_MIXER_ELEM_SIMPLE);
|
||||
s = elem->private_data;
|
||||
return !!(s->caps & SM_CAP_GVOLUME);
|
||||
CHECK_BASIC(elem);
|
||||
return COND_CAPS(elem, SM_CAP_GVOLUME);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -171,11 +203,8 @@ int snd_mixer_selem_has_common_volume(snd_mixer_elem_t *elem)
|
||||
*/
|
||||
int snd_mixer_selem_has_common_switch(snd_mixer_elem_t *elem)
|
||||
{
|
||||
sm_selem_t *s;
|
||||
assert(elem);
|
||||
assert(elem->type == SND_MIXER_ELEM_SIMPLE);
|
||||
s = elem->private_data;
|
||||
return !!(s->caps & SM_CAP_GSWITCH);
|
||||
CHECK_BASIC(elem);
|
||||
return COND_CAPS(elem, SM_CAP_GSWITCH);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -211,8 +240,7 @@ const char *snd_mixer_selem_channel_name(snd_mixer_selem_channel_id_t channel)
|
||||
*/
|
||||
int snd_mixer_selem_is_active(snd_mixer_elem_t *elem)
|
||||
{
|
||||
assert(elem);
|
||||
assert(elem->type == SND_MIXER_ELEM_SIMPLE);
|
||||
CHECK_BASIC(elem);
|
||||
return sm_selem_ops(elem)->is(elem, SM_PLAY, SM_OPS_IS_ACTIVE, 0);
|
||||
}
|
||||
|
||||
@ -223,8 +251,7 @@ int snd_mixer_selem_is_active(snd_mixer_elem_t *elem)
|
||||
*/
|
||||
int snd_mixer_selem_is_playback_mono(snd_mixer_elem_t *elem)
|
||||
{
|
||||
assert(elem);
|
||||
assert(elem->type == SND_MIXER_ELEM_SIMPLE);
|
||||
CHECK_BASIC(elem);
|
||||
return sm_selem_ops(elem)->is(elem, SM_PLAY, SM_OPS_IS_MONO, 0);
|
||||
}
|
||||
|
||||
@ -236,8 +263,7 @@ int snd_mixer_selem_is_playback_mono(snd_mixer_elem_t *elem)
|
||||
*/
|
||||
int snd_mixer_selem_has_playback_channel(snd_mixer_elem_t *elem, snd_mixer_selem_channel_id_t channel)
|
||||
{
|
||||
assert(elem);
|
||||
assert(elem->type == SND_MIXER_ELEM_SIMPLE);
|
||||
CHECK_BASIC(elem);
|
||||
return sm_selem_ops(elem)->is(elem, SM_PLAY, SM_OPS_IS_CHANNEL, (int)channel);
|
||||
}
|
||||
|
||||
@ -247,12 +273,12 @@ int snd_mixer_selem_has_playback_channel(snd_mixer_elem_t *elem, snd_mixer_selem
|
||||
* \param min Pointer to returned minimum
|
||||
* \param max Pointer to returned maximum
|
||||
*/
|
||||
void snd_mixer_selem_get_playback_volume_range(snd_mixer_elem_t *elem,
|
||||
int snd_mixer_selem_get_playback_volume_range(snd_mixer_elem_t *elem,
|
||||
long *min, long *max)
|
||||
{
|
||||
assert(elem);
|
||||
assert(elem->type == SND_MIXER_ELEM_SIMPLE);
|
||||
sm_selem_ops(elem)->get_range(elem, SM_PLAY, min, max);
|
||||
CHECK_BASIC(elem);
|
||||
CHECK_DIR(elem, SM_CAP_PVOLUME);
|
||||
return sm_selem_ops(elem)->get_range(elem, SM_PLAY, min, max);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -261,12 +287,12 @@ void snd_mixer_selem_get_playback_volume_range(snd_mixer_elem_t *elem,
|
||||
* \param min Pointer to returned minimum (dB * 100)
|
||||
* \param max Pointer to returned maximum (dB * 100)
|
||||
*/
|
||||
void snd_mixer_selem_get_playback_dB_range(snd_mixer_elem_t *elem,
|
||||
long *min, long *max)
|
||||
int snd_mixer_selem_get_playback_dB_range(snd_mixer_elem_t *elem,
|
||||
long *min, long *max)
|
||||
{
|
||||
assert(elem);
|
||||
assert(elem->type == SND_MIXER_ELEM_SIMPLE);
|
||||
sm_selem_ops(elem)->get_dB_range(elem, SM_PLAY, min, max);
|
||||
CHECK_BASIC(elem);
|
||||
CHECK_DIR(elem, SM_CAP_PVOLUME);
|
||||
return sm_selem_ops(elem)->get_dB_range(elem, SM_PLAY, min, max);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -275,13 +301,13 @@ void snd_mixer_selem_get_playback_dB_range(snd_mixer_elem_t *elem,
|
||||
* \param min minimum volume value
|
||||
* \param max maximum volume value
|
||||
*/
|
||||
void snd_mixer_selem_set_playback_volume_range(snd_mixer_elem_t *elem,
|
||||
long min, long max)
|
||||
int snd_mixer_selem_set_playback_volume_range(snd_mixer_elem_t *elem,
|
||||
long min, long max)
|
||||
{
|
||||
assert(elem);
|
||||
assert(elem->type == SND_MIXER_ELEM_SIMPLE);
|
||||
CHECK_BASIC(elem);
|
||||
assert(min < max);
|
||||
sm_selem_ops(elem)->set_range(elem, SM_PLAY, min, max);
|
||||
CHECK_DIR(elem, SM_CAP_PVOLUME);
|
||||
return sm_selem_ops(elem)->set_range(elem, SM_PLAY, min, max);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -291,11 +317,8 @@ void snd_mixer_selem_set_playback_volume_range(snd_mixer_elem_t *elem,
|
||||
*/
|
||||
int snd_mixer_selem_has_playback_volume(snd_mixer_elem_t *elem)
|
||||
{
|
||||
sm_selem_t *s;
|
||||
assert(elem);
|
||||
assert(elem->type == SND_MIXER_ELEM_SIMPLE);
|
||||
s = elem->private_data;
|
||||
return !!(s->caps & SM_CAP_PVOLUME) || !!(s->caps & SM_CAP_GVOLUME);
|
||||
CHECK_BASIC(elem);
|
||||
return COND_CAPS(elem, SM_CAP_PVOLUME);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -305,11 +328,8 @@ int snd_mixer_selem_has_playback_volume(snd_mixer_elem_t *elem)
|
||||
*/
|
||||
int snd_mixer_selem_has_playback_volume_joined(snd_mixer_elem_t *elem)
|
||||
{
|
||||
sm_selem_t *s;
|
||||
assert(elem);
|
||||
assert(elem->type == SND_MIXER_ELEM_SIMPLE);
|
||||
s = elem->private_data;
|
||||
return !!(s->caps & SM_CAP_PVOLUME_JOIN);
|
||||
CHECK_BASIC(elem);
|
||||
return COND_CAPS(elem, SM_CAP_PVOLUME_JOIN);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -319,11 +339,8 @@ int snd_mixer_selem_has_playback_volume_joined(snd_mixer_elem_t *elem)
|
||||
*/
|
||||
int snd_mixer_selem_has_playback_switch(snd_mixer_elem_t *elem)
|
||||
{
|
||||
sm_selem_t *s;
|
||||
assert(elem);
|
||||
assert(elem->type == SND_MIXER_ELEM_SIMPLE);
|
||||
s = elem->private_data;
|
||||
return !!(s->caps & SM_CAP_PSWITCH) || !!(s->caps & SM_CAP_GSWITCH);
|
||||
CHECK_BASIC(elem);
|
||||
return COND_CAPS(elem, SM_CAP_PSWITCH);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -333,11 +350,8 @@ int snd_mixer_selem_has_playback_switch(snd_mixer_elem_t *elem)
|
||||
*/
|
||||
int snd_mixer_selem_has_playback_switch_joined(snd_mixer_elem_t *elem)
|
||||
{
|
||||
sm_selem_t *s;
|
||||
assert(elem);
|
||||
assert(elem->type == SND_MIXER_ELEM_SIMPLE);
|
||||
s = elem->private_data;
|
||||
return !!(s->caps & SM_CAP_PSWITCH_JOIN);
|
||||
CHECK_BASIC(elem);
|
||||
return COND_CAPS(elem, SM_CAP_PSWITCH_JOIN);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -349,8 +363,8 @@ int snd_mixer_selem_has_playback_switch_joined(snd_mixer_elem_t *elem)
|
||||
*/
|
||||
int snd_mixer_selem_get_playback_volume(snd_mixer_elem_t *elem, snd_mixer_selem_channel_id_t channel, long *value)
|
||||
{
|
||||
assert(elem);
|
||||
assert(elem->type == SND_MIXER_ELEM_SIMPLE);
|
||||
CHECK_BASIC(elem);
|
||||
CHECK_DIR_CHN(elem, SM_CAP_PVOLUME, SM_CAP_PVOLUME_JOIN, channel);
|
||||
return sm_selem_ops(elem)->get_volume(elem, SM_PLAY, channel, value);
|
||||
}
|
||||
|
||||
@ -363,8 +377,14 @@ int snd_mixer_selem_get_playback_volume(snd_mixer_elem_t *elem, snd_mixer_selem_
|
||||
*/
|
||||
int snd_mixer_selem_get_playback_dB(snd_mixer_elem_t *elem, snd_mixer_selem_channel_id_t channel, long *value)
|
||||
{
|
||||
assert(elem);
|
||||
assert(elem->type == SND_MIXER_ELEM_SIMPLE);
|
||||
unsigned int caps;
|
||||
|
||||
CHECK_BASIC(elem);
|
||||
caps = ((sm_selem_t *)elem->private_data)->caps;
|
||||
if (!(caps & SM_CAP_PVOLUME))
|
||||
return -EINVAL;
|
||||
if (caps & SM_CAP_PVOLUME_JOIN)
|
||||
channel = 0;
|
||||
return sm_selem_ops(elem)->get_dB(elem, SM_PLAY, channel, value);
|
||||
}
|
||||
|
||||
@ -377,8 +397,8 @@ int snd_mixer_selem_get_playback_dB(snd_mixer_elem_t *elem, snd_mixer_selem_chan
|
||||
*/
|
||||
int snd_mixer_selem_get_playback_switch(snd_mixer_elem_t *elem, snd_mixer_selem_channel_id_t channel, int *value)
|
||||
{
|
||||
assert(elem);
|
||||
assert(elem->type == SND_MIXER_ELEM_SIMPLE);
|
||||
CHECK_BASIC(elem);
|
||||
CHECK_DIR_CHN(elem, SM_CAP_PSWITCH, SM_CAP_PSWITCH_JOIN, channel);
|
||||
return sm_selem_ops(elem)->get_switch(elem, SM_PLAY, channel, value);
|
||||
}
|
||||
|
||||
@ -391,8 +411,8 @@ int snd_mixer_selem_get_playback_switch(snd_mixer_elem_t *elem, snd_mixer_selem_
|
||||
*/
|
||||
int snd_mixer_selem_set_playback_volume(snd_mixer_elem_t *elem, snd_mixer_selem_channel_id_t channel, long value)
|
||||
{
|
||||
assert(elem);
|
||||
assert(elem->type == SND_MIXER_ELEM_SIMPLE);
|
||||
CHECK_BASIC(elem);
|
||||
CHECK_DIR_CHN(elem, SM_CAP_PVOLUME, SM_CAP_PVOLUME_JOIN, channel);
|
||||
return sm_selem_ops(elem)->set_volume(elem, SM_PLAY, channel, value);
|
||||
}
|
||||
|
||||
@ -401,12 +421,13 @@ int snd_mixer_selem_set_playback_volume(snd_mixer_elem_t *elem, snd_mixer_selem_
|
||||
* \param elem Mixer simple element handle
|
||||
* \param channel mixer simple element channel identifier
|
||||
* \param value control value in dB * 100
|
||||
* \param dir select direction (-1 = accurate or first bellow, 0 = accurate, 1 = accurate or first above)
|
||||
* \return 0 on success otherwise a negative error code
|
||||
*/
|
||||
int snd_mixer_selem_set_playback_dB(snd_mixer_elem_t *elem, snd_mixer_selem_channel_id_t channel, long value, int dir)
|
||||
{
|
||||
assert(elem);
|
||||
assert(elem->type == SND_MIXER_ELEM_SIMPLE);
|
||||
CHECK_BASIC(elem);
|
||||
CHECK_DIR_CHN(elem, SM_CAP_PVOLUME, SM_CAP_PVOLUME_JOIN, channel);
|
||||
return sm_selem_ops(elem)->set_dB(elem, SM_PLAY, channel, value, dir);
|
||||
}
|
||||
|
||||
@ -418,22 +439,43 @@ int snd_mixer_selem_set_playback_dB(snd_mixer_elem_t *elem, snd_mixer_selem_chan
|
||||
*/
|
||||
int snd_mixer_selem_set_playback_volume_all(snd_mixer_elem_t *elem, long value)
|
||||
{
|
||||
assert(elem);
|
||||
assert(elem->type == SND_MIXER_ELEM_SIMPLE);
|
||||
return sm_selem_ops(elem)->set_volume_all(elem, SM_PLAY, value);
|
||||
snd_mixer_selem_channel_id_t chn;
|
||||
int err;
|
||||
|
||||
for (chn = 0; chn < 32; chn++) {
|
||||
if (!snd_mixer_selem_has_playback_channel(elem, chn))
|
||||
continue;
|
||||
err = snd_mixer_selem_set_playback_volume(elem, chn, value);
|
||||
if (err < 0)
|
||||
return err;
|
||||
if (chn == 0 && snd_mixer_selem_has_playback_volume_joined(elem))
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Set value in dB of playback volume control for all channels of a mixer simple element
|
||||
* \param elem Mixer simple element handle
|
||||
* \param value control value in dB * 100
|
||||
* \param dir select direction (-1 = accurate or first bellow, 0 = accurate, 1 = accurate or first above)
|
||||
* \return 0 on success otherwise a negative error code
|
||||
*/
|
||||
int snd_mixer_selem_set_playback_dB_all(snd_mixer_elem_t *elem, long value, int dir)
|
||||
{
|
||||
assert(elem);
|
||||
assert(elem->type == SND_MIXER_ELEM_SIMPLE);
|
||||
return sm_selem_ops(elem)->set_dB_all(elem, SM_PLAY, value, dir);
|
||||
snd_mixer_selem_channel_id_t chn;
|
||||
int err;
|
||||
|
||||
for (chn = 0; chn < 32; chn++) {
|
||||
if (!snd_mixer_selem_has_playback_channel(elem, chn))
|
||||
continue;
|
||||
err = snd_mixer_selem_set_playback_dB(elem, chn, value, dir);
|
||||
if (err < 0)
|
||||
return err;
|
||||
if (chn == 0 && snd_mixer_selem_has_playback_volume_joined(elem))
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -445,8 +487,8 @@ int snd_mixer_selem_set_playback_dB_all(snd_mixer_elem_t *elem, long value, int
|
||||
*/
|
||||
int snd_mixer_selem_set_playback_switch(snd_mixer_elem_t *elem, snd_mixer_selem_channel_id_t channel, int value)
|
||||
{
|
||||
assert(elem);
|
||||
assert(elem->type == SND_MIXER_ELEM_SIMPLE);
|
||||
CHECK_BASIC(elem);
|
||||
CHECK_DIR_CHN(elem, SM_CAP_PSWITCH, SM_CAP_PSWITCH, channel);
|
||||
return sm_selem_ops(elem)->set_switch(elem, SM_PLAY, channel, value);
|
||||
}
|
||||
|
||||
@ -458,9 +500,20 @@ int snd_mixer_selem_set_playback_switch(snd_mixer_elem_t *elem, snd_mixer_selem_
|
||||
*/
|
||||
int snd_mixer_selem_set_playback_switch_all(snd_mixer_elem_t *elem, int value)
|
||||
{
|
||||
assert(elem);
|
||||
assert(elem->type == SND_MIXER_ELEM_SIMPLE);
|
||||
return sm_selem_ops(elem)->set_switch_all(elem, SM_PLAY, value);
|
||||
snd_mixer_selem_channel_id_t chn;
|
||||
int err;
|
||||
|
||||
CHECK_BASIC(elem);
|
||||
for (chn = 0; chn < 32; chn++) {
|
||||
if (!snd_mixer_selem_has_playback_channel(elem, chn))
|
||||
continue;
|
||||
err = snd_mixer_selem_set_playback_switch(elem, chn, value);
|
||||
if (err < 0)
|
||||
return err;
|
||||
if (chn == 0 && snd_mixer_selem_has_playback_switch_joined(elem))
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -470,8 +523,8 @@ int snd_mixer_selem_set_playback_switch_all(snd_mixer_elem_t *elem, int value)
|
||||
*/
|
||||
int snd_mixer_selem_is_capture_mono(snd_mixer_elem_t *elem)
|
||||
{
|
||||
assert(elem);
|
||||
assert(elem->type == SND_MIXER_ELEM_SIMPLE);
|
||||
CHECK_BASIC(elem);
|
||||
CHECK_DIR(elem, SM_CAP_CVOLUME|SM_CAP_CSWITCH);
|
||||
return sm_selem_ops(elem)->is(elem, SM_CAPT, SM_OPS_IS_MONO, 0);
|
||||
}
|
||||
|
||||
@ -483,8 +536,8 @@ int snd_mixer_selem_is_capture_mono(snd_mixer_elem_t *elem)
|
||||
*/
|
||||
int snd_mixer_selem_has_capture_channel(snd_mixer_elem_t *elem, snd_mixer_selem_channel_id_t channel)
|
||||
{
|
||||
assert(elem);
|
||||
assert(elem->type == SND_MIXER_ELEM_SIMPLE);
|
||||
CHECK_BASIC(elem);
|
||||
CHECK_DIR(elem, SM_CAP_CVOLUME|SM_CAP_CSWITCH);
|
||||
return sm_selem_ops(elem)->is(elem, SM_CAPT, SM_OPS_IS_CHANNEL, channel);
|
||||
}
|
||||
|
||||
@ -494,12 +547,12 @@ int snd_mixer_selem_has_capture_channel(snd_mixer_elem_t *elem, snd_mixer_selem_
|
||||
* \param min Pointer to returned minimum
|
||||
* \param max Pointer to returned maximum
|
||||
*/
|
||||
void snd_mixer_selem_get_capture_volume_range(snd_mixer_elem_t *elem,
|
||||
long *min, long *max)
|
||||
int snd_mixer_selem_get_capture_volume_range(snd_mixer_elem_t *elem,
|
||||
long *min, long *max)
|
||||
{
|
||||
assert(elem);
|
||||
assert(elem->type == SND_MIXER_ELEM_SIMPLE);
|
||||
sm_selem_ops(elem)->get_range(elem, SM_CAPT, min, max);
|
||||
CHECK_BASIC(elem);
|
||||
CHECK_DIR(elem, SM_CAP_CVOLUME);
|
||||
return sm_selem_ops(elem)->get_range(elem, SM_CAPT, min, max);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -508,12 +561,12 @@ void snd_mixer_selem_get_capture_volume_range(snd_mixer_elem_t *elem,
|
||||
* \param min Pointer to returned minimum (dB * 100)
|
||||
* \param max Pointer to returned maximum (dB * 100)
|
||||
*/
|
||||
void snd_mixer_selem_get_capture_dB_range(snd_mixer_elem_t *elem,
|
||||
long *min, long *max)
|
||||
int snd_mixer_selem_get_capture_dB_range(snd_mixer_elem_t *elem,
|
||||
long *min, long *max)
|
||||
{
|
||||
assert(elem);
|
||||
assert(elem->type == SND_MIXER_ELEM_SIMPLE);
|
||||
sm_selem_ops(elem)->get_dB_range(elem, SM_CAPT, min, max);
|
||||
CHECK_BASIC(elem);
|
||||
CHECK_DIR(elem, SM_CAP_CVOLUME);
|
||||
return sm_selem_ops(elem)->get_dB_range(elem, SM_CAPT, min, max);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -522,13 +575,13 @@ void snd_mixer_selem_get_capture_dB_range(snd_mixer_elem_t *elem,
|
||||
* \param min minimum volume value
|
||||
* \param max maximum volume value
|
||||
*/
|
||||
void snd_mixer_selem_set_capture_volume_range(snd_mixer_elem_t *elem,
|
||||
long min, long max)
|
||||
int snd_mixer_selem_set_capture_volume_range(snd_mixer_elem_t *elem,
|
||||
long min, long max)
|
||||
{
|
||||
assert(elem);
|
||||
assert(elem->type == SND_MIXER_ELEM_SIMPLE);
|
||||
CHECK_BASIC(elem);
|
||||
assert(min < max);
|
||||
sm_selem_ops(elem)->set_range(elem, SM_CAPT, min, max);
|
||||
CHECK_DIR(elem, SM_CAP_CVOLUME);
|
||||
return sm_selem_ops(elem)->set_range(elem, SM_CAPT, min, max);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -538,11 +591,8 @@ void snd_mixer_selem_set_capture_volume_range(snd_mixer_elem_t *elem,
|
||||
*/
|
||||
int snd_mixer_selem_has_capture_volume(snd_mixer_elem_t *elem)
|
||||
{
|
||||
sm_selem_t *s;
|
||||
assert(elem);
|
||||
assert(elem->type == SND_MIXER_ELEM_SIMPLE);
|
||||
s = elem->private_data;
|
||||
return !!(s->caps & SM_CAP_CVOLUME);
|
||||
CHECK_BASIC(elem);
|
||||
return COND_CAPS(elem, SM_CAP_CVOLUME);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -552,11 +602,8 @@ int snd_mixer_selem_has_capture_volume(snd_mixer_elem_t *elem)
|
||||
*/
|
||||
int snd_mixer_selem_has_capture_volume_joined(snd_mixer_elem_t *elem)
|
||||
{
|
||||
sm_selem_t *s;
|
||||
assert(elem);
|
||||
assert(elem->type == SND_MIXER_ELEM_SIMPLE);
|
||||
s = elem->private_data;
|
||||
return !!(s->caps & SM_CAP_CVOLUME_JOIN);
|
||||
CHECK_BASIC(elem);
|
||||
return COND_CAPS(elem, SM_CAP_CVOLUME_JOIN);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -566,11 +613,8 @@ int snd_mixer_selem_has_capture_volume_joined(snd_mixer_elem_t *elem)
|
||||
*/
|
||||
int snd_mixer_selem_has_capture_switch(snd_mixer_elem_t *elem)
|
||||
{
|
||||
sm_selem_t *s;
|
||||
assert(elem);
|
||||
assert(elem->type == SND_MIXER_ELEM_SIMPLE);
|
||||
s = elem->private_data;
|
||||
return !!(s->caps & SM_CAP_CSWITCH);
|
||||
CHECK_BASIC(elem);
|
||||
return COND_CAPS(elem, SM_CAP_CSWITCH);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -580,11 +624,8 @@ int snd_mixer_selem_has_capture_switch(snd_mixer_elem_t *elem)
|
||||
*/
|
||||
int snd_mixer_selem_has_capture_switch_joined(snd_mixer_elem_t *elem)
|
||||
{
|
||||
sm_selem_t *s;
|
||||
assert(elem);
|
||||
assert(elem->type == SND_MIXER_ELEM_SIMPLE);
|
||||
s = elem->private_data;
|
||||
return !!(s->caps & SM_CAP_CSWITCH_JOIN);
|
||||
CHECK_BASIC(elem);
|
||||
return COND_CAPS(elem, SM_CAP_CSWITCH_JOIN);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -594,11 +635,8 @@ int snd_mixer_selem_has_capture_switch_joined(snd_mixer_elem_t *elem)
|
||||
*/
|
||||
int snd_mixer_selem_has_capture_switch_exclusive(snd_mixer_elem_t *elem)
|
||||
{
|
||||
sm_selem_t *s;
|
||||
assert(elem);
|
||||
assert(elem->type == SND_MIXER_ELEM_SIMPLE);
|
||||
s = elem->private_data;
|
||||
return !!(s->caps & SM_CAP_CSWITCH_EXCL);
|
||||
CHECK_BASIC(elem);
|
||||
return COND_CAPS(elem, SM_CAP_CSWITCH_EXCL);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -609,8 +647,7 @@ int snd_mixer_selem_has_capture_switch_exclusive(snd_mixer_elem_t *elem)
|
||||
int snd_mixer_selem_get_capture_group(snd_mixer_elem_t *elem)
|
||||
{
|
||||
sm_selem_t *s;
|
||||
assert(elem);
|
||||
assert(elem->type == SND_MIXER_ELEM_SIMPLE);
|
||||
CHECK_BASIC(elem);
|
||||
s = elem->private_data;
|
||||
if (! (s->caps & SM_CAP_CSWITCH_EXCL))
|
||||
return -EINVAL;
|
||||
@ -626,8 +663,8 @@ int snd_mixer_selem_get_capture_group(snd_mixer_elem_t *elem)
|
||||
*/
|
||||
int snd_mixer_selem_get_capture_volume(snd_mixer_elem_t *elem, snd_mixer_selem_channel_id_t channel, long *value)
|
||||
{
|
||||
assert(elem);
|
||||
assert(elem->type == SND_MIXER_ELEM_SIMPLE);
|
||||
CHECK_BASIC(elem);
|
||||
CHECK_DIR_CHN(elem, SM_CAP_CVOLUME, SM_CAP_CVOLUME_JOIN, channel);
|
||||
return sm_selem_ops(elem)->get_volume(elem, SM_CAPT, channel, value);
|
||||
}
|
||||
|
||||
@ -640,8 +677,8 @@ int snd_mixer_selem_get_capture_volume(snd_mixer_elem_t *elem, snd_mixer_selem_c
|
||||
*/
|
||||
int snd_mixer_selem_get_capture_dB(snd_mixer_elem_t *elem, snd_mixer_selem_channel_id_t channel, long *value)
|
||||
{
|
||||
assert(elem);
|
||||
assert(elem->type == SND_MIXER_ELEM_SIMPLE);
|
||||
CHECK_BASIC(elem);
|
||||
CHECK_DIR_CHN(elem, SM_CAP_CVOLUME, SM_CAP_CVOLUME_JOIN, channel);
|
||||
return sm_selem_ops(elem)->get_dB(elem, SM_CAPT, channel, value);
|
||||
}
|
||||
|
||||
@ -654,8 +691,8 @@ int snd_mixer_selem_get_capture_dB(snd_mixer_elem_t *elem, snd_mixer_selem_chann
|
||||
*/
|
||||
int snd_mixer_selem_get_capture_switch(snd_mixer_elem_t *elem, snd_mixer_selem_channel_id_t channel, int *value)
|
||||
{
|
||||
assert(elem);
|
||||
assert(elem->type == SND_MIXER_ELEM_SIMPLE);
|
||||
CHECK_BASIC(elem);
|
||||
CHECK_DIR_CHN(elem, SM_CAP_CSWITCH, SM_CAP_CSWITCH_JOIN, channel);
|
||||
return sm_selem_ops(elem)->get_switch(elem, SM_CAPT, channel, value);
|
||||
}
|
||||
|
||||
@ -668,8 +705,8 @@ int snd_mixer_selem_get_capture_switch(snd_mixer_elem_t *elem, snd_mixer_selem_c
|
||||
*/
|
||||
int snd_mixer_selem_set_capture_volume(snd_mixer_elem_t *elem, snd_mixer_selem_channel_id_t channel, long value)
|
||||
{
|
||||
assert(elem);
|
||||
assert(elem->type == SND_MIXER_ELEM_SIMPLE);
|
||||
CHECK_BASIC(elem);
|
||||
CHECK_DIR_CHN(elem, SM_CAP_CVOLUME, SM_CAP_CVOLUME_JOIN, channel);
|
||||
return sm_selem_ops(elem)->set_volume(elem, SM_CAPT, channel, value);
|
||||
}
|
||||
|
||||
@ -678,12 +715,13 @@ int snd_mixer_selem_set_capture_volume(snd_mixer_elem_t *elem, snd_mixer_selem_c
|
||||
* \param elem Mixer simple element handle
|
||||
* \param channel mixer simple element channel identifier
|
||||
* \param value control value in dB * 100
|
||||
* \param dir select direction (-1 = accurate or first bellow, 0 = accurate, 1 = accurate or first above)
|
||||
* \return 0 on success otherwise a negative error code
|
||||
*/
|
||||
int snd_mixer_selem_set_capture_dB(snd_mixer_elem_t *elem, snd_mixer_selem_channel_id_t channel, long value, int dir)
|
||||
{
|
||||
assert(elem);
|
||||
assert(elem->type == SND_MIXER_ELEM_SIMPLE);
|
||||
CHECK_BASIC(elem);
|
||||
CHECK_DIR_CHN(elem, SM_CAP_CVOLUME, SM_CAP_CVOLUME_JOIN, channel);
|
||||
return sm_selem_ops(elem)->set_dB(elem, SM_CAPT, channel, value, dir);
|
||||
}
|
||||
|
||||
@ -695,22 +733,43 @@ int snd_mixer_selem_set_capture_dB(snd_mixer_elem_t *elem, snd_mixer_selem_chann
|
||||
*/
|
||||
int snd_mixer_selem_set_capture_volume_all(snd_mixer_elem_t *elem, long value)
|
||||
{
|
||||
assert(elem);
|
||||
assert(elem->type == SND_MIXER_ELEM_SIMPLE);
|
||||
return sm_selem_ops(elem)->set_volume_all(elem, SM_CAPT, value);
|
||||
snd_mixer_selem_channel_id_t chn;
|
||||
int err;
|
||||
|
||||
for (chn = 0; chn < 32; chn++) {
|
||||
if (!snd_mixer_selem_has_capture_channel(elem, chn))
|
||||
continue;
|
||||
err = snd_mixer_selem_set_capture_volume(elem, chn, value);
|
||||
if (err < 0)
|
||||
return err;
|
||||
if (chn == 0 && snd_mixer_selem_has_capture_volume_joined(elem))
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Set value in dB of capture volume control for all channels of a mixer simple element
|
||||
* \param elem Mixer simple element handle
|
||||
* \param value control value in dB * 100
|
||||
* \param dir select direction (-1 = accurate or first bellow, 0 = accurate, 1 = accurate or first above)
|
||||
* \return 0 on success otherwise a negative error code
|
||||
*/
|
||||
int snd_mixer_selem_set_capture_dB_all(snd_mixer_elem_t *elem, long value, int dir)
|
||||
{
|
||||
assert(elem);
|
||||
assert(elem->type == SND_MIXER_ELEM_SIMPLE);
|
||||
return sm_selem_ops(elem)->set_dB_all(elem, SM_CAPT, value, dir);
|
||||
snd_mixer_selem_channel_id_t chn;
|
||||
int err;
|
||||
|
||||
for (chn = 0; chn < 32; chn++) {
|
||||
if (!snd_mixer_selem_has_capture_channel(elem, chn))
|
||||
continue;
|
||||
err = snd_mixer_selem_set_capture_dB(elem, chn, value, dir);
|
||||
if (err < 0)
|
||||
return err;
|
||||
if (chn == 0 && snd_mixer_selem_has_capture_volume_joined(elem))
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -722,8 +781,8 @@ int snd_mixer_selem_set_capture_dB_all(snd_mixer_elem_t *elem, long value, int d
|
||||
*/
|
||||
int snd_mixer_selem_set_capture_switch(snd_mixer_elem_t *elem, snd_mixer_selem_channel_id_t channel, int value)
|
||||
{
|
||||
assert(elem);
|
||||
assert(elem->type == SND_MIXER_ELEM_SIMPLE);
|
||||
CHECK_BASIC(elem);
|
||||
CHECK_DIR_CHN(elem, SM_CAP_CSWITCH, SM_CAP_CSWITCH_JOIN, channel);
|
||||
return sm_selem_ops(elem)->set_switch(elem, SM_CAPT, channel, value);
|
||||
}
|
||||
|
||||
@ -735,9 +794,19 @@ int snd_mixer_selem_set_capture_switch(snd_mixer_elem_t *elem, snd_mixer_selem_c
|
||||
*/
|
||||
int snd_mixer_selem_set_capture_switch_all(snd_mixer_elem_t *elem, int value)
|
||||
{
|
||||
assert(elem);
|
||||
assert(elem->type == SND_MIXER_ELEM_SIMPLE);
|
||||
return sm_selem_ops(elem)->set_switch_all(elem, SM_CAPT, value);
|
||||
snd_mixer_selem_channel_id_t chn;
|
||||
int err;
|
||||
|
||||
for (chn = 0; chn < 32; chn++) {
|
||||
if (!snd_mixer_selem_has_capture_channel(elem, chn))
|
||||
continue;
|
||||
err = snd_mixer_selem_set_capture_switch(elem, chn, value);
|
||||
if (err < 0)
|
||||
return err;
|
||||
if (chn == 0 && snd_mixer_selem_has_capture_switch_joined(elem))
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -747,8 +816,8 @@ int snd_mixer_selem_set_capture_switch_all(snd_mixer_elem_t *elem, int value)
|
||||
*/
|
||||
int snd_mixer_selem_is_enumerated(snd_mixer_elem_t *elem)
|
||||
{
|
||||
assert(elem);
|
||||
assert(elem->type == SND_MIXER_ELEM_SIMPLE);
|
||||
CHECK_BASIC(elem);
|
||||
CHECK_ENUM(elem);
|
||||
return sm_selem_ops(elem)->is(elem, SM_PLAY, SM_OPS_IS_ENUMERATED, 0);
|
||||
}
|
||||
|
||||
@ -759,8 +828,8 @@ int snd_mixer_selem_is_enumerated(snd_mixer_elem_t *elem)
|
||||
*/
|
||||
int snd_mixer_selem_get_enum_items(snd_mixer_elem_t *elem)
|
||||
{
|
||||
assert(elem);
|
||||
assert(elem->type == SND_MIXER_ELEM_SIMPLE);
|
||||
CHECK_BASIC(elem);
|
||||
CHECK_ENUM(elem);
|
||||
return sm_selem_ops(elem)->is(elem, SM_PLAY, SM_OPS_IS_ENUMCNT, 0);
|
||||
}
|
||||
|
||||
@ -776,8 +845,8 @@ int snd_mixer_selem_get_enum_item_name(snd_mixer_elem_t *elem,
|
||||
unsigned int item,
|
||||
size_t maxlen, char *buf)
|
||||
{
|
||||
assert(elem);
|
||||
assert(elem->type == SND_MIXER_ELEM_SIMPLE);
|
||||
CHECK_BASIC(elem);
|
||||
CHECK_ENUM(elem);
|
||||
return sm_selem_ops(elem)->enum_item_name(elem, item, maxlen, buf);
|
||||
}
|
||||
|
||||
@ -792,8 +861,8 @@ int snd_mixer_selem_get_enum_item(snd_mixer_elem_t *elem,
|
||||
snd_mixer_selem_channel_id_t channel,
|
||||
unsigned int *itemp)
|
||||
{
|
||||
assert(elem);
|
||||
assert(elem->type == SND_MIXER_ELEM_SIMPLE);
|
||||
CHECK_BASIC(elem);
|
||||
CHECK_ENUM(elem);
|
||||
return sm_selem_ops(elem)->get_enum_item(elem, channel, itemp);
|
||||
}
|
||||
|
||||
@ -808,8 +877,8 @@ int snd_mixer_selem_set_enum_item(snd_mixer_elem_t *elem,
|
||||
snd_mixer_selem_channel_id_t channel,
|
||||
unsigned int item)
|
||||
{
|
||||
assert(elem);
|
||||
assert(elem->type == SND_MIXER_ELEM_SIMPLE);
|
||||
CHECK_BASIC(elem);
|
||||
CHECK_ENUM(elem);
|
||||
return sm_selem_ops(elem)->set_enum_item(elem, channel, item);
|
||||
}
|
||||
|
||||
|
@ -27,10 +27,520 @@
|
||||
#include <sys/ioctl.h>
|
||||
#include <math.h>
|
||||
#include <alsa/asoundlib.h>
|
||||
#include <alsa/mixer_abst.h>
|
||||
#include <alsa/list.h>
|
||||
|
||||
#define MAX_CHANNEL 6
|
||||
|
||||
#define SID_MASTER 0
|
||||
|
||||
struct melem_sids {
|
||||
const char *sname;
|
||||
unsigned short sindex;
|
||||
unsigned short weight;
|
||||
unsigned int chanmap[2];
|
||||
};
|
||||
|
||||
struct melem_sids sids[] = {
|
||||
{
|
||||
.sname = "Master",
|
||||
.sindex = 0,
|
||||
.weight = 1,
|
||||
.chanmap = { 3, 0 },
|
||||
}
|
||||
};
|
||||
|
||||
#define PURPOSE_VOLUME 0
|
||||
#define PURPOSE_SWITCH 1
|
||||
#define PURPOSE_ENUMLIST 2
|
||||
|
||||
struct helem_selector {
|
||||
snd_ctl_elem_iface_t iface;
|
||||
const char *name;
|
||||
unsigned short index;
|
||||
unsigned short sid;
|
||||
unsigned short purpose;
|
||||
unsigned short caps;
|
||||
};
|
||||
|
||||
#define SELECTORS (sizeof(selectors)/sizeof(selectors[0]))
|
||||
|
||||
struct helem_selector selectors[] = {
|
||||
{
|
||||
.iface = SND_CTL_ELEM_IFACE_MIXER,
|
||||
.name = "Master Playback Volume",
|
||||
.index = 0,
|
||||
.sid = SID_MASTER,
|
||||
.purpose = PURPOSE_VOLUME,
|
||||
.caps = SM_CAP_PVOLUME,
|
||||
},
|
||||
{
|
||||
.iface = SND_CTL_ELEM_IFACE_MIXER,
|
||||
.name = "Master Playback Switch",
|
||||
.index = 0,
|
||||
.sid = SID_MASTER,
|
||||
.purpose = PURPOSE_SWITCH,
|
||||
.caps = SM_CAP_PSWITCH,
|
||||
}
|
||||
};
|
||||
|
||||
struct helem_ac97 {
|
||||
struct list_head list;
|
||||
snd_hctl_elem_t *helem;
|
||||
unsigned short purpose;
|
||||
unsigned int caps;
|
||||
unsigned int inactive: 1;
|
||||
long min, max;
|
||||
unsigned int count;
|
||||
};
|
||||
|
||||
struct selem_ac97 {
|
||||
sm_selem_t selem;
|
||||
struct list_head helems;
|
||||
unsigned short sid;
|
||||
struct {
|
||||
unsigned int chanmap;
|
||||
unsigned int forced_range: 1;
|
||||
long min, max;
|
||||
long vol[MAX_CHANNEL];
|
||||
} dir[2];
|
||||
};
|
||||
|
||||
/*
|
||||
* Prototypes
|
||||
*/
|
||||
|
||||
static int selem_read(snd_mixer_elem_t *elem);
|
||||
|
||||
/*
|
||||
* Helpers
|
||||
*/
|
||||
|
||||
static unsigned int chanmap_to_channels(unsigned int chanmap)
|
||||
{
|
||||
unsigned int i, res;
|
||||
|
||||
for (i = 0, res = 0; i < MAX_CHANNEL; i++)
|
||||
if (chanmap & (1 << i))
|
||||
res++;
|
||||
return res;
|
||||
}
|
||||
|
||||
static long to_user(struct selem_ac97 *s, int dir, struct helem_ac97 *c, long value)
|
||||
{
|
||||
int64_t n;
|
||||
if (c->max == c->min)
|
||||
return s->dir[dir].min;
|
||||
n = (int64_t) (value - c->min) * (s->dir[dir].max - s->dir[dir].min);
|
||||
return s->dir[dir].min + (n + (c->max - c->min) / 2) / (c->max - c->min);
|
||||
}
|
||||
|
||||
static long from_user(struct selem_ac97 *s, int dir, struct helem_ac97 *c, long value)
|
||||
{
|
||||
int64_t n;
|
||||
if (s->dir[dir].max == s->dir[dir].min)
|
||||
return c->min;
|
||||
n = (int64_t) (value - s->dir[dir].min) * (c->max - c->min);
|
||||
return c->min + (n + (s->dir[dir].max - s->dir[dir].min) / 2) / (s->dir[dir].max - s->dir[dir].min);
|
||||
}
|
||||
|
||||
static void update_ranges(struct selem_ac97 *s)
|
||||
{
|
||||
static unsigned int mask[2] = { SM_CAP_PVOLUME, SM_CAP_CVOLUME };
|
||||
static unsigned int gmask[2] = { SM_CAP_GVOLUME, SM_CAP_GVOLUME };
|
||||
unsigned int dir, ok_flag;
|
||||
struct list_head *pos;
|
||||
struct helem_ac97 *helem;
|
||||
|
||||
for (dir = 0; dir < 2; dir++) {
|
||||
s->dir[dir].min = 0;
|
||||
s->dir[dir].max = 0;
|
||||
ok_flag = 0;
|
||||
list_for_each(pos, &s->helems) {
|
||||
helem = list_entry(pos, struct helem_ac97, list);
|
||||
printf("min = %li, max = %li\n", helem->min, helem->max);
|
||||
if (helem->caps & mask[dir]) {
|
||||
s->dir[dir].min = helem->min;
|
||||
s->dir[dir].max = helem->max;
|
||||
ok_flag = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (ok_flag)
|
||||
continue;
|
||||
list_for_each(pos, &s->helems) {
|
||||
helem = list_entry(pos, struct helem_ac97, list);
|
||||
if (helem->caps & gmask[dir]) {
|
||||
s->dir[dir].min = helem->min;
|
||||
s->dir[dir].max = helem->max;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Simple Mixer Operations
|
||||
*/
|
||||
|
||||
static int is_ops(snd_mixer_elem_t *elem, int dir, int cmd, int val)
|
||||
{
|
||||
struct selem_ac97 *s = snd_mixer_elem_get_private(elem);
|
||||
|
||||
switch (cmd) {
|
||||
|
||||
case SM_OPS_IS_ACTIVE: {
|
||||
struct list_head *pos;
|
||||
struct helem_ac97 *helem;
|
||||
list_for_each(pos, &s->helems) {
|
||||
helem = list_entry(pos, struct helem_ac97, list);
|
||||
if (helem->inactive)
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
case SM_OPS_IS_MONO:
|
||||
return chanmap_to_channels(s->dir[dir].chanmap) == 1;
|
||||
|
||||
case SM_OPS_IS_CHANNEL:
|
||||
if (val > MAX_CHANNEL)
|
||||
return 0;
|
||||
return !!((1 << val) & s->dir[dir].chanmap);
|
||||
|
||||
case SM_OPS_IS_ENUMERATED: {
|
||||
struct helem_ac97 *helem;
|
||||
helem = list_entry(s->helems.next, struct helem_ac97, list);
|
||||
return !!(helem->purpose == PURPOSE_ENUMLIST);
|
||||
}
|
||||
|
||||
case SM_OPS_IS_ENUMCNT: {
|
||||
struct helem_ac97 *helem;
|
||||
helem = list_entry(s->helems.next, struct helem_ac97, list);
|
||||
return helem->max;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int get_range_ops(snd_mixer_elem_t *elem, int dir,
|
||||
long *min, long *max)
|
||||
{
|
||||
struct selem_ac97 *s = snd_mixer_elem_get_private(elem);
|
||||
|
||||
*min = s->dir[dir].min;
|
||||
*max = s->dir[dir].max;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_dB_range_ops(snd_mixer_elem_t *elem ATTRIBUTE_UNUSED,
|
||||
int dir ATTRIBUTE_UNUSED,
|
||||
long *min ATTRIBUTE_UNUSED,
|
||||
long *max ATTRIBUTE_UNUSED)
|
||||
{
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
static int set_range_ops(snd_mixer_elem_t *elem, int dir,
|
||||
long min, long max)
|
||||
{
|
||||
struct selem_ac97 *s = snd_mixer_elem_get_private(elem);
|
||||
int err;
|
||||
|
||||
s->dir[dir].forced_range = 1;
|
||||
s->dir[dir].min = min;
|
||||
s->dir[dir].max = max;
|
||||
|
||||
if ((err = selem_read(elem)) < 0)
|
||||
return err;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_volume_ops(snd_mixer_elem_t *elem, int dir,
|
||||
snd_mixer_selem_channel_id_t channel, long *value)
|
||||
{
|
||||
struct selem_ac97 *s = snd_mixer_elem_get_private(elem);
|
||||
|
||||
*value = s->dir[dir].vol[channel];
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_dB_ops(snd_mixer_elem_t *elem ATTRIBUTE_UNUSED,
|
||||
int dir ATTRIBUTE_UNUSED,
|
||||
snd_mixer_selem_channel_id_t channel ATTRIBUTE_UNUSED,
|
||||
long *value ATTRIBUTE_UNUSED)
|
||||
{
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
static int get_switch_ops(snd_mixer_elem_t *elem, int dir,
|
||||
snd_mixer_selem_channel_id_t channel, int *value)
|
||||
{
|
||||
struct selem_ac97 *s = snd_mixer_elem_get_private(elem);
|
||||
*value = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int set_volume_ops(snd_mixer_elem_t *elem, int dir,
|
||||
snd_mixer_selem_channel_id_t channel, long value)
|
||||
{
|
||||
struct selem_ac97 *s = snd_mixer_elem_get_private(elem);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int set_dB_ops(snd_mixer_elem_t *elem ATTRIBUTE_UNUSED,
|
||||
int dir ATTRIBUTE_UNUSED,
|
||||
snd_mixer_selem_channel_id_t channel ATTRIBUTE_UNUSED,
|
||||
long value ATTRIBUTE_UNUSED,
|
||||
int xdir ATTRIBUTE_UNUSED)
|
||||
{
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
static int set_switch_ops(snd_mixer_elem_t *elem, int dir,
|
||||
snd_mixer_selem_channel_id_t channel, int value)
|
||||
{
|
||||
struct selem_ac97 *s = snd_mixer_elem_get_private(elem);
|
||||
int changed;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int enum_item_name_ops(snd_mixer_elem_t *elem,
|
||||
unsigned int item,
|
||||
size_t maxlen, char *buf)
|
||||
{
|
||||
struct selem_ac97 *s = snd_mixer_elem_get_private(elem);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_enum_item_ops(snd_mixer_elem_t *elem,
|
||||
snd_mixer_selem_channel_id_t channel,
|
||||
unsigned int *itemp)
|
||||
{
|
||||
struct selem_ac97 *s = snd_mixer_elem_get_private(elem);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int set_enum_item_ops(snd_mixer_elem_t *elem,
|
||||
snd_mixer_selem_channel_id_t channel,
|
||||
unsigned int item)
|
||||
{
|
||||
struct selem_ac97 *s = snd_mixer_elem_get_private(elem);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct sm_elem_ops simple_ac97_ops = {
|
||||
.is = is_ops,
|
||||
.get_range = get_range_ops,
|
||||
.get_dB_range = get_dB_range_ops,
|
||||
.set_range = set_range_ops,
|
||||
.get_volume = get_volume_ops,
|
||||
.get_dB = get_dB_ops,
|
||||
.set_volume = set_volume_ops,
|
||||
.set_dB = set_dB_ops,
|
||||
.get_switch = get_switch_ops,
|
||||
.set_switch = set_switch_ops,
|
||||
.enum_item_name = enum_item_name_ops,
|
||||
.get_enum_item = get_enum_item_ops,
|
||||
.set_enum_item = set_enum_item_ops
|
||||
};
|
||||
|
||||
/*
|
||||
* event handling
|
||||
*/
|
||||
|
||||
static int selem_read(snd_mixer_elem_t *elem)
|
||||
{
|
||||
printf("elem read: %p\n", elem);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int simple_event_remove(snd_hctl_elem_t *helem,
|
||||
snd_mixer_elem_t *melem)
|
||||
{
|
||||
printf("event remove: %p\n", helem);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void selem_free(snd_mixer_elem_t *elem)
|
||||
{
|
||||
struct selem_ac97 *simple = snd_mixer_elem_get_private(elem);
|
||||
struct helem_ac97 *hsimple;
|
||||
struct list_head *pos, *npos;
|
||||
|
||||
if (simple->selem.id)
|
||||
snd_mixer_selem_id_free(simple->selem.id);
|
||||
list_for_each_safe(pos, npos, &simple->helems) {
|
||||
hsimple = list_entry(pos, struct helem_ac97, list);
|
||||
free(hsimple);
|
||||
}
|
||||
free(simple);
|
||||
}
|
||||
|
||||
static int simple_event_add(snd_mixer_class_t *class, snd_hctl_elem_t *helem)
|
||||
{
|
||||
int count;
|
||||
struct helem_selector *sel;
|
||||
snd_ctl_elem_iface_t iface = snd_hctl_elem_get_interface(helem);
|
||||
const char *name = snd_hctl_elem_get_name(helem);
|
||||
unsigned int index = snd_hctl_elem_get_index(helem);
|
||||
snd_mixer_elem_t *melem;
|
||||
snd_mixer_selem_id_t *id;
|
||||
struct selem_ac97 *simple;
|
||||
struct helem_ac97 *hsimple;
|
||||
snd_ctl_elem_info_t *info;
|
||||
snd_ctl_elem_type_t ctype;
|
||||
unsigned long values;
|
||||
long min, max;
|
||||
int err, new = 0;
|
||||
|
||||
snd_ctl_elem_info_alloca(&info);
|
||||
for (count = SELECTORS, sel = selectors; count > 0; count--, sel++) {
|
||||
if (sel->iface == iface && !strcmp(sel->name, name) && sel->index == index)
|
||||
break;
|
||||
}
|
||||
if (count == 0)
|
||||
return 0; /* ignore this helem */
|
||||
err = snd_hctl_elem_info(helem, info);
|
||||
if (err < 0)
|
||||
return err;
|
||||
ctype = snd_ctl_elem_info_get_type(info);
|
||||
values = snd_ctl_elem_info_get_count(info);
|
||||
switch (ctype) {
|
||||
case SND_CTL_ELEM_TYPE_ENUMERATED:
|
||||
min = 0;
|
||||
max = snd_ctl_elem_info_get_items(info);
|
||||
break;
|
||||
case SND_CTL_ELEM_TYPE_INTEGER:
|
||||
min = snd_ctl_elem_info_get_min(info);
|
||||
max = snd_ctl_elem_info_get_max(info);
|
||||
break;
|
||||
default:
|
||||
min = max = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
printf("event add: %p, %p (%s)\n", helem, sel, name);
|
||||
if (snd_mixer_selem_id_malloc(&id))
|
||||
return -ENOMEM;
|
||||
hsimple = calloc(1, sizeof(*hsimple));
|
||||
if (hsimple == NULL) {
|
||||
snd_mixer_selem_id_free(id);
|
||||
return -ENOMEM;
|
||||
}
|
||||
switch (sel->purpose) {
|
||||
case PURPOSE_SWITCH:
|
||||
if (ctype != SND_CTL_ELEM_TYPE_BOOLEAN) {
|
||||
__invalid_type:
|
||||
snd_mixer_selem_id_free(id);
|
||||
return -EINVAL;
|
||||
}
|
||||
break;
|
||||
case PURPOSE_VOLUME:
|
||||
if (ctype != SND_CTL_ELEM_TYPE_INTEGER)
|
||||
goto __invalid_type;
|
||||
break;
|
||||
}
|
||||
hsimple->purpose = sel->purpose;
|
||||
hsimple->caps = sel->caps;
|
||||
hsimple->min = min;
|
||||
hsimple->max = max;
|
||||
snd_mixer_selem_id_set_name(id, sids[sel->sid].sname);
|
||||
snd_mixer_selem_id_set_index(id, sids[sel->sid].sindex);
|
||||
melem = snd_mixer_find_selem(snd_mixer_class_get_mixer(class), id);
|
||||
if (!melem) {
|
||||
simple = calloc(1, sizeof(*simple));
|
||||
if (!simple) {
|
||||
snd_mixer_selem_id_free(id);
|
||||
free(hsimple);
|
||||
return -ENOMEM;
|
||||
}
|
||||
simple->selem.id = id;
|
||||
simple->selem.ops = &simple_ac97_ops;
|
||||
INIT_LIST_HEAD(&simple->helems);
|
||||
simple->sid = sel->sid;
|
||||
err = snd_mixer_elem_new(&melem, SND_MIXER_ELEM_SIMPLE,
|
||||
sids[sel->sid].weight,
|
||||
simple, selem_free);
|
||||
if (err < 0) {
|
||||
snd_mixer_selem_id_free(id);
|
||||
free(hsimple);
|
||||
free(simple);
|
||||
return err;
|
||||
}
|
||||
new = 1;
|
||||
} else {
|
||||
simple = snd_mixer_elem_get_private(melem);
|
||||
snd_mixer_selem_id_free(id);
|
||||
}
|
||||
list_add_tail(&hsimple->list, &simple->helems);
|
||||
hsimple->inactive = snd_ctl_elem_info_is_inactive(info);
|
||||
err = snd_mixer_elem_attach(melem, helem);
|
||||
if (err < 0)
|
||||
goto __error;
|
||||
simple->dir[0].chanmap |= sids[sel->sid].chanmap[0];
|
||||
simple->dir[1].chanmap |= sids[sel->sid].chanmap[1];
|
||||
simple->selem.caps |= hsimple->caps;
|
||||
update_ranges(simple);
|
||||
#if 0
|
||||
err = simple_update(melem);
|
||||
if (err < 0) {
|
||||
if (new)
|
||||
goto __error;
|
||||
return err;
|
||||
}
|
||||
#endif
|
||||
if (new)
|
||||
err = snd_mixer_elem_add(melem, class);
|
||||
else
|
||||
err = snd_mixer_elem_info(melem);
|
||||
if (err < 0)
|
||||
return err;
|
||||
err = selem_read(melem);
|
||||
if (err < 0)
|
||||
return err;
|
||||
if (err)
|
||||
err = snd_mixer_elem_value(melem);
|
||||
return err;
|
||||
__error:
|
||||
if (new)
|
||||
snd_mixer_elem_free(melem);
|
||||
return -EINVAL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int alsa_mixer_simple_event(snd_mixer_class_t *class, unsigned int mask,
|
||||
snd_hctl_elem_t *helem, snd_mixer_elem_t *melem)
|
||||
{
|
||||
return -EIO;
|
||||
int err;
|
||||
if (mask == SND_CTL_EVENT_MASK_REMOVE)
|
||||
return simple_event_remove(helem, melem);
|
||||
if (mask & SND_CTL_EVENT_MASK_ADD) {
|
||||
err = simple_event_add(class, helem);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
if (mask & SND_CTL_EVENT_MASK_INFO) {
|
||||
err = simple_event_remove(helem, melem);
|
||||
if (err < 0)
|
||||
return err;
|
||||
err = simple_event_add(class, helem);
|
||||
if (err < 0)
|
||||
return err;
|
||||
return 0;
|
||||
}
|
||||
if (mask & SND_CTL_EVENT_MASK_VALUE) {
|
||||
err = selem_read(melem);
|
||||
if (err < 0)
|
||||
return err;
|
||||
if (err) {
|
||||
err = snd_mixer_elem_value(melem);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
@ -45,6 +45,7 @@ typedef struct _class_priv {
|
||||
char *device;
|
||||
snd_ctl_t *ctl;
|
||||
snd_hctl_t *hctl;
|
||||
int attach_flag;
|
||||
snd_ctl_card_info_t *info;
|
||||
void *dlhandle;
|
||||
} class_priv_t;
|
||||
@ -81,7 +82,7 @@ static int try_open(snd_mixer_class_t *class, const char *lib)
|
||||
free(xlib);
|
||||
snd_mixer_class_set_event(class, event_func);
|
||||
priv->dlhandle = h;
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int match(snd_mixer_class_t *class, const char *lib, const char *searchl)
|
||||
@ -151,9 +152,11 @@ static void private_free(snd_mixer_class_t *class)
|
||||
snd_dlclose(priv->dlhandle);
|
||||
if (priv->info)
|
||||
snd_ctl_card_info_free(priv->info);
|
||||
if (priv->hctl)
|
||||
if (priv->hctl) {
|
||||
if (priv->attach_flag)
|
||||
snd_mixer_detach_hctl(snd_mixer_class_get_mixer(class), priv->hctl);
|
||||
snd_hctl_close(priv->hctl);
|
||||
else if (priv->ctl)
|
||||
} else if (priv->ctl)
|
||||
snd_ctl_close(priv->ctl);
|
||||
if (priv->device)
|
||||
free(priv->device);
|
||||
@ -178,10 +181,12 @@ int snd_mixer_simple_basic_register(snd_mixer_t *mixer,
|
||||
snd_config_t *top = NULL;
|
||||
int err;
|
||||
|
||||
if (options->device == NULL)
|
||||
return -EIO;
|
||||
if (priv == NULL)
|
||||
return -ENOMEM;
|
||||
if (options->device == NULL) {
|
||||
free(priv);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (snd_mixer_class_malloc(&class)) {
|
||||
free(priv);
|
||||
return -ENOMEM;
|
||||
@ -226,13 +231,19 @@ int snd_mixer_simple_basic_register(snd_mixer_t *mixer,
|
||||
goto __error;
|
||||
}
|
||||
err = find_module(class, top);
|
||||
snd_config_delete(top);
|
||||
top = NULL;
|
||||
}
|
||||
if (err >= 0)
|
||||
err = snd_mixer_attach_hctl(mixer, priv->hctl);
|
||||
if (err >= 0) {
|
||||
priv->attach_flag = 1;
|
||||
err = snd_mixer_class_register(class, mixer);
|
||||
}
|
||||
if (err < 0) {
|
||||
__error:
|
||||
if (top)
|
||||
snd_config_delete(top);
|
||||
if (top)
|
||||
snd_config_delete(top);
|
||||
if (class)
|
||||
snd_mixer_class_free(class);
|
||||
return err;
|
||||
@ -243,3 +254,23 @@ int snd_mixer_simple_basic_register(snd_mixer_t *mixer,
|
||||
*classp = class;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Register mixer simple element class - basic abstraction
|
||||
* \param mixer Mixer handle
|
||||
* \param options Options container
|
||||
* \param classp Pointer to returned mixer simple element class handle (or NULL
|
||||
* \return 0 on success otherwise a negative error code
|
||||
*/
|
||||
int snd_mixer_sbasic_info(const snd_mixer_class_t *class, sm_class_basic_t *info)
|
||||
{
|
||||
class_priv_t *priv = snd_mixer_class_get_private(class);
|
||||
|
||||
if (class == NULL || info == NULL)
|
||||
return -EINVAL;
|
||||
info->device = priv->device;
|
||||
info->ctl = priv->ctl;
|
||||
info->hctl = priv->hctl;
|
||||
info->info = priv->info;
|
||||
return 0;
|
||||
}
|
||||
|
@ -783,6 +783,14 @@ static int simple_update(snd_mixer_elem_t *melem)
|
||||
caps |= SM_CAP_PSWITCH;
|
||||
}
|
||||
|
||||
if ((caps & SM_CAP_GSWITCH) &&
|
||||
(caps & (SM_CAP_PSWITCH|SM_CAP_CSWITCH)) == 0)
|
||||
caps |= SM_CAP_PSWITCH|SM_CAP_CSWITCH;
|
||||
|
||||
if ((caps & SM_CAP_GVOLUME) &&
|
||||
(caps & (SM_CAP_PVOLUME|SM_CAP_CVOLUME)) == 0)
|
||||
caps |= SM_CAP_PVOLUME|SM_CAP_CVOLUME;
|
||||
|
||||
simple->selem.caps = caps;
|
||||
simple->str[SM_PLAY].channels = pchannels;
|
||||
if (!simple->str[SM_PLAY].range) {
|
||||
@ -859,22 +867,6 @@ static int _snd_mixer_selem_set_volume(snd_mixer_elem_t *elem, int dir, snd_mixe
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _snd_mixer_selem_set_volume_all(snd_mixer_elem_t *elem, int dir, long value)
|
||||
{
|
||||
int changed = 0;
|
||||
snd_mixer_selem_channel_id_t channel;
|
||||
selem_none_t *s = snd_mixer_elem_get_private(elem);
|
||||
if (value < s->str[dir].min || value > s->str[dir].max)
|
||||
return 0;
|
||||
for (channel = 0; (unsigned int) channel < s->str[dir].channels; channel++) {
|
||||
if (value != s->str[dir].vol[channel]) {
|
||||
s->str[dir].vol[channel] = value;
|
||||
changed = 1;
|
||||
}
|
||||
}
|
||||
return changed;
|
||||
}
|
||||
|
||||
static int _snd_mixer_selem_set_switch(snd_mixer_elem_t *elem, int dir, snd_mixer_selem_channel_id_t channel, int value)
|
||||
{
|
||||
selem_none_t *s = snd_mixer_elem_get_private(elem);
|
||||
@ -897,23 +889,6 @@ static int _snd_mixer_selem_set_switch(snd_mixer_elem_t *elem, int dir, snd_mixe
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _snd_mixer_selem_set_switch_all(snd_mixer_elem_t *elem, int dir, int value)
|
||||
{
|
||||
selem_none_t *s = snd_mixer_elem_get_private(elem);
|
||||
if (value) {
|
||||
if (s->str[dir].sw != ~0U) {
|
||||
s->str[dir].sw = ~0U;
|
||||
return 1;
|
||||
}
|
||||
} else {
|
||||
if (s->str[dir].sw != 0U) {
|
||||
s->str[dir].sw = 0U;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int is_ops(snd_mixer_elem_t *elem, int dir, int cmd, int val)
|
||||
{
|
||||
selem_none_t *s = snd_mixer_elem_get_private(elem);
|
||||
@ -984,17 +959,6 @@ static int get_volume_ops(snd_mixer_elem_t *elem, int dir,
|
||||
selem_none_t *s = snd_mixer_elem_get_private(elem);
|
||||
if ((unsigned int) channel >= s->str[dir].channels)
|
||||
return -EINVAL;
|
||||
if (dir == SM_PLAY) {
|
||||
if (! (s->selem.caps & (SM_CAP_PVOLUME|SM_CAP_GVOLUME)))
|
||||
return -EINVAL;
|
||||
if (s->selem.caps & SM_CAP_PVOLUME_JOIN)
|
||||
channel = 0;
|
||||
} else {
|
||||
if (! (s->selem.caps & (SM_CAP_CVOLUME|SM_CAP_GVOLUME)))
|
||||
return -EINVAL;
|
||||
if (s->selem.caps & SM_CAP_CVOLUME_JOIN)
|
||||
channel = 0;
|
||||
}
|
||||
*value = s->str[dir].vol[channel];
|
||||
return 0;
|
||||
}
|
||||
@ -1013,17 +977,6 @@ static int get_switch_ops(snd_mixer_elem_t *elem, int dir,
|
||||
selem_none_t *s = snd_mixer_elem_get_private(elem);
|
||||
if ((unsigned int) channel >= s->str[dir].channels)
|
||||
return -EINVAL;
|
||||
if (dir == SM_PLAY) {
|
||||
if (! (s->selem.caps & (SM_CAP_PSWITCH|SM_CAP_GSWITCH)))
|
||||
return -EINVAL;
|
||||
if (s->selem.caps & SM_CAP_PSWITCH_JOIN)
|
||||
channel = 0;
|
||||
} else {
|
||||
if (! (s->selem.caps & (SM_CAP_CSWITCH|SM_CAP_GSWITCH)))
|
||||
return -EINVAL;
|
||||
if (s->selem.caps & SM_CAP_CSWITCH_JOIN)
|
||||
channel = 0;
|
||||
}
|
||||
*value = !!(s->str[dir].sw & (1 << channel));
|
||||
return 0;
|
||||
}
|
||||
@ -1032,14 +985,6 @@ static int set_volume_ops(snd_mixer_elem_t *elem, int dir,
|
||||
snd_mixer_selem_channel_id_t channel, long value)
|
||||
{
|
||||
int changed;
|
||||
selem_none_t *s = snd_mixer_elem_get_private(elem);
|
||||
if (dir == SM_PLAY) {
|
||||
if (! (s->selem.caps & (SM_CAP_GVOLUME|SM_CAP_PVOLUME)))
|
||||
return -EINVAL;
|
||||
} else {
|
||||
if (! (s->selem.caps & (SM_CAP_GVOLUME|SM_CAP_CVOLUME)))
|
||||
return -EINVAL;
|
||||
}
|
||||
changed = _snd_mixer_selem_set_volume(elem, dir, channel, value);
|
||||
if (changed < 0)
|
||||
return changed;
|
||||
@ -1057,33 +1002,6 @@ static int set_dB_ops(snd_mixer_elem_t *elem ATTRIBUTE_UNUSED,
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
static int set_volume_all_ops(snd_mixer_elem_t *elem, int dir, long value)
|
||||
{
|
||||
int changed;
|
||||
selem_none_t *s = snd_mixer_elem_get_private(elem);
|
||||
if (dir == SM_PLAY) {
|
||||
if (! (s->selem.caps & (SM_CAP_GVOLUME|SM_CAP_PVOLUME)))
|
||||
return -EINVAL;
|
||||
} else {
|
||||
if (! (s->selem.caps & (SM_CAP_GVOLUME|SM_CAP_CVOLUME)))
|
||||
return -EINVAL;
|
||||
}
|
||||
changed = _snd_mixer_selem_set_volume_all(elem, dir, value);
|
||||
if (changed < 0)
|
||||
return changed;
|
||||
if (changed)
|
||||
return selem_write(elem);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int set_dB_all_ops(snd_mixer_elem_t *elem ATTRIBUTE_UNUSED,
|
||||
int dir ATTRIBUTE_UNUSED,
|
||||
long value ATTRIBUTE_UNUSED,
|
||||
int xdir ATTRIBUTE_UNUSED)
|
||||
{
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
static int set_switch_ops(snd_mixer_elem_t *elem, int dir,
|
||||
snd_mixer_selem_channel_id_t channel, int value)
|
||||
{
|
||||
@ -1104,25 +1022,6 @@ static int set_switch_ops(snd_mixer_elem_t *elem, int dir,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int set_switch_all_ops(snd_mixer_elem_t *elem, int dir, int value)
|
||||
{
|
||||
int changed;
|
||||
selem_none_t *s = snd_mixer_elem_get_private(elem);
|
||||
if (dir == SM_PLAY) {
|
||||
if (! (s->selem.caps & (SM_CAP_GSWITCH|SM_CAP_PSWITCH)))
|
||||
return -EINVAL;
|
||||
} else {
|
||||
if (! (s->selem.caps & (SM_CAP_GSWITCH|SM_CAP_CSWITCH)))
|
||||
return -EINVAL;
|
||||
}
|
||||
changed = _snd_mixer_selem_set_switch_all(elem, dir, value);
|
||||
if (changed < 0)
|
||||
return changed;
|
||||
if (changed)
|
||||
return selem_write(elem);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int enum_item_name_ops(snd_mixer_elem_t *elem,
|
||||
unsigned int item,
|
||||
size_t maxlen, char *buf)
|
||||
@ -1195,11 +1094,8 @@ static struct sm_elem_ops simple_none_ops = {
|
||||
.get_dB = get_dB_ops,
|
||||
.set_volume = set_volume_ops,
|
||||
.set_dB = set_dB_ops,
|
||||
.set_volume_all = set_volume_all_ops,
|
||||
.set_dB_all = set_dB_all_ops,
|
||||
.get_switch = get_switch_ops,
|
||||
.set_switch = set_switch_ops,
|
||||
.set_switch_all = set_switch_all_ops,
|
||||
.enum_item_name = enum_item_name_ops,
|
||||
.get_enum_item = get_enum_item_ops,
|
||||
.set_enum_item = set_enum_item_ops
|
||||
@ -1287,7 +1183,7 @@ static int simple_add1(snd_mixer_class_t *class, const char *name,
|
||||
if (!melem) {
|
||||
simple = calloc(1, sizeof(*simple));
|
||||
if (!simple) {
|
||||
free(id);
|
||||
snd_mixer_selem_id_free(id);
|
||||
return -ENOMEM;
|
||||
}
|
||||
simple->selem.id = id;
|
||||
@ -1296,14 +1192,14 @@ static int simple_add1(snd_mixer_class_t *class, const char *name,
|
||||
get_compare_weight(snd_mixer_selem_id_get_name(simple->selem.id), snd_mixer_selem_id_get_index(simple->selem.id)),
|
||||
simple, selem_free);
|
||||
if (err < 0) {
|
||||
free(id);
|
||||
snd_mixer_selem_id_free(id);
|
||||
free(simple);
|
||||
return err;
|
||||
}
|
||||
new = 1;
|
||||
} else {
|
||||
simple = snd_mixer_elem_get_private(melem);
|
||||
free(id);
|
||||
snd_mixer_selem_id_free(id);
|
||||
}
|
||||
if (simple->ctls[type].elem) {
|
||||
SNDERR("helem (%s,'%s',%li,%li,%li) appears twice or more",
|
||||
|
Loading…
Reference in New Issue
Block a user