]> git.alsa-project.org Git - alsa-lib.git/commitdiff
more simple mixer - basic abstraction - work
authorJaroslav Kysela <perex@perex.cz>
Thu, 16 Jun 2005 11:59:26 +0000 (11:59 +0000)
committerJaroslav Kysela <perex@perex.cz>
Thu, 16 Jun 2005 11:59:26 +0000 (11:59 +0000)
- 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
include/mixer_abst.h
src/Versions
src/mixer/mixer.c
src/mixer/simple.c
src/mixer/simple/ac97.c
src/mixer/simple_abst.c
src/mixer/simple_none.c

index a5d8ef157cc1dc7c8ccaf93b911cfd1b0084484f..caa12a943932f6d8d094c7d00a6140be98c52473 100644 (file)
@@ -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);
index 90acdb6acd9c9b5dbee986d3678d1984261c9aa1..9e50f5300cf93430e3fc8a50bded7606a0030966 100644 (file)
@@ -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
index f9d5f81a23d428e6bb75c498af25a6d8d7114f7a..030530c448b3efa17756ea77d62885ee03919cb3 100644 (file)
@@ -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;
index 88c3947531300e5821456287d859794215be24ef..332982319109e3a400bcfd6e00cea9c958869a8a 100644 (file)
@@ -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
index 5041a48b0fb9f5c8f208f93154783773a38f0459..59507eda09f3412fde7c9144a978854fac278bdf 100644 (file)
@@ -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);
 }
 
index e10da33b3a4f01d13389f28f693dffb46065c098..daff3cbddb38ac66773fe5bd1d27183e73017519 100644 (file)
 #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;
 }
-                                 
\ No newline at end of file
index 13a5958594920e17b76bc9ee287f7c3d315b06a3..5f6567dc539c78568513758de38a7d2ad0ec0eb1 100644 (file)
@@ -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;
+}
index 5f5801bde6aea9f5955ae3abea7973ba03d3e3df..f8a63760dea5c4586e0e25671346c9795508ad94 100644 (file)
@@ -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",