From ce67d5389bbcef1dbe4b6c1643be23ee767c30bd Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Thu, 16 Jun 2005 11:59:26 +0000 Subject: [PATCH] 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 --- include/mixer.h | 20 +- include/mixer_abst.h | 12 +- src/Versions | 3 + src/mixer/mixer.c | 53 ++++- src/mixer/simple.c | 379 +++++++++++++++++------------ src/mixer/simple/ac97.c | 514 +++++++++++++++++++++++++++++++++++++++- src/mixer/simple_abst.c | 45 +++- src/mixer/simple_none.c | 126 +--------- 8 files changed, 855 insertions(+), 297 deletions(-) diff --git a/include/mixer.h b/include/mixer.h index a5d8ef15..caa12a94 100644 --- a/include/mixer.h +++ b/include/mixer.h @@ -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); diff --git a/include/mixer_abst.h b/include/mixer_abst.h index 90acdb6a..9e50f530 100644 --- a/include/mixer_abst.h +++ b/include/mixer_abst.h @@ -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 diff --git a/src/Versions b/src/Versions index f9d5f81a..030530c4 100644 --- a/src/Versions +++ b/src/Versions @@ -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; diff --git a/src/mixer/mixer.c b/src/mixer/mixer.c index 88c39475..33298231 100644 --- a/src/mixer/mixer.c +++ b/src/mixer/mixer.c @@ -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; - slave = calloc(1, sizeof(*slave)); - if (slave == NULL) - return -ENOMEM; + err = snd_hctl_open(&hctl, name, 0); + if (err < 0) + return err; + err = snd_mixer_attach_hctl(mixer, hctl); if (err < 0) { - free(slave); + 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_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 diff --git a/src/mixer/simple.c b/src/mixer/simple.c index 5041a48b..59507eda 100644 --- a/src/mixer/simple.c +++ b/src/mixer/simple.c @@ -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); } diff --git a/src/mixer/simple/ac97.c b/src/mixer/simple/ac97.c index e10da33b..daff3cbd 100644 --- a/src/mixer/simple/ac97.c +++ b/src/mixer/simple/ac97.c @@ -27,10 +27,520 @@ #include #include #include +#include +#include + +#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; } - \ No newline at end of file diff --git a/src/mixer/simple_abst.c b/src/mixer/simple_abst.c index 13a59585..5f6567dc 100644 --- a/src/mixer/simple_abst.c +++ b/src/mixer/simple_abst.c @@ -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; +} diff --git a/src/mixer/simple_none.c b/src/mixer/simple_none.c index 5f5801bd..f8a63760 100644 --- a/src/mixer/simple_none.c +++ b/src/mixer/simple_none.c @@ -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", -- 2.47.1