OSDN Git Service

The simple mixer abstraction - more work
authorJaroslav Kysela <perex@perex.cz>
Sun, 31 Jul 2005 09:08:43 +0000 (09:08 +0000)
committerJaroslav Kysela <perex@perex.cz>
Sun, 31 Jul 2005 09:08:43 +0000 (09:08 +0000)
- separated the "base library" from ac97.c (dynamically loaded)
  - added necessary handling of all private structures
  - added selector and sid registration functions and handling code
- added basic code for hda.c (yeah, my notebook has this hw)

src/Versions
src/conf/smixer.conf
src/mixer/simple/Makefile.am
src/mixer/simple/ac97.c
src/mixer/simple/hda.c [new file with mode: 0644]
src/mixer/simple/sbase.c [new file with mode: 0644]
src/mixer/simple/sbase.h [new file with mode: 0644]
src/mixer/simple/sbasedl.c [new file with mode: 0644]
src/mixer/simple_abst.c

index 030530c..c85c246 100644 (file)
@@ -256,6 +256,9 @@ ALSA_1.0.10 {
     snd_mixer_selem_set_capture_dB_all;
     snd_mixer_selem_compare;
     snd_mixer_sbasic_info;
+    snd_mixer_sbasic_get_private;
+    snd_mixer_sbasic_set_private;
+    snd_mixer_sbasic_set_private_free;
 
     snd_ctl_ext_create;
     snd_ctl_ext_delete;
index 000e8f0..53c44b6 100644 (file)
@@ -6,3 +6,7 @@ ac97 {
        searchl "AC97a:"
        lib smixer-ac97.so
 }
+hda {
+       searchl "HDA:"
+       lib smixer-hda.so
+}
index c4fee41..aec4afd 100644 (file)
@@ -2,7 +2,15 @@ pkglibdir = $(libdir)/@PACKAGE@/smixer
 
 AM_CFLAGS = -g -O2 -W -Wall
 
-pkglib_LTLIBRARIES = smixer-ac97.la
+pkglib_LTLIBRARIES = smixer-sbase.la \
+                    smixer-ac97.la \
+                    smixer-hda.la
 
-smixer_ac97_la_SOURCES = ac97.c
+smixer_sbase_la_SOURCES = sbase.c
+smixer_sbase_la_LDFLAGS = -module
+
+smixer_ac97_la_SOURCES = ac97.c sbasedl.c
 smixer_ac97_la_LDFLAGS = -module
+
+smixer_hda_la_SOURCES = hda.c sbasedl.c
+smixer_hda_la_LDFLAGS = -module
index 760801e..9646b1b 100644 (file)
 #include <math.h>
 #include "asoundlib.h"
 #include "mixer_abst.h"
-#include "list.h"
+#include "sbase.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];
-};
+static struct sm_elem_ops simple_ac97_ops;
 
 struct melem_sids sids[] = {
        {
+               .sid    = SID_MASTER,
                .sname  = "Master",
                .sindex = 0,
                .weight = 1,
                .chanmap = { 3, 0 },
+               .sops = &simple_ac97_ops,
        }
 };
 
-#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[] = {
@@ -84,463 +64,26 @@ struct helem_selector selectors[] = {
        }
 };
 
-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)
+int alsa_mixer_simple_event(snd_mixer_class_t *class, unsigned int mask,
+                           snd_hctl_elem_t *helem, snd_mixer_elem_t *melem)
 {
-       return -ENXIO;
+       struct bclass_private *priv = snd_mixer_sbasic_get_private(class);
+       return priv->ops.event(class, mask, helem, melem);
 }
 
-static int set_range_ops(snd_mixer_elem_t *elem, int dir,
-                        long min, long max)
+int alsa_mixer_simple_init(snd_mixer_class_t *class)
 {
-       struct selem_ac97 *s = snd_mixer_elem_get_private(elem);
+       struct bclass_base_ops *ops;
        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);
