From 59a8226184c305edc4a1f8873981f366444ccdbd Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Sun, 31 Jul 2005 09:08:43 +0000 Subject: [PATCH] The simple mixer abstraction - more work - 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 | 3 + src/conf/smixer.conf | 4 + src/mixer/simple/Makefile.am | 12 +- src/mixer/simple/ac97.c | 485 +---------------------------- src/mixer/simple/hda.c | 90 ++++++ src/mixer/simple/sbase.c | 579 +++++++++++++++++++++++++++++++++++ src/mixer/simple/sbase.h | 111 +++++++ src/mixer/simple/sbasedl.c | 106 +++++++ src/mixer/simple_abst.c | 71 ++++- 9 files changed, 984 insertions(+), 477 deletions(-) create mode 100644 src/mixer/simple/hda.c create mode 100644 src/mixer/simple/sbase.c create mode 100644 src/mixer/simple/sbase.h create mode 100644 src/mixer/simple/sbasedl.c diff --git a/src/Versions b/src/Versions index 030530c4..c85c246c 100644 --- a/src/Versions +++ b/src/Versions @@ -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; diff --git a/src/conf/smixer.conf b/src/conf/smixer.conf index 000e8f02..53c44b62 100644 --- a/src/conf/smixer.conf +++ b/src/conf/smixer.conf @@ -6,3 +6,7 @@ ac97 { searchl "AC97a:" lib smixer-ac97.so } +hda { + searchl "HDA:" + lib smixer-hda.so +} diff --git a/src/mixer/simple/Makefile.am b/src/mixer/simple/Makefile.am index c4fee410..aec4afd7 100644 --- a/src/mixer/simple/Makefile.am +++ b/src/mixer/simple/Makefile.am @@ -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 diff --git a/src/mixer/simple/ac97.c b/src/mixer/simple/ac97.c index 760801e5..9646b1bf 100644 --- a/src/mixer/simple/ac97.c +++ b/src/mixer/simple/ac97.c @@ -28,41 +28,21 @@ #include #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 index 00000000..7f79cec2 --- /dev/null +++ b/src/mixer/simple/hda.c @@ -0,0 +1,90 @@ +/* + * Mixer Interface - HDA simple abstact module + * Copyright (c) 2005 by Jaroslav Kysela + * + * + * 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 +#include +#include +#include +#include +#include +#include +#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 index 00000000..0c804b38 --- /dev/null +++ b/src/mixer/simple/sbase.c @@ -0,0 +1,579 @@ +/* + * Mixer Interface - simple abstact module - base library + * Copyright (c) 2005 by Jaroslav Kysela + * + * + * 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 +#include +#include +#include +#include +#include +#include +#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 index 00000000..f00688f4 --- /dev/null +++ b/src/mixer/simple/sbase.h @@ -0,0 +1,111 @@ +/* + * Mixer Interface - simple abstact module - base library + * Copyright (c) 2005 by Jaroslav Kysela + * + * + * 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 index 00000000..80001838 --- /dev/null +++ b/src/mixer/simple/sbasedl.c @@ -0,0 +1,106 @@ +/* + * Mixer Interface - simple abstact module - base library (dlopen function) + * Copyright (c) 2005 by Jaroslav Kysela + * + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#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; +} diff --git a/src/mixer/simple_abst.c b/src/mixer/simple_abst.c index 5f6567dc..0a88dc09 100644 --- a/src/mixer/simple_abst.c +++ b/src/mixer/simple_abst.c @@ -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; +} -- 2.47.1