Home | History | Annotate | Download | only in simple
      1 /*
      2  *  Mixer Interface - simple abstact module - base library
      3  *  Copyright (c) 2005 by Jaroslav Kysela <perex (at) perex.cz>
      4  *
      5  *
      6  *   This library is free software; you can redistribute it and/or modify
      7  *   it under the terms of the GNU Lesser General Public License as
      8  *   published by the Free Software Foundation; either version 2.1 of
      9  *   the License, or (at your option) any later version.
     10  *
     11  *   This program is distributed in the hope that it will be useful,
     12  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
     13  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     14  *   GNU Lesser General Public License for more details.
     15  *
     16  *   You should have received a copy of the GNU Lesser General Public
     17  *   License along with this library; if not, write to the Free Software
     18  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
     19  *
     20  */
     21 
     22 #include <stdio.h>
     23 #include <stdlib.h>
     24 #include <unistd.h>
     25 #include <string.h>
     26 #include <fcntl.h>
     27 #include <sys/ioctl.h>
     28 #include <math.h>
     29 #include "asoundlib.h"
     30 #include "mixer_abst.h"
     31 #include "sbase.h"
     32 
     33 /*
     34  * Prototypes
     35  */
     36 
     37 static int selem_read(snd_mixer_elem_t *elem);
     38 
     39 /*
     40  * Helpers
     41  */
     42 
     43 static unsigned int chanmap_to_channels(unsigned int chanmap)
     44 {
     45 	unsigned int i, res;
     46 
     47 	for (i = 0, res = 0; i < MAX_CHANNEL; i++)
     48 		if (chanmap & (1 << i))
     49 			res++;
     50 	return res;
     51 }
     52 
     53 #if 0
     54 static long to_user(struct selem_base *s, int dir, struct helem_base *c, long value)
     55 {
     56 	int64_t n;
     57 	if (c->max == c->min)
     58 		return s->dir[dir].min;
     59 	n = (int64_t) (value - c->min) * (s->dir[dir].max - s->dir[dir].min);
     60 	return s->dir[dir].min + (n + (c->max - c->min) / 2) / (c->max - c->min);
     61 }
     62 
     63 static long from_user(struct selem_base *s, int dir, struct helem_base *c, long value)
     64 {
     65         int64_t n;
     66 	if (s->dir[dir].max == s->dir[dir].min)
     67 		return c->min;
     68         n = (int64_t) (value - s->dir[dir].min) * (c->max - c->min);
     69 	return c->min + (n + (s->dir[dir].max - s->dir[dir].min) / 2) / (s->dir[dir].max - s->dir[dir].min);
     70 }
     71 #endif
     72 
     73 static void update_ranges(struct selem_base *s)
     74 {
     75 	static unsigned int mask[2] = { SM_CAP_PVOLUME, SM_CAP_CVOLUME };
     76 	static unsigned int gmask[2] = { SM_CAP_GVOLUME, SM_CAP_GVOLUME };
     77 	unsigned int dir, ok_flag;
     78 	struct list_head *pos;
     79 	struct helem_base *helem;
     80 
     81 	for (dir = 0; dir < 2; dir++) {
     82 		s->dir[dir].min = 0;
     83 		s->dir[dir].max = 0;
     84 		ok_flag = 0;
     85 		list_for_each(pos, &s->helems) {
     86 			helem = list_entry(pos, struct helem_base, list);
     87 			printf("min = %li, max = %li\n", helem->min, helem->max);
     88 			if (helem->caps & mask[dir]) {
     89 				s->dir[dir].min = helem->min;
     90 				s->dir[dir].max = helem->max;
     91 				ok_flag = 1;
     92 				break;
     93 			}
     94 		}
     95 		if (ok_flag)
     96 			continue;
     97 		list_for_each(pos, &s->helems) {
     98 			helem = list_entry(pos, struct helem_base, list);
     99 			if (helem->caps & gmask[dir]) {
    100 				s->dir[dir].min = helem->min;
    101 				s->dir[dir].max = helem->max;
    102 				break;
    103 			}
    104 		}
    105 	}
    106 }
    107 
    108 /*
    109  * Simple Mixer Operations
    110  */
    111 
    112 static int is_ops(snd_mixer_elem_t *elem, int dir, int cmd, int val)
    113 {
    114 	struct selem_base *s = snd_mixer_elem_get_private(elem);
    115 
    116 	switch (cmd) {
    117 
    118 	case SM_OPS_IS_ACTIVE: {
    119 		struct list_head *pos;
    120 		struct helem_base *helem;
    121 		list_for_each(pos, &s->helems) {
    122 			helem = list_entry(pos, struct helem_base, list);
    123 			if (helem->inactive)
    124 				return 0;
    125 		}
    126 		return 1;
    127 	}
    128 
    129 	case SM_OPS_IS_MONO:
    130 		return chanmap_to_channels(s->dir[dir].chanmap) == 1;
    131 
    132 	case SM_OPS_IS_CHANNEL:
    133 		if (val > MAX_CHANNEL)
    134 			return 0;
    135 		return !!((1 << val) & s->dir[dir].chanmap);
    136 
    137 	case SM_OPS_IS_ENUMERATED: {
    138 		struct helem_base *helem;
    139 		helem = list_entry(s->helems.next, struct helem_base, list);
    140 		return !!(helem->purpose == PURPOSE_ENUMLIST);
    141 	}
    142 
    143 	case SM_OPS_IS_ENUMCNT: {
    144 		struct helem_base *helem;
    145 		helem = list_entry(s->helems.next, struct helem_base, list);
    146 		return helem->max;
    147 	}
    148 
    149 	}
    150 
    151 	return 1;
    152 }
    153 
    154 static int get_range_ops(snd_mixer_elem_t *elem, int dir,
    155 			 long *min, long *max)
    156 {
    157 	struct selem_base *s = snd_mixer_elem_get_private(elem);
    158 
    159 	*min = s->dir[dir].min;
    160 	*max = s->dir[dir].max;
    161 
    162 	return 0;
    163 }
    164 
    165 static int get_dB_range_ops(snd_mixer_elem_t *elem ATTRIBUTE_UNUSED,
    166 			    int dir ATTRIBUTE_UNUSED,
    167 			    long *min ATTRIBUTE_UNUSED,
    168 			    long *max ATTRIBUTE_UNUSED)
    169 {
    170 	return -ENXIO;
    171 }
    172 
    173 static int set_range_ops(snd_mixer_elem_t *elem, int dir,
    174 			 long min, long max)
    175 {
    176 	struct selem_base *s = snd_mixer_elem_get_private(elem);
    177 	int err;
    178 
    179 	s->dir[dir].forced_range = 1;
    180 	s->dir[dir].min = min;
    181 	s->dir[dir].max = max;
    182 
    183 	if ((err = selem_read(elem)) < 0)
    184 		return err;
    185 	return 0;
    186 }
    187 
    188 static int get_volume_ops(snd_mixer_elem_t *elem, int dir,
    189 			  snd_mixer_selem_channel_id_t channel, long *value)
    190 {
    191 	struct selem_base *s = snd_mixer_elem_get_private(elem);
    192 
    193 	*value = s->dir[dir].vol[channel];
    194 	return 0;
    195 }
    196 
    197 static int get_dB_ops(snd_mixer_elem_t *elem ATTRIBUTE_UNUSED,
    198 		      int dir ATTRIBUTE_UNUSED,
    199 		      snd_mixer_selem_channel_id_t channel ATTRIBUTE_UNUSED,
    200 		      long *value ATTRIBUTE_UNUSED)
    201 {
    202 	return -ENXIO;
    203 }
    204 
    205 static int get_switch_ops(snd_mixer_elem_t *elem ATTRIBUTE_UNUSED,
    206 			  int dir ATTRIBUTE_UNUSED,
    207 			  snd_mixer_selem_channel_id_t channel ATTRIBUTE_UNUSED,
    208 			  int *value)
    209 {
    210 	/* struct selem_base *s = snd_mixer_elem_get_private(elem); */
    211 	*value = 0;
    212 	return 0;
    213 }
    214 
    215 static int set_volume_ops(snd_mixer_elem_t *elem ATTRIBUTE_UNUSED,
    216 			  int dir ATTRIBUTE_UNUSED,
    217 			  snd_mixer_selem_channel_id_t channel ATTRIBUTE_UNUSED,
    218 			  long value ATTRIBUTE_UNUSED)
    219 {
    220 	/* struct selem_base *s = snd_mixer_elem_get_private(elem); */
    221 	return 0;
    222 }
    223 
    224 static int set_dB_ops(snd_mixer_elem_t *elem ATTRIBUTE_UNUSED,
    225 		      int dir ATTRIBUTE_UNUSED,
    226 		      snd_mixer_selem_channel_id_t channel ATTRIBUTE_UNUSED,
    227 		      long value ATTRIBUTE_UNUSED,
    228 		      int xdir ATTRIBUTE_UNUSED)
    229 {
    230 	return -ENXIO;
    231 }
    232 
    233 static int set_switch_ops(snd_mixer_elem_t *elem ATTRIBUTE_UNUSED,
    234 			  int dir ATTRIBUTE_UNUSED,
    235 			  snd_mixer_selem_channel_id_t channel ATTRIBUTE_UNUSED,
    236 			  int value ATTRIBUTE_UNUSED)
    237 {
    238 	/* struct selem_base *s = snd_mixer_elem_get_private(elem); */
    239 	/* int changed; */
    240 	return 0;
    241 }
    242 
    243 static int enum_item_name_ops(snd_mixer_elem_t *elem ATTRIBUTE_UNUSED,
    244 			      unsigned int item ATTRIBUTE_UNUSED,
    245 			      size_t maxlen ATTRIBUTE_UNUSED,
    246 			      char *buf ATTRIBUTE_UNUSED)
    247 {
    248 	/* struct selem_base *s = snd_mixer_elem_get_private(elem);*/
    249 	return 0;
    250 }
    251 
    252 static int get_enum_item_ops(snd_mixer_elem_t *elem ATTRIBUTE_UNUSED,
    253 			     snd_mixer_selem_channel_id_t channel ATTRIBUTE_UNUSED,
    254 			     unsigned int *itemp ATTRIBUTE_UNUSED)
    255 {
    256 	/* struct selem_base *s = snd_mixer_elem_get_private(elem); */
    257 	return 0;
    258 }
    259 
    260 static int set_enum_item_ops(snd_mixer_elem_t *elem ATTRIBUTE_UNUSED,
    261 			     snd_mixer_selem_channel_id_t channel ATTRIBUTE_UNUSED,
    262 			     unsigned int item ATTRIBUTE_UNUSED)
    263 {
    264 	/* struct selem_base *s = snd_mixer_elem_get_private(elem); */
    265 	return 0;
    266 }
    267 
    268 static struct sm_elem_ops simple_ac97_ops = {
    269 	.is		= is_ops,
    270 	.get_range	= get_range_ops,
    271 	.get_dB_range	= get_dB_range_ops,
    272 	.set_range	= set_range_ops,
    273 	.get_volume	= get_volume_ops,
    274 	.get_dB		= get_dB_ops,
    275 	.set_volume	= set_volume_ops,
    276 	.set_dB		= set_dB_ops,
    277 	.get_switch	= get_switch_ops,
    278 	.set_switch	= set_switch_ops,
    279 	.enum_item_name	= enum_item_name_ops,
    280 	.get_enum_item	= get_enum_item_ops,
    281 	.set_enum_item	= set_enum_item_ops
    282 };
    283 
    284 /*
    285  * event handling
    286  */
    287 
    288 static int selem_read(snd_mixer_elem_t *elem)
    289 {
    290 	printf("elem read: %p\n", elem);
    291 	return 0;
    292 }
    293 
    294 static int simple_event_remove(snd_hctl_elem_t *helem,
    295 			       snd_mixer_elem_t *melem ATTRIBUTE_UNUSED)
    296 {
    297 	printf("event remove: %p\n", helem);
    298 	return 0;
    299 }
    300 
    301 static void selem_free(snd_mixer_elem_t *elem)
    302 {
    303 	struct selem_base *simple = snd_mixer_elem_get_private(elem);
    304 	struct helem_base *hsimple;
    305 	struct list_head *pos, *npos;
    306 
    307 	if (simple->selem.id)
    308 		snd_mixer_selem_id_free(simple->selem.id);
    309 	list_for_each_safe(pos, npos, &simple->helems) {
    310 		hsimple = list_entry(pos, struct helem_base, list);
    311 		free(hsimple);
    312 	}
    313 	free(simple);
    314 }
    315 
    316 static int simple_event_add1(snd_mixer_class_t *class,
    317 			     snd_hctl_elem_t *helem,
    318 			     struct helem_selector *sel)
    319 {
    320 	struct bclass_private *priv = snd_mixer_sbasic_get_private(class);
    321 	snd_mixer_elem_t *melem;
    322 	snd_mixer_selem_id_t *id;
    323 	snd_ctl_elem_info_t *info;
    324 	struct selem_base *simple;
    325 	struct helem_base *hsimple;
    326 	snd_ctl_elem_type_t ctype;
    327 	unsigned long values;
    328 	long min, max;
    329 	int err, new = 0;
    330 	struct list_head *pos;
    331 	struct bclass_sid *bsid;
    332 	struct melem_sids *sid;
    333 	unsigned int ui;
    334 
    335 	list_for_each(pos, &priv->sids) {
    336 		bsid = list_entry(pos, struct bclass_sid, list);
    337 		for (ui = 0; ui < bsid->count; ui++) {
    338 			if (bsid->sids[ui].sid == sel->sid) {
    339 				sid = &bsid->sids[ui];
    340 				goto __sid_ok;
    341 			}
    342 		}
    343 	}
    344 	return 0;
    345 
    346       __sid_ok:
    347 	snd_ctl_elem_info_alloca(&info);
    348 	err = snd_hctl_elem_info(helem, info);
    349 	if (err < 0)
    350 		return err;
    351 	ctype = snd_ctl_elem_info_get_type(info);
    352 	values = snd_ctl_elem_info_get_count(info);
    353 	switch (ctype) {
    354 	case SND_CTL_ELEM_TYPE_ENUMERATED:
    355 		min = 0;
    356 		max = snd_ctl_elem_info_get_items(info);
    357 		break;
    358 	case SND_CTL_ELEM_TYPE_INTEGER:
    359 		min = snd_ctl_elem_info_get_min(info);
    360 		max = snd_ctl_elem_info_get_max(info);
    361 		break;
    362 	default:
    363 		min = max = 0;
    364 		break;
    365 	}
    366 
    367 	printf("event add: %p, %p (%s)\n", helem, sel, snd_hctl_elem_get_name(helem));
    368 	if (snd_mixer_selem_id_malloc(&id))
    369 		return -ENOMEM;
    370 	hsimple = calloc(1, sizeof(*hsimple));
    371 	if (hsimple == NULL) {
    372 		snd_mixer_selem_id_free(id);
    373 		return -ENOMEM;
    374 	}
    375 	switch (sel->purpose) {
    376 	case PURPOSE_SWITCH:
    377 		if (ctype != SND_CTL_ELEM_TYPE_BOOLEAN) {
    378 		      __invalid_type:
    379 		      	snd_mixer_selem_id_free(id);
    380 			return -EINVAL;
    381 		}
    382 		break;
    383 	case PURPOSE_VOLUME:
    384 		if (ctype != SND_CTL_ELEM_TYPE_INTEGER)
    385 			goto __invalid_type;
    386 		break;
    387 	}
    388 	hsimple->purpose = sel->purpose;
    389 	hsimple->caps = sel->caps;
    390 	hsimple->min = min;
    391 	hsimple->max = max;
    392 	snd_mixer_selem_id_set_name(id, sid->sname);
    393 	snd_mixer_selem_id_set_index(id, sid->sindex);
    394 	melem = snd_mixer_find_selem(snd_mixer_class_get_mixer(class), id);
    395 	if (!melem) {
    396 		simple = calloc(1, sizeof(*simple));
    397 		if (!simple) {
    398 			snd_mixer_selem_id_free(id);
    399 			free(hsimple);
    400 			return -ENOMEM;
    401 		}
    402 		simple->selem.id = id;
    403 		simple->selem.ops = &simple_ac97_ops;
    404 		INIT_LIST_HEAD(&simple->helems);
    405 		simple->sid = sel->sid;
    406 		err = snd_mixer_elem_new(&melem, SND_MIXER_ELEM_SIMPLE,
    407 					 sid->weight,
    408 					 simple, selem_free);
    409 		if (err < 0) {
    410 			snd_mixer_selem_id_free(id);
    411 			free(hsimple);
    412 			free(simple);
    413 			return err;
    414 		}
    415 		new = 1;
    416 	} else {
    417 		simple = snd_mixer_elem_get_private(melem);
    418 		snd_mixer_selem_id_free(id);
    419 	}
    420 	list_add_tail(&hsimple->list, &simple->helems);
    421 	hsimple->inactive = snd_ctl_elem_info_is_inactive(info);
    422 	err = snd_mixer_elem_attach(melem, helem);
    423 	if (err < 0)
    424 		goto __error;
    425 	simple->dir[0].chanmap |= sid->chanmap[0];
    426 	simple->dir[1].chanmap |= sid->chanmap[1];
    427 	simple->selem.caps |= hsimple->caps;
    428 	update_ranges(simple);
    429 #if 0
    430 	err = simple_update(melem);
    431 	if (err < 0) {
    432 		if (new)
    433 			goto __error;
    434 		return err;
    435 	}
    436 #endif
    437 	if (new)
    438 		err = snd_mixer_elem_add(melem, class);
    439 	else
    440 		err = snd_mixer_elem_info(melem);
    441 	if (err < 0)
    442 		return err;
    443 	err = selem_read(melem);
    444 	if (err < 0)
    445 		return err;
    446 	if (err)
    447 		err = snd_mixer_elem_value(melem);
    448 	return err;
    449       __error:
    450       	if (new)
    451       		snd_mixer_elem_free(melem);
    452       	return -EINVAL;
    453 }
    454 
    455 static int simple_event_add(snd_mixer_class_t *class, snd_hctl_elem_t *helem)
    456 {
    457 	struct bclass_private *priv = snd_mixer_sbasic_get_private(class);
    458 	struct bclass_selector *sel;
    459 	struct helem_selector *hsel;
    460 	struct list_head *pos;
    461 	snd_ctl_elem_iface_t iface = snd_hctl_elem_get_interface(helem);
    462 	const char *name = snd_hctl_elem_get_name(helem);
    463 	unsigned int index = snd_hctl_elem_get_index(helem);
    464 	unsigned int ui;
    465 	int err;
    466 
    467 	list_for_each(pos, &priv->selectors) {
    468 		sel = list_entry(pos, struct bclass_selector, list);
    469 		for (ui = 0; ui < sel->count; ui++) {
    470 			hsel = &sel->selectors[ui];
    471 			if (hsel->iface == iface && !strcmp(hsel->name, name) && hsel->index == index) {
    472 				err = simple_event_add1(class, helem, hsel);
    473 				if (err < 0)
    474 					return err;	/* early exit? */
    475 			}
    476 		}
    477 	}
    478 	return 0;
    479 }
    480 
    481 int alsa_mixer_sbasic_event(snd_mixer_class_t *class, unsigned int mask,
    482 			    snd_hctl_elem_t *helem, snd_mixer_elem_t *melem)
    483 {
    484 	int err;
    485 	if (mask == SND_CTL_EVENT_MASK_REMOVE)
    486 		return simple_event_remove(helem, melem);
    487 	if (mask & SND_CTL_EVENT_MASK_ADD) {
    488 		err = simple_event_add(class, helem);
    489 		if (err < 0)
    490 			return err;
    491 	}
    492 	if (mask & SND_CTL_EVENT_MASK_INFO) {
    493 		err = simple_event_remove(helem, melem);
    494 		if (err < 0)
    495 			return err;
    496 		err = simple_event_add(class, helem);
    497 		if (err < 0)
    498 			return err;
    499 		return 0;
    500 	}
    501 	if (mask & SND_CTL_EVENT_MASK_VALUE) {
    502 		err = selem_read(melem);
    503 		if (err < 0)
    504 			return err;
    505 		if (err) {
    506 			err = snd_mixer_elem_value(melem);
    507 			if (err < 0)
    508 				return err;
    509 		}
    510 	}
    511 	return 0;
    512 }
    513 
    514 static void sbasic_cpriv_free(snd_mixer_class_t *class)
    515 {
    516 	struct bclass_private *priv = snd_mixer_sbasic_get_private(class);
    517 	struct bclass_selector *sel;
    518 	struct bclass_sid *sid;
    519 	struct list_head *pos, *pos1;
    520 
    521 	list_for_each_safe(pos, pos1, &priv->selectors) {
    522 		sel = list_entry(pos, struct bclass_selector, list);
    523 		free(sel);
    524 	}
    525 	list_for_each_safe(pos, pos1, &priv->sids) {
    526 		sid = list_entry(pos, struct bclass_sid, list);
    527 		free(sid);
    528 	}
    529 	free(priv);
    530 }
    531 
    532 void alsa_mixer_sbasic_initpriv(snd_mixer_class_t *class,
    533 				struct bclass_private *priv)
    534 {
    535 	INIT_LIST_HEAD(&priv->selectors);
    536 	INIT_LIST_HEAD(&priv->sids);
    537 	snd_mixer_sbasic_set_private(class, priv);
    538 	snd_mixer_sbasic_set_private_free(class, sbasic_cpriv_free);
    539 }
    540 
    541 int alsa_mixer_sbasic_selreg(snd_mixer_class_t *class,
    542 			     struct helem_selector *selectors,
    543 			     unsigned int count)
    544 {
    545 	struct bclass_private *priv = snd_mixer_sbasic_get_private(class);
    546 	struct bclass_selector *sel = calloc(1, sizeof(*sel));
    547 
    548 	if (sel == NULL)
    549 		return -ENOMEM;
    550 	if (priv == NULL) {
    551 		priv = calloc(1, sizeof(*priv));
    552 		if (priv == NULL) {
    553 			free(sel);
    554 			return -ENOMEM;
    555 		}
    556 	}
    557 	sel->selectors = selectors;
    558 	sel->count = count;
    559 	list_add_tail(&sel->list, &priv->selectors);
    560 	return 0;
    561 }
    562 
    563 int alsa_mixer_sbasic_sidreg(snd_mixer_class_t *class,
    564 			     struct melem_sids *sids,
    565 			     unsigned int count)
    566 {
    567 	struct bclass_private *priv = snd_mixer_sbasic_get_private(class);
    568 	struct bclass_sid *sid = calloc(1, sizeof(*sid));
    569 
    570 	if (sid == NULL)
    571 		return -ENOMEM;
    572 	if (priv == NULL) {
    573 		priv = calloc(1, sizeof(*priv));
    574 		if (priv == NULL) {
    575 			free(sid);
    576 			return -ENOMEM;
    577 		}
    578 		INIT_LIST_HEAD(&priv->selectors);
    579 		INIT_LIST_HEAD(&priv->sids);
    580 		snd_mixer_sbasic_set_private(class, priv);
    581 		snd_mixer_sbasic_set_private_free(class, sbasic_cpriv_free);
    582 	}
    583 	sid->sids = sids;
    584 	sid->count = count;
    585 	list_add(&sid->list, &priv->sids);
    586 	return 0;
    587 }
    588