+       err = mixer_simple_basic_dlopen(class, &ops);
        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);
+               return 0;
+       err = ops->selreg(class, selectors, SELECTORS);
        if (err < 0)
                return err;
-       err = selem_read(melem);
+       err = ops->sidreg(class, sids, SELECTORS);
        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)
-{
-       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;
 }
diff --git a/src/mixer/simple/hda.c b/src/mixer/simple/hda.c
new file mode 100644 (file)
index 0000000..7f79cec
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+ *  Mixer Interface - HDA simple abstact module
+ *  Copyright (c) 2005 by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ *   This library is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU Lesser General Public License as
+ *   published by the Free Software Foundation; either version 2.1 of
+ *   the License, or (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU Lesser General Public License for more details.
+ *
+ *   You should have received a copy of the GNU Lesser General Public
+ *   License along with this library; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <math.h>
+#include "asoundlib.h"
+#include "mixer_abst.h"
+#include "sbase.h"
+
+static struct sm_elem_ops simple_hda_ops;
+
+struct melem_sids sids[] = {
+       {
+               .sid    = SID_FRONT,
+               .sname  = "Front",
+               .sindex = 0,
+               .weight = 1,
+               .chanmap = { 3, 0 },
+               .sops = &simple_hda_ops,
+       }
+};
+
+#define SELECTORS (sizeof(selectors)/sizeof(selectors[0]))
+
+struct helem_selector selectors[] = {
+       {
+               .iface =        SND_CTL_ELEM_IFACE_MIXER,
+               .name =         "Front Playback Volume",
+               .index =        0,
+               .sid =          SID_FRONT,
+               .purpose =      PURPOSE_VOLUME,
+               .caps =         SM_CAP_PVOLUME,
+       },
+       {
+               .iface =        SND_CTL_ELEM_IFACE_MIXER,
+               .name =         "Front Playback Switch",
+               .index =        0,
+               .sid =          SID_FRONT,
+               .purpose =      PURPOSE_SWITCH,
+               .caps =         SM_CAP_PSWITCH,
+       }
+};
+
+int alsa_mixer_simple_event(snd_mixer_class_t *class, unsigned int mask,
+                           snd_hctl_elem_t *helem, snd_mixer_elem_t *melem)
+{
+       struct bclass_private *priv = snd_mixer_sbasic_get_private(class);
+       return priv->ops.event(class, mask, helem, melem);
+}
+
+int alsa_mixer_simple_init(snd_mixer_class_t *class)
+{
+       struct bclass_base_ops *ops;
+       int err;
+       
+       err = mixer_simple_basic_dlopen(class, &ops);
+       if (err < 0)
+               return 0;
+       err = ops->selreg(class, selectors, SELECTORS);
+       if (err < 0)
+               return err;
+       err = ops->sidreg(class, sids, SELECTORS);
+       if (err < 0)
+               return err;
+       return 0;
+}
diff --git a/src/mixer/simple/sbase.c b/src/mixer/simple/sbase.c
new file mode 100644 (file)
index 0000000..0c804b3
--- /dev/null
@@ -0,0 +1,579 @@
+/*
+ *  Mixer Interface - simple abstact module - base library
+ *  Copyright (c) 2005 by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ *   This library is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU Lesser General Public License as
+ *   published by the Free Software Foundation; either version 2.1 of
+ *   the License, or (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU Lesser General Public License for more details.
+ *
+ *   You should have received a copy of the GNU Lesser General Public
+ *   License along with this library; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <math.h>
+#include "asoundlib.h"
+#include "mixer_abst.h"
+#include "sbase.h"
+
+/*
+ * 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_base *s, int dir, struct helem_base *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_base *s, int dir, struct helem_base *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_base *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_base *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_base, 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_base, 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_base *s = snd_mixer_elem_get_private(elem);
+
+       switch (cmd) {
+
+       case SM_OPS_IS_ACTIVE: {
+               struct list_head *pos;
+               struct helem_base *helem;
+               list_for_each(pos, &s->helems) {
+                       helem = list_entry(pos, struct helem_base, 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_base *helem;
+               helem = list_entry(s->helems.next, struct helem_base, list);
+               return !!(helem->purpose == PURPOSE_ENUMLIST);
+       }
+       
+       case SM_OPS_IS_ENUMCNT: {
+               struct helem_base *helem;
+               helem = list_entry(s->helems.next, struct helem_base, list);
+               return helem->max;
+       }
+
+       }
+       
+       return 1;
+}
+
+static int get_range_ops(snd_mixer_elem_t *elem, int dir,
+                        long *min, long *max)
+{
+       struct selem_base *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_base *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_base *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_base *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_base *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_base *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_base *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_base *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_base *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_base *simple = snd_mixer_elem_get_private(elem);
+       struct helem_base *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_base, list);
+               free(hsimple);
+       }
+       free(simple);
+}
+
+static int simple_event_add1(snd_mixer_class_t *class,
+                            snd_hctl_elem_t *helem,
+                            struct helem_selector *sel)
+{
+       struct bclass_private *priv = snd_mixer_sbasic_get_private(class);
+       int count;
+       snd_mixer_elem_t *melem;
+       snd_mixer_selem_id_t *id;
+       snd_ctl_elem_info_t *info;
+       struct selem_base *simple;
+       struct helem_base *hsimple;
+       snd_ctl_elem_type_t ctype;
+       unsigned long values;
+       long min, max;
+       int err, new = 0;
+       struct list_head *pos;
+       struct bclass_sid *bsid;
+       struct melem_sids *sid;
+       unsigned int ui;
+       
+       list_for_each(pos, &priv->sids) {
+               bsid = list_entry(pos, struct bclass_sid, list);
+               for (ui = 0; ui < bsid->count; ui++) {
+                       if (bsid->sids[ui].sid == sel->sid) {
+                               sid = &bsid->sids[ui];
+                               goto __sid_ok;
+                       }
+               }
+       }
+       return 0;
+
+      __sid_ok:
+       snd_ctl_elem_info_alloca(&info);
+       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, snd_hctl_elem_get_name(helem));
+       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, sid->sname);
+       snd_mixer_selem_id_set_index(id, 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,
+                                        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 |= sid->chanmap[0];
+       simple->dir[1].chanmap |= 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;
+}
+
+static int simple_event_add(snd_mixer_class_t *class, snd_hctl_elem_t *helem)
+{
+       struct bclass_private *priv = snd_mixer_sbasic_get_private(class);
+       struct bclass_selector *sel;
+       struct helem_selector *hsel;
+       struct list_head *pos;
+       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);
+       unsigned int ui;
+       int err;
+
+       list_for_each(pos, &priv->selectors) {
+               sel = list_entry(pos, struct bclass_selector, list);
+               for (ui = 0; ui < sel->count; ui++) {
+                       hsel = &sel->selectors[ui];
+                       if (hsel->iface == iface && !strcmp(hsel->name, name) && hsel->index == index) {
+                               err = simple_event_add1(class, helem, hsel);
+                               if (err < 0)
+                                       return err;     /* early exit? */
+                       }
+               }
+       }
+       return 0;
+}
+
+int alsa_mixer_sbasic_event(snd_mixer_class_t *class, unsigned int mask,
+                           snd_hctl_elem_t *helem, snd_mixer_elem_t *melem)
+{
+       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;
+}
+
+static void sbasic_cpriv_free(snd_mixer_class_t *class)
+{
+       struct bclass_private *priv = snd_mixer_sbasic_get_private(class);
+       struct bclass_selector *sel;
+       struct bclass_sid *sid;
+       struct list_head *pos, *pos1;
+
+       list_for_each_safe(pos, pos1, &priv->selectors) {
+               sel = list_entry(pos, struct bclass_selector, list);
+               free(sel);
+       }
+       list_for_each_safe(pos, pos1, &priv->sids) {
+               sid = list_entry(pos, struct bclass_sid, list);
+               free(sid);
+       }
+       free(priv);
+}
+
+void alsa_mixer_sbasic_initpriv(snd_mixer_class_t *class,
+                               struct bclass_private *priv)
+{
+       INIT_LIST_HEAD(&priv->selectors);
+       INIT_LIST_HEAD(&priv->sids);
+       snd_mixer_sbasic_set_private(class, priv);
+       snd_mixer_sbasic_set_private_free(class, sbasic_cpriv_free);
+}
+
+int alsa_mixer_sbasic_selreg(snd_mixer_class_t *class,
+                            struct helem_selector *selectors,
+                            unsigned int count)
+{
+       struct bclass_private *priv = snd_mixer_sbasic_get_private(class);
+       struct bclass_selector *sel = calloc(1, sizeof(*sel));
+
+       if (sel == NULL)
+               return -ENOMEM;
+       if (priv == NULL) {
+               priv = calloc(1, sizeof(*priv));
+               if (priv == NULL) {
+                       free(sel);
+                       return -ENOMEM;
+               }
+       }
+       sel->selectors = selectors;
+       sel->count = count;
+       list_add_tail(&sel->list, &priv->selectors);
+       return 0;
+}
+
+int alsa_mixer_sbasic_sidreg(snd_mixer_class_t *class,
+                            struct melem_sids *sids,
+                            unsigned int count)
+{
+       struct bclass_private *priv = snd_mixer_sbasic_get_private(class);
+       struct bclass_sid *sid = calloc(1, sizeof(*sid));
+
+       if (sid == NULL)
+               return -ENOMEM;
+       if (priv == NULL) {
+               priv = calloc(1, sizeof(*priv));
+               if (priv == NULL) {
+                       free(sid);
+                       return -ENOMEM;
+               }
+               INIT_LIST_HEAD(&priv->selectors);
+               INIT_LIST_HEAD(&priv->sids);
+               snd_mixer_sbasic_set_private(class, priv);
+               snd_mixer_sbasic_set_private_free(class, sbasic_cpriv_free);
+               }
+       sid->sids = sids;
+       sid->count = count;
+       list_add(&sid->list, &priv->sids);
+       return 0;
+}
diff --git a/src/mixer/simple/sbase.h b/src/mixer/simple/sbase.h
new file mode 100644 (file)
index 0000000..f00688f
--- /dev/null
@@ -0,0 +1,111 @@
+/*
+ *  Mixer Interface - simple abstact module - base library
+ *  Copyright (c) 2005 by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ *   This library is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU Lesser General Public License as
+ *   published by the Free Software Foundation; either version 2.1 of
+ *   the License, or (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU Lesser General Public License for more details.
+ *
+ *   You should have received a copy of the GNU Lesser General Public
+ *   License along with this library; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+#ifndef __SMIXER_BASE_H
+
+#include "list.h"
+
+#define MAX_CHANNEL    6
+
+#define SID_MASTER     0
+#define SID_HEADPHONE  1
+#define SID_FRONT      2
+#define SID_PCM                3
+#define SID_CD         4
+
+struct melem_sids {
+       unsigned short sid;
+       const char *sname;
+       unsigned short sindex;
+       unsigned short weight;
+       unsigned int chanmap[2];
+       struct sm_elem_ops *sops;
+};
+
+#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;
+};
+
+struct helem_base {
+       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_base {
+       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];
+};
+
+struct bclass_selector {
+       struct list_head list;
+       struct helem_selector *selectors;
+       unsigned int count;
+};
+
+struct bclass_sid {
+       struct list_head list;
+       struct melem_sids *sids;
+       unsigned int count;
+};
+
+typedef struct bclass_base_ops {
+       int (*event)(snd_mixer_class_t *class, unsigned int mask,
+                    snd_hctl_elem_t *helem, snd_mixer_elem_t *melem);
+       int (*selreg)(snd_mixer_class_t *class,
+                     struct helem_selector *selectors,
+                     unsigned int count);
+       int (*sidreg)(snd_mixer_class_t *class,
+                     struct melem_sids *sids,
+                     unsigned int count);
+} bclass_base_ops_t;
+
+struct bclass_private {
+       struct list_head selectors;
+       struct list_head sids;
+       void *dl_sbase;
+       bclass_base_ops_t ops;
+};
+
+int mixer_simple_basic_dlopen(snd_mixer_class_t *class,
+                             bclass_base_ops_t **ops);
+
+#endif /* __SMIXER_BASE_H */
diff --git a/src/mixer/simple/sbasedl.c b/src/mixer/simple/sbasedl.c
new file mode 100644 (file)
index 0000000..8000183
--- /dev/null
@@ -0,0 +1,106 @@
+/*
+ *  Mixer Interface - simple abstact module - base library (dlopen function)
+ *  Copyright (c) 2005 by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ *   This library is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU Lesser General Public License as
+ *   published by the Free Software Foundation; either version 2.1 of
+ *   the License, or (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU Lesser General Public License for more details.
+ *
+ *   You should have received a copy of the GNU Lesser General Public
+ *   License along with this library; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <math.h>
+#include <dlfcn.h>
+#include "config.h"
+#include "asoundlib.h"
+#include "mixer_abst.h"
+#include "sbase.h"
+
+#define SO_PATH PKGLIBDIR "/smixer"
+
+int mixer_simple_basic_dlopen(snd_mixer_class_t *class,
+                             bclass_base_ops_t **ops)
+{
+       struct bclass_private *priv = snd_mixer_sbasic_get_private(class);
+       const char *lib = "smixer-sbase.so";
+       void (*initpriv)(snd_mixer_class_t *class, struct bclass_private *priv);
+       char *xlib, *path;
+       void *h;
+       int initflag = 0;
+
+       if (priv == NULL) {
+               priv = calloc(1, sizeof(*priv));
+               if (priv == NULL)
+                       return -ENOMEM;
+               initflag = 1;
+       }
+       path = getenv("ALSA_MIXER_SIMPLE_MODULES");
+       if (!path)
+               path = SO_PATH;
+       xlib = malloc(strlen(lib) + strlen(path) + 1 + 1);
+       if (xlib == NULL) {
+               if (initflag)
+                       free(priv);
+               return -ENOMEM;
+       }
+       strcpy(xlib, path);
+       strcat(xlib, "/");
+       strcat(xlib, lib);
+       h = snd_dlopen(xlib, RTLD_NOW);
+       if (h == NULL) {
+               SNDERR("Unable to open library '%s'", xlib);
+               goto __error;
+       }
+       initpriv = dlsym(h, "alsa_mixer_sbasic_initpriv");
+       if (initpriv == NULL) {
+               SNDERR("Symbol 'alsa_mixer_sbasic_initpriv' was not found in '%s'", xlib);
+               goto __error;
+       }
+       priv->ops.event = dlsym(h, "alsa_mixer_sbasic_event");
+       if (priv->ops.event == NULL) {
+               SNDERR("Symbol 'alsa_mixer_sbasic_event' was not found in '%s'", xlib);
+               goto __error;
+       }
+       priv->ops.selreg = dlsym(h, "alsa_mixer_sbasic_selreg");
+       if (priv->ops.selreg == NULL) {
+               SNDERR("Symbol 'alsa_mixer_sbasic_selreg' was not found in '%s'", xlib);
+               goto __error;
+       }
+       priv->ops.sidreg = dlsym(h, "alsa_mixer_sbasic_sidreg");
+       if (priv->ops.sidreg == NULL) {
+               SNDERR("Symbol 'alsa_mixer_sbasic_sidreg' was not found in '%s'", xlib);
+               goto __error;
+       }
+       free(xlib);
+       if (initflag)
+               initpriv(class, priv);
+       priv->dl_sbase = h;
+       if (ops)
+               *ops = &priv->ops;
+       return 1;
+
+      __error:
+       if (initflag)
+               free(priv);
+       if (h == NULL)
+               snd_dlclose(h);
+       free(xlib);
+       return -ENXIO;
+}
index 5f6567d..0a88dc0 100644 (file)
@@ -48,14 +48,20 @@ typedef struct _class_priv {
        int attach_flag;
        snd_ctl_card_info_t *info;
        void *dlhandle;
+       void *private_data;
+       void (*private_free)(snd_mixer_class_t *class);
 } class_priv_t;
 
+typedef int (*snd_mixer_sbasic_init_t)(snd_mixer_class_t *class);
+
 static int try_open(snd_mixer_class_t *class, const char *lib)
 {
        class_priv_t *priv = snd_mixer_class_get_private(class);
        snd_mixer_event_t event_func;
+       snd_mixer_sbasic_init_t init_func;
        char *xlib, *path;
        void *h;
+       int err;
 
        path = getenv("ALSA_MIXER_SIMPLE_MODULES");
        if (!path)
@@ -79,7 +85,19 @@ static int try_open(snd_mixer_class_t *class, const char *lib)
                free(xlib);
                return -ENXIO;
        }
+       init_func = dlsym(h, "alsa_mixer_simple_init");
+       if (init_func == NULL) {
+               SNDERR("Symbol 'alsa_mixer_simple_init' was not found in '%s'", xlib);
+               snd_dlclose(h);
+               free(xlib);
+               return -ENXIO;
+       }
        free(xlib);
+       err = init_func(class);
+       if (err < 0) {
+               snd_dlclose(h);
+               return err;
+       }
        snd_mixer_class_set_event(class, event_func);
        priv->dlhandle = h;
        return 1;
@@ -148,6 +166,8 @@ static void private_free(snd_mixer_class_t *class)
 {
        class_priv_t *priv = snd_mixer_class_get_private(class);
        
+       if (priv->private_free)
+               priv->private_free(class);
        if (priv->dlhandle)
                snd_dlclose(priv->dlhandle);
        if (priv->info)
@@ -256,10 +276,9 @@ int snd_mixer_simple_basic_register(snd_mixer_t *mixer,
 }
 
 /**
- * \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
+ * \brief Basic Mixer Abstraction - Get information about device
+ * \param class Mixer class
+ * \param info Info structure
  * \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)
@@ -274,3 +293,47 @@ int snd_mixer_sbasic_info(const snd_mixer_class_t *class, sm_class_basic_t *info
        info->info = priv->info;
        return 0;
 }
+
+/**
+ * \brief Get private data for basic abstraction
+ * \param class Mixer class
+ * \return private data
+ */
+void *snd_mixer_sbasic_get_private(const snd_mixer_class_t *class)
+{
+       class_priv_t *priv = snd_mixer_class_get_private(class);
+
+       if (class == NULL)
+               return NULL;
+       return priv->private_data;
+}
+
+/**
+ * \brief Set private data for basic abstraction
+ * \param class Mixer class
+ * \param private_data Private data
+ */
+void snd_mixer_sbasic_set_private(const snd_mixer_class_t *class, void *private_data)
+{
+       class_priv_t *priv;
+
+       if (class == NULL)
+               return;
+       priv = snd_mixer_class_get_private(class);
+       priv->private_data = private_data;
+}
+
+/**
+ * \brief Set private data for basic abstraction
+ * \param class Mixer class
+ * \param private_data Private data
+ */
+void snd_mixer_sbasic_set_private_free(const snd_mixer_class_t *class, void (*private_free)(snd_mixer_class_t *class))
+{
+       class_priv_t *priv;
+
+       if (class == NULL)
+               return;
+       priv = snd_mixer_class_get_private(class);
+       priv->private_free = private_free;
+}