• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  *  Mixer Interface - simple abstact module - base library
3  *  Copyright (c) 2005 by Jaroslav Kysela <perex@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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  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 
chanmap_to_channels(unsigned int chanmap)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 
update_ranges(struct selem_base * s)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 
is_ops(snd_mixer_elem_t * elem,int dir,int cmd,int val)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 
get_range_ops(snd_mixer_elem_t * elem,int dir,long * min,long * max)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 
get_dB_range_ops(snd_mixer_elem_t * elem ATTRIBUTE_UNUSED,int dir ATTRIBUTE_UNUSED,long * min ATTRIBUTE_UNUSED,long * max ATTRIBUTE_UNUSED)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 
set_range_ops(snd_mixer_elem_t * elem,int dir,long min,long max)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 
get_volume_ops(snd_mixer_elem_t * elem,int dir,snd_mixer_selem_channel_id_t channel,long * value)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 
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)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 
get_switch_ops(snd_mixer_elem_t * elem ATTRIBUTE_UNUSED,int dir ATTRIBUTE_UNUSED,snd_mixer_selem_channel_id_t channel ATTRIBUTE_UNUSED,int * value)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 
set_volume_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)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 
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)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 
set_switch_ops(snd_mixer_elem_t * elem ATTRIBUTE_UNUSED,int dir ATTRIBUTE_UNUSED,snd_mixer_selem_channel_id_t channel ATTRIBUTE_UNUSED,int value ATTRIBUTE_UNUSED)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 
enum_item_name_ops(snd_mixer_elem_t * elem ATTRIBUTE_UNUSED,unsigned int item ATTRIBUTE_UNUSED,size_t maxlen ATTRIBUTE_UNUSED,char * buf ATTRIBUTE_UNUSED)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 
get_enum_item_ops(snd_mixer_elem_t * elem ATTRIBUTE_UNUSED,snd_mixer_selem_channel_id_t channel ATTRIBUTE_UNUSED,unsigned int * itemp ATTRIBUTE_UNUSED)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 
set_enum_item_ops(snd_mixer_elem_t * elem ATTRIBUTE_UNUSED,snd_mixer_selem_channel_id_t channel ATTRIBUTE_UNUSED,unsigned int item ATTRIBUTE_UNUSED)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 
selem_read(snd_mixer_elem_t * elem)288 static int selem_read(snd_mixer_elem_t *elem)
289 {
290 	printf("elem read: %p\n", elem);
291 	return 0;
292 }
293 
simple_event_remove(snd_hctl_elem_t * helem,snd_mixer_elem_t * melem ATTRIBUTE_UNUSED)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 
selem_free(snd_mixer_elem_t * elem)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 
simple_event_add1(snd_mixer_class_t * class,snd_hctl_elem_t * helem,struct helem_selector * sel)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 	long min, max;
328 	int err, new = 0;
329 	struct list_head *pos;
330 	struct bclass_sid *bsid;
331 	struct melem_sids *sid;
332 	unsigned int ui;
333 
334 	list_for_each(pos, &priv->sids) {
335 		bsid = list_entry(pos, struct bclass_sid, list);
336 		for (ui = 0; ui < bsid->count; ui++) {
337 			if (bsid->sids[ui].sid == sel->sid) {
338 				sid = &bsid->sids[ui];
339 				goto __sid_ok;
340 			}
341 		}
342 	}
343 	return 0;
344 
345       __sid_ok:
346 	snd_ctl_elem_info_alloca(&info);
347 	err = snd_hctl_elem_info(helem, info);
348 	if (err < 0)
349 		return err;
350 	ctype = snd_ctl_elem_info_get_type(info);
351 	switch (ctype) {
352 	case SND_CTL_ELEM_TYPE_ENUMERATED:
353 		min = 0;
354 		max = snd_ctl_elem_info_get_items(info);
355 		break;
356 	case SND_CTL_ELEM_TYPE_INTEGER:
357 		min = snd_ctl_elem_info_get_min(info);
358 		max = snd_ctl_elem_info_get_max(info);
359 		break;
360 	default:
361 		min = max = 0;
362 		break;
363 	}
364 
365 	printf("event add: %p, %p (%s)\n", helem, sel, snd_hctl_elem_get_name(helem));
366 	if (snd_mixer_selem_id_malloc(&id))
367 		return -ENOMEM;
368 	hsimple = calloc(1, sizeof(*hsimple));
369 	if (hsimple == NULL) {
370 		snd_mixer_selem_id_free(id);
371 		return -ENOMEM;
372 	}
373 	switch (sel->purpose) {
374 	case PURPOSE_SWITCH:
375 		if (ctype != SND_CTL_ELEM_TYPE_BOOLEAN) {
376 		      __invalid_type:
377 		      	snd_mixer_selem_id_free(id);
378 			free(hsimple);
379 			return -EINVAL;
380 		}
381 		break;
382 	case PURPOSE_VOLUME:
383 		if (ctype != SND_CTL_ELEM_TYPE_INTEGER)
384 			goto __invalid_type;
385 		break;
386 	}
387 	hsimple->purpose = sel->purpose;
388 	hsimple->caps = sel->caps;
389 	hsimple->min = min;
390 	hsimple->max = max;
391 	snd_mixer_selem_id_set_name(id, sid->sname);
392 	snd_mixer_selem_id_set_index(id, sid->sindex);
393 	melem = snd_mixer_find_selem(snd_mixer_class_get_mixer(class), id);
394 	if (!melem) {
395 		simple = calloc(1, sizeof(*simple));
396 		if (!simple) {
397 			snd_mixer_selem_id_free(id);
398 			free(hsimple);
399 			return -ENOMEM;
400 		}
401 		simple->selem.id = id;
402 		simple->selem.ops = &simple_ac97_ops;
403 		INIT_LIST_HEAD(&simple->helems);
404 		simple->sid = sel->sid;
405 		err = snd_mixer_elem_new(&melem, SND_MIXER_ELEM_SIMPLE,
406 					 sid->weight,
407 					 simple, selem_free);
408 		if (err < 0) {
409 			snd_mixer_selem_id_free(id);
410 			free(hsimple);
411 			free(simple);
412 			return err;
413 		}
414 		new = 1;
415 	} else {
416 		simple = snd_mixer_elem_get_private(melem);
417 		snd_mixer_selem_id_free(id);
418 	}
419 	list_add_tail(&hsimple->list, &simple->helems);
420 	hsimple->inactive = snd_ctl_elem_info_is_inactive(info);
421 	err = snd_mixer_elem_attach(melem, helem);
422 	if (err < 0)
423 		goto __error;
424 	simple->dir[0].chanmap |= sid->chanmap[0];
425 	simple->dir[1].chanmap |= sid->chanmap[1];
426 	simple->selem.caps |= hsimple->caps;
427 	update_ranges(simple);
428 #if 0
429 	err = simple_update(melem);
430 	if (err < 0) {
431 		if (new)
432 			goto __error;
433 		return err;
434 	}
435 #endif
436 	if (new)
437 		err = snd_mixer_elem_add(melem, class);
438 	else
439 		err = snd_mixer_elem_info(melem);
440 	if (err < 0)
441 		return err;
442 	err = selem_read(melem);
443 	if (err < 0)
444 		return err;
445 	if (err)
446 		err = snd_mixer_elem_value(melem);
447 	return err;
448       __error:
449       	if (new)
450       		snd_mixer_elem_free(melem);
451       	return -EINVAL;
452 }
453 
simple_event_add(snd_mixer_class_t * class,snd_hctl_elem_t * helem)454 static int simple_event_add(snd_mixer_class_t *class, snd_hctl_elem_t *helem)
455 {
456 	struct bclass_private *priv = snd_mixer_sbasic_get_private(class);
457 	struct bclass_selector *sel;
458 	struct helem_selector *hsel;
459 	struct list_head *pos;
460 	snd_ctl_elem_iface_t iface = snd_hctl_elem_get_interface(helem);
461 	const char *name = snd_hctl_elem_get_name(helem);
462 	unsigned int index = snd_hctl_elem_get_index(helem);
463 	unsigned int ui;
464 	int err;
465 
466 	list_for_each(pos, &priv->selectors) {
467 		sel = list_entry(pos, struct bclass_selector, list);
468 		for (ui = 0; ui < sel->count; ui++) {
469 			hsel = &sel->selectors[ui];
470 			if (hsel->iface == iface && !strcmp(hsel->name, name) && hsel->index == index) {
471 				err = simple_event_add1(class, helem, hsel);
472 				if (err < 0)
473 					return err;	/* early exit? */
474 			}
475 		}
476 	}
477 	return 0;
478 }
479 
alsa_mixer_sbasic_event(snd_mixer_class_t * class,unsigned int mask,snd_hctl_elem_t * helem,snd_mixer_elem_t * melem)480 int alsa_mixer_sbasic_event(snd_mixer_class_t *class, unsigned int mask,
481 			    snd_hctl_elem_t *helem, snd_mixer_elem_t *melem)
482 {
483 	int err;
484 	if (mask == SND_CTL_EVENT_MASK_REMOVE)
485 		return simple_event_remove(helem, melem);
486 	if (mask & SND_CTL_EVENT_MASK_ADD) {
487 		err = simple_event_add(class, helem);
488 		if (err < 0)
489 			return err;
490 	}
491 	if (mask & SND_CTL_EVENT_MASK_INFO) {
492 		err = simple_event_remove(helem, melem);
493 		if (err < 0)
494 			return err;
495 		err = simple_event_add(class, helem);
496 		if (err < 0)
497 			return err;
498 		return 0;
499 	}
500 	if (mask & SND_CTL_EVENT_MASK_VALUE) {
501 		err = selem_read(melem);
502 		if (err < 0)
503 			return err;
504 		if (err) {
505 			err = snd_mixer_elem_value(melem);
506 			if (err < 0)
507 				return err;
508 		}
509 	}
510 	return 0;
511 }
512 
sbasic_cpriv_free(snd_mixer_class_t * class)513 static void sbasic_cpriv_free(snd_mixer_class_t *class)
514 {
515 	struct bclass_private *priv = snd_mixer_sbasic_get_private(class);
516 	struct bclass_selector *sel;
517 	struct bclass_sid *sid;
518 	struct list_head *pos, *pos1;
519 
520 	list_for_each_safe(pos, pos1, &priv->selectors) {
521 		sel = list_entry(pos, struct bclass_selector, list);
522 		free(sel);
523 	}
524 	list_for_each_safe(pos, pos1, &priv->sids) {
525 		sid = list_entry(pos, struct bclass_sid, list);
526 		free(sid);
527 	}
528 	free(priv);
529 }
530 
alsa_mixer_sbasic_initpriv(snd_mixer_class_t * class,struct bclass_private * priv)531 void alsa_mixer_sbasic_initpriv(snd_mixer_class_t *class,
532 				struct bclass_private *priv)
533 {
534 	INIT_LIST_HEAD(&priv->selectors);
535 	INIT_LIST_HEAD(&priv->sids);
536 	snd_mixer_sbasic_set_private(class, priv);
537 	snd_mixer_sbasic_set_private_free(class, sbasic_cpriv_free);
538 }
539 
alsa_mixer_sbasic_selreg(snd_mixer_class_t * class,struct helem_selector * selectors,unsigned int count)540 int alsa_mixer_sbasic_selreg(snd_mixer_class_t *class,
541 			     struct helem_selector *selectors,
542 			     unsigned int count)
543 {
544 	struct bclass_private *priv = snd_mixer_sbasic_get_private(class);
545 	struct bclass_selector *sel = calloc(1, sizeof(*sel));
546 
547 	if (sel == NULL)
548 		return -ENOMEM;
549 	if (priv == NULL) {
550 		priv = calloc(1, sizeof(*priv));
551 		if (priv == NULL) {
552 			free(sel);
553 			return -ENOMEM;
554 		}
555 	}
556 	sel->selectors = selectors;
557 	sel->count = count;
558 	list_add_tail(&sel->list, &priv->selectors);
559 	return 0;
560 }
561 
alsa_mixer_sbasic_sidreg(snd_mixer_class_t * class,struct melem_sids * sids,unsigned int count)562 int alsa_mixer_sbasic_sidreg(snd_mixer_class_t *class,
563 			     struct melem_sids *sids,
564 			     unsigned int count)
565 {
566 	struct bclass_private *priv = snd_mixer_sbasic_get_private(class);
567 	struct bclass_sid *sid = calloc(1, sizeof(*sid));
568 
569 	if (sid == NULL)
570 		return -ENOMEM;
571 	if (priv == NULL) {
572 		priv = calloc(1, sizeof(*priv));
573 		if (priv == NULL) {
574 			free(sid);
575 			return -ENOMEM;
576 		}
577 		INIT_LIST_HEAD(&priv->selectors);
578 		INIT_LIST_HEAD(&priv->sids);
579 		snd_mixer_sbasic_set_private(class, priv);
580 		snd_mixer_sbasic_set_private_free(class, sbasic_cpriv_free);
581 	}
582 	sid->sids = sids;
583 	sid->count = count;
584 	list_add(&sid->list, &priv->sids);
585 	return 0;
586 }
587