1*d0c94b83SXin Li /* mixer.c
2*d0c94b83SXin Li **
3*d0c94b83SXin Li ** Copyright 2011, The Android Open Source Project
4*d0c94b83SXin Li **
5*d0c94b83SXin Li ** Redistribution and use in source and binary forms, with or without
6*d0c94b83SXin Li ** modification, are permitted provided that the following conditions are met:
7*d0c94b83SXin Li ** * Redistributions of source code must retain the above copyright
8*d0c94b83SXin Li ** notice, this list of conditions and the following disclaimer.
9*d0c94b83SXin Li ** * Redistributions in binary form must reproduce the above copyright
10*d0c94b83SXin Li ** notice, this list of conditions and the following disclaimer in the
11*d0c94b83SXin Li ** documentation and/or other materials provided with the distribution.
12*d0c94b83SXin Li ** * Neither the name of The Android Open Source Project nor the names of
13*d0c94b83SXin Li ** its contributors may be used to endorse or promote products derived
14*d0c94b83SXin Li ** from this software without specific prior written permission.
15*d0c94b83SXin Li **
16*d0c94b83SXin Li ** THIS SOFTWARE IS PROVIDED BY The Android Open Source Project ``AS IS'' AND
17*d0c94b83SXin Li ** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18*d0c94b83SXin Li ** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19*d0c94b83SXin Li ** ARE DISCLAIMED. IN NO EVENT SHALL The Android Open Source Project BE LIABLE
20*d0c94b83SXin Li ** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21*d0c94b83SXin Li ** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22*d0c94b83SXin Li ** SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
23*d0c94b83SXin Li ** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24*d0c94b83SXin Li ** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25*d0c94b83SXin Li ** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
26*d0c94b83SXin Li ** DAMAGE.
27*d0c94b83SXin Li */
28*d0c94b83SXin Li
29*d0c94b83SXin Li #include <stdbool.h>
30*d0c94b83SXin Li #include <stdio.h>
31*d0c94b83SXin Li #include <stdlib.h>
32*d0c94b83SXin Li #include <stdint.h>
33*d0c94b83SXin Li #include <string.h>
34*d0c94b83SXin Li #include <unistd.h>
35*d0c94b83SXin Li #include <fcntl.h>
36*d0c94b83SXin Li #include <errno.h>
37*d0c94b83SXin Li #include <ctype.h>
38*d0c94b83SXin Li #include <poll.h>
39*d0c94b83SXin Li
40*d0c94b83SXin Li #include <sys/ioctl.h>
41*d0c94b83SXin Li
42*d0c94b83SXin Li #include <linux/ioctl.h>
43*d0c94b83SXin Li
44*d0c94b83SXin Li #define __force
45*d0c94b83SXin Li #define __bitwise
46*d0c94b83SXin Li #define __user
47*d0c94b83SXin Li #include <sound/asound.h>
48*d0c94b83SXin Li
49*d0c94b83SXin Li #ifndef SNDRV_CTL_ELEM_ID_NAME_MAXLEN
50*d0c94b83SXin Li #define SNDRV_CTL_ELEM_ID_NAME_MAXLEN 44
51*d0c94b83SXin Li #endif
52*d0c94b83SXin Li
53*d0c94b83SXin Li #include <tinyalsa/asoundlib.h>
54*d0c94b83SXin Li #include "mixer_io.h"
55*d0c94b83SXin Li
56*d0c94b83SXin Li struct mixer_ctl {
57*d0c94b83SXin Li struct mixer_ctl_group *grp;
58*d0c94b83SXin Li struct snd_ctl_elem_info *info;
59*d0c94b83SXin Li char **ename;
60*d0c94b83SXin Li bool info_retrieved;
61*d0c94b83SXin Li };
62*d0c94b83SXin Li
63*d0c94b83SXin Li struct mixer_ctl_group {
64*d0c94b83SXin Li struct snd_ctl_elem_info *elem_info;
65*d0c94b83SXin Li struct mixer_ctl *ctl;
66*d0c94b83SXin Li unsigned int count;
67*d0c94b83SXin Li int event_cnt;
68*d0c94b83SXin Li
69*d0c94b83SXin Li struct mixer_ops *ops;
70*d0c94b83SXin Li void *data;
71*d0c94b83SXin Li };
72*d0c94b83SXin Li
73*d0c94b83SXin Li struct mixer {
74*d0c94b83SXin Li int fd;
75*d0c94b83SXin Li struct snd_ctl_card_info card_info;
76*d0c94b83SXin Li
77*d0c94b83SXin Li /* hardware/physical mixer control group */
78*d0c94b83SXin Li struct mixer_ctl_group *hw_grp;
79*d0c94b83SXin Li
80*d0c94b83SXin Li /*
81*d0c94b83SXin Li * Virutal mixer control group.
82*d0c94b83SXin Li * Currently supports one virtual mixer (.so)
83*d0c94b83SXin Li * per card. Could be extended to multiple
84*d0c94b83SXin Li */
85*d0c94b83SXin Li struct mixer_ctl_group *virt_grp;
86*d0c94b83SXin Li
87*d0c94b83SXin Li unsigned int total_ctl_count;
88*d0c94b83SXin Li };
89*d0c94b83SXin Li
mixer_grp_close(struct mixer_ctl_group * grp)90*d0c94b83SXin Li static void mixer_grp_close(struct mixer_ctl_group *grp)
91*d0c94b83SXin Li {
92*d0c94b83SXin Li unsigned int n, m;
93*d0c94b83SXin Li
94*d0c94b83SXin Li if (!grp)
95*d0c94b83SXin Li return;
96*d0c94b83SXin Li
97*d0c94b83SXin Li if (grp->ctl) {
98*d0c94b83SXin Li for (n = 0; n < grp->count; n++) {
99*d0c94b83SXin Li if (grp->ctl[n].ename) {
100*d0c94b83SXin Li unsigned int max = grp->ctl[n].info->value.enumerated.items;
101*d0c94b83SXin Li for (m = 0; m < max; m++)
102*d0c94b83SXin Li free(grp->ctl[n].ename[m]);
103*d0c94b83SXin Li free(grp->ctl[n].ename);
104*d0c94b83SXin Li }
105*d0c94b83SXin Li }
106*d0c94b83SXin Li free(grp->ctl);
107*d0c94b83SXin Li }
108*d0c94b83SXin Li
109*d0c94b83SXin Li if (grp->elem_info)
110*d0c94b83SXin Li free(grp->elem_info);
111*d0c94b83SXin Li
112*d0c94b83SXin Li free(grp);
113*d0c94b83SXin Li }
114*d0c94b83SXin Li
mixer_close(struct mixer * mixer)115*d0c94b83SXin Li void mixer_close(struct mixer *mixer)
116*d0c94b83SXin Li {
117*d0c94b83SXin Li if (!mixer)
118*d0c94b83SXin Li return;
119*d0c94b83SXin Li
120*d0c94b83SXin Li if (mixer->fd >= 0 && mixer->hw_grp)
121*d0c94b83SXin Li mixer->hw_grp->ops->close(mixer->hw_grp->data);
122*d0c94b83SXin Li mixer_grp_close(mixer->hw_grp);
123*d0c94b83SXin Li
124*d0c94b83SXin Li if (mixer->virt_grp)
125*d0c94b83SXin Li mixer->virt_grp->ops->close(mixer->virt_grp->data);
126*d0c94b83SXin Li mixer_grp_close(mixer->virt_grp);
127*d0c94b83SXin Li
128*d0c94b83SXin Li free(mixer);
129*d0c94b83SXin Li
130*d0c94b83SXin Li /* TODO: verify frees */
131*d0c94b83SXin Li }
132*d0c94b83SXin Li
mixer_grp_open(struct mixer_ctl_group ** ctl_grp,struct mixer_ops * ops,void * data,int * num_ctls_in_grp)133*d0c94b83SXin Li static int mixer_grp_open(struct mixer_ctl_group **ctl_grp,
134*d0c94b83SXin Li struct mixer_ops *ops,
135*d0c94b83SXin Li void *data, int *num_ctls_in_grp)
136*d0c94b83SXin Li {
137*d0c94b83SXin Li struct mixer_ctl_group *grp;
138*d0c94b83SXin Li struct snd_ctl_elem_list elist;
139*d0c94b83SXin Li struct snd_ctl_elem_id *eid = NULL;
140*d0c94b83SXin Li unsigned int n;
141*d0c94b83SXin Li int ret;
142*d0c94b83SXin Li
143*d0c94b83SXin Li grp = calloc(1, sizeof(*grp));
144*d0c94b83SXin Li if (!grp)
145*d0c94b83SXin Li return -ENOMEM;
146*d0c94b83SXin Li
147*d0c94b83SXin Li memset(&elist, 0, sizeof(elist));
148*d0c94b83SXin Li ret = ops->ioctl(data, SNDRV_CTL_IOCTL_ELEM_LIST, &elist);
149*d0c94b83SXin Li if (ret < 0)
150*d0c94b83SXin Li goto err_get_elem_list;
151*d0c94b83SXin Li
152*d0c94b83SXin Li grp->ctl = calloc(elist.count, sizeof(struct mixer_ctl));
153*d0c94b83SXin Li grp->elem_info = calloc(elist.count, sizeof(struct snd_ctl_elem_info));
154*d0c94b83SXin Li if (!grp->ctl || !grp->elem_info) {
155*d0c94b83SXin Li ret = -ENOMEM;
156*d0c94b83SXin Li goto err_ctl_alloc;
157*d0c94b83SXin Li }
158*d0c94b83SXin Li
159*d0c94b83SXin Li eid = calloc(elist.count, sizeof(*eid));
160*d0c94b83SXin Li if (!eid) {
161*d0c94b83SXin Li ret = -ENOMEM;
162*d0c94b83SXin Li goto err_ctl_alloc;
163*d0c94b83SXin Li }
164*d0c94b83SXin Li
165*d0c94b83SXin Li grp->count = elist.count;
166*d0c94b83SXin Li elist.space = grp->count;
167*d0c94b83SXin Li elist.pids = eid;
168*d0c94b83SXin Li ret = ops->ioctl(data, SNDRV_CTL_IOCTL_ELEM_LIST, &elist);
169*d0c94b83SXin Li if (ret < 0)
170*d0c94b83SXin Li goto err_ctl_alloc;
171*d0c94b83SXin Li
172*d0c94b83SXin Li for (n = 0; n < grp->count; n++) {
173*d0c94b83SXin Li struct mixer_ctl *ctl = grp->ctl + n;
174*d0c94b83SXin Li
175*d0c94b83SXin Li ctl->grp = grp;
176*d0c94b83SXin Li ctl->info = grp->elem_info + n;
177*d0c94b83SXin Li ctl->info->id.numid = eid[n].numid;
178*d0c94b83SXin Li strncpy((char *)ctl->info->id.name, (char *)eid[n].name,
179*d0c94b83SXin Li SNDRV_CTL_ELEM_ID_NAME_MAXLEN);
180*d0c94b83SXin Li ctl->info->id.name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN - 1] = 0;
181*d0c94b83SXin Li }
182*d0c94b83SXin Li
183*d0c94b83SXin Li grp->data = data;
184*d0c94b83SXin Li grp->ops = ops;
185*d0c94b83SXin Li *ctl_grp = grp;
186*d0c94b83SXin Li *num_ctls_in_grp = grp->count;
187*d0c94b83SXin Li
188*d0c94b83SXin Li free(eid);
189*d0c94b83SXin Li return 0;
190*d0c94b83SXin Li
191*d0c94b83SXin Li err_ctl_alloc:
192*d0c94b83SXin Li
193*d0c94b83SXin Li free(eid);
194*d0c94b83SXin Li free(grp->elem_info);
195*d0c94b83SXin Li free(grp->ctl);
196*d0c94b83SXin Li
197*d0c94b83SXin Li err_get_elem_list:
198*d0c94b83SXin Li
199*d0c94b83SXin Li free(grp);
200*d0c94b83SXin Li return ret;
201*d0c94b83SXin Li
202*d0c94b83SXin Li }
203*d0c94b83SXin Li
mixer_do_hw_open(struct mixer * mixer,unsigned int card)204*d0c94b83SXin Li static int mixer_do_hw_open(struct mixer *mixer, unsigned int card)
205*d0c94b83SXin Li {
206*d0c94b83SXin Li struct mixer_ops *ops;
207*d0c94b83SXin Li void *data;
208*d0c94b83SXin Li int fd, ret, num_grp_ctls = 0;
209*d0c94b83SXin Li
210*d0c94b83SXin Li mixer->fd = -1;
211*d0c94b83SXin Li fd = mixer_hw_open(card, &data, &ops);
212*d0c94b83SXin Li if (fd < 0)
213*d0c94b83SXin Li return fd;
214*d0c94b83SXin Li
215*d0c94b83SXin Li ret = ops->ioctl(data, SNDRV_CTL_IOCTL_CARD_INFO, &mixer->card_info);
216*d0c94b83SXin Li if (ret < 0)
217*d0c94b83SXin Li goto err_card_info;
218*d0c94b83SXin Li
219*d0c94b83SXin Li ret = mixer_grp_open(&mixer->hw_grp, ops, data, &num_grp_ctls);
220*d0c94b83SXin Li if (ret < 0)
221*d0c94b83SXin Li goto err_card_info;
222*d0c94b83SXin Li
223*d0c94b83SXin Li mixer->total_ctl_count += num_grp_ctls;
224*d0c94b83SXin Li
225*d0c94b83SXin Li mixer->fd = fd;
226*d0c94b83SXin Li return 0;
227*d0c94b83SXin Li
228*d0c94b83SXin Li err_card_info:
229*d0c94b83SXin Li ops->close(data);
230*d0c94b83SXin Li return ret;
231*d0c94b83SXin Li
232*d0c94b83SXin Li }
233*d0c94b83SXin Li
mixer_do_plugin_open(struct mixer * mixer,unsigned int card,int is_hw_open_failed)234*d0c94b83SXin Li static int mixer_do_plugin_open(struct mixer *mixer, unsigned int card,
235*d0c94b83SXin Li int is_hw_open_failed)
236*d0c94b83SXin Li {
237*d0c94b83SXin Li struct mixer_ops *ops;
238*d0c94b83SXin Li void *data;
239*d0c94b83SXin Li int ret, num_grp_ctls = 0;
240*d0c94b83SXin Li
241*d0c94b83SXin Li ret = mixer_plugin_open(card, &data, &ops);
242*d0c94b83SXin Li if (ret < 0)
243*d0c94b83SXin Li return ret;
244*d0c94b83SXin Li
245*d0c94b83SXin Li /* Get card_info if hw_open failed */
246*d0c94b83SXin Li if (is_hw_open_failed) {
247*d0c94b83SXin Li ret = ops->ioctl(data, SNDRV_CTL_IOCTL_CARD_INFO, &mixer->card_info);
248*d0c94b83SXin Li if (ret < 0)
249*d0c94b83SXin Li goto err_card_info;
250*d0c94b83SXin Li }
251*d0c94b83SXin Li
252*d0c94b83SXin Li ret = mixer_grp_open(&mixer->virt_grp, ops, data, &num_grp_ctls);
253*d0c94b83SXin Li if (ret < 0)
254*d0c94b83SXin Li goto err_card_info;
255*d0c94b83SXin Li
256*d0c94b83SXin Li mixer->total_ctl_count += num_grp_ctls;
257*d0c94b83SXin Li return 0;
258*d0c94b83SXin Li
259*d0c94b83SXin Li err_card_info:
260*d0c94b83SXin Li ops->close(data);
261*d0c94b83SXin Li return ret;
262*d0c94b83SXin Li
263*d0c94b83SXin Li }
264*d0c94b83SXin Li
mixer_open(unsigned int card)265*d0c94b83SXin Li struct mixer *mixer_open(unsigned int card)
266*d0c94b83SXin Li {
267*d0c94b83SXin Li struct mixer *mixer = NULL;
268*d0c94b83SXin Li int h_status, v_status;
269*d0c94b83SXin Li
270*d0c94b83SXin Li mixer = calloc(1, sizeof(*mixer));
271*d0c94b83SXin Li if (!mixer)
272*d0c94b83SXin Li goto fail;
273*d0c94b83SXin Li
274*d0c94b83SXin Li /* open hardware node first */
275*d0c94b83SXin Li h_status = mixer_do_hw_open(mixer, card);
276*d0c94b83SXin Li
277*d0c94b83SXin Li /*
278*d0c94b83SXin Li * open the virtual node even if hw_open fails
279*d0c94b83SXin Li * since hw_open is expected to fail for virtual cards
280*d0c94b83SXin Li * for which kernel does not register mixer node
281*d0c94b83SXin Li */
282*d0c94b83SXin Li //TODO: open virtual node only if mixer is defined under snd-card-def
283*d0c94b83SXin Li v_status = mixer_do_plugin_open(mixer, card, h_status);
284*d0c94b83SXin Li
285*d0c94b83SXin Li /* Fail mixer_open if both hw and plugin nodes cannot be opened */
286*d0c94b83SXin Li if (h_status < 0 && v_status < 0)
287*d0c94b83SXin Li goto fail;
288*d0c94b83SXin Li
289*d0c94b83SXin Li return mixer;
290*d0c94b83SXin Li
291*d0c94b83SXin Li fail:
292*d0c94b83SXin Li if (mixer)
293*d0c94b83SXin Li mixer_close(mixer);
294*d0c94b83SXin Li
295*d0c94b83SXin Li return 0;
296*d0c94b83SXin Li }
297*d0c94b83SXin Li
mixer_ctl_get_elem_info(struct mixer_ctl * ctl)298*d0c94b83SXin Li static bool mixer_ctl_get_elem_info(struct mixer_ctl* ctl)
299*d0c94b83SXin Li {
300*d0c94b83SXin Li struct mixer_ctl_group *grp;
301*d0c94b83SXin Li unsigned int i;
302*d0c94b83SXin Li
303*d0c94b83SXin Li if (!ctl || !ctl->grp)
304*d0c94b83SXin Li return false;
305*d0c94b83SXin Li
306*d0c94b83SXin Li grp = ctl->grp;
307*d0c94b83SXin Li if (!ctl->info_retrieved) {
308*d0c94b83SXin Li if (grp->ops->ioctl(grp->data, SNDRV_CTL_IOCTL_ELEM_INFO,
309*d0c94b83SXin Li ctl->info) < 0)
310*d0c94b83SXin Li return false;
311*d0c94b83SXin Li ctl->info_retrieved = true;
312*d0c94b83SXin Li }
313*d0c94b83SXin Li
314*d0c94b83SXin Li if (ctl->info->type != SNDRV_CTL_ELEM_TYPE_ENUMERATED || ctl->ename)
315*d0c94b83SXin Li return true;
316*d0c94b83SXin Li
317*d0c94b83SXin Li struct snd_ctl_elem_info tmp;
318*d0c94b83SXin Li char** enames = calloc(ctl->info->value.enumerated.items, sizeof(char*));
319*d0c94b83SXin Li if (!enames)
320*d0c94b83SXin Li return false;
321*d0c94b83SXin Li
322*d0c94b83SXin Li for (i = 0; i < ctl->info->value.enumerated.items; i++) {
323*d0c94b83SXin Li memset(&tmp, 0, sizeof(tmp));
324*d0c94b83SXin Li tmp.id.numid = ctl->info->id.numid;
325*d0c94b83SXin Li tmp.value.enumerated.item = i;
326*d0c94b83SXin Li if (grp->ops->ioctl(grp->data, SNDRV_CTL_IOCTL_ELEM_INFO, &tmp) < 0)
327*d0c94b83SXin Li goto fail;
328*d0c94b83SXin Li enames[i] = strdup(tmp.value.enumerated.name);
329*d0c94b83SXin Li if (!enames[i])
330*d0c94b83SXin Li goto fail;
331*d0c94b83SXin Li }
332*d0c94b83SXin Li ctl->ename = enames;
333*d0c94b83SXin Li return true;
334*d0c94b83SXin Li
335*d0c94b83SXin Li fail:
336*d0c94b83SXin Li free(enames);
337*d0c94b83SXin Li return false;
338*d0c94b83SXin Li }
339*d0c94b83SXin Li
mixer_get_name(struct mixer * mixer)340*d0c94b83SXin Li const char *mixer_get_name(struct mixer *mixer)
341*d0c94b83SXin Li {
342*d0c94b83SXin Li return (const char *)mixer->card_info.name;
343*d0c94b83SXin Li }
344*d0c94b83SXin Li
mixer_get_num_ctls(struct mixer * mixer)345*d0c94b83SXin Li unsigned int mixer_get_num_ctls(struct mixer *mixer)
346*d0c94b83SXin Li {
347*d0c94b83SXin Li if (!mixer)
348*d0c94b83SXin Li return 0;
349*d0c94b83SXin Li
350*d0c94b83SXin Li return mixer->total_ctl_count;
351*d0c94b83SXin Li }
352*d0c94b83SXin Li
mixer_grp_get_count(struct mixer_ctl_group * grp)353*d0c94b83SXin Li static int mixer_grp_get_count(struct mixer_ctl_group *grp)
354*d0c94b83SXin Li {
355*d0c94b83SXin Li if (!grp)
356*d0c94b83SXin Li return 0;
357*d0c94b83SXin Li
358*d0c94b83SXin Li return grp->count;
359*d0c94b83SXin Li }
360*d0c94b83SXin Li
mixer_get_ctl(struct mixer * mixer,unsigned int id)361*d0c94b83SXin Li struct mixer_ctl *mixer_get_ctl(struct mixer *mixer, unsigned int id)
362*d0c94b83SXin Li {
363*d0c94b83SXin Li struct mixer_ctl *ctl;
364*d0c94b83SXin Li unsigned int hw_ctl_count, virt_ctl_count;
365*d0c94b83SXin Li
366*d0c94b83SXin Li if (!mixer || (id >= mixer->total_ctl_count))
367*d0c94b83SXin Li return NULL;
368*d0c94b83SXin Li
369*d0c94b83SXin Li hw_ctl_count = mixer_grp_get_count(mixer->hw_grp);
370*d0c94b83SXin Li virt_ctl_count = mixer_grp_get_count(mixer->virt_grp);
371*d0c94b83SXin Li
372*d0c94b83SXin Li if (id < hw_ctl_count)
373*d0c94b83SXin Li ctl = mixer->hw_grp->ctl + id;
374*d0c94b83SXin Li else if ((id - hw_ctl_count) < virt_ctl_count)
375*d0c94b83SXin Li ctl = mixer->virt_grp->ctl + (id - hw_ctl_count);
376*d0c94b83SXin Li else
377*d0c94b83SXin Li return NULL;
378*d0c94b83SXin Li
379*d0c94b83SXin Li if (!mixer_ctl_get_elem_info(ctl))
380*d0c94b83SXin Li return NULL;
381*d0c94b83SXin Li
382*d0c94b83SXin Li return ctl;
383*d0c94b83SXin Li }
384*d0c94b83SXin Li
mixer_get_ctl_by_name(struct mixer * mixer,const char * name)385*d0c94b83SXin Li struct mixer_ctl *mixer_get_ctl_by_name(struct mixer *mixer, const char *name)
386*d0c94b83SXin Li {
387*d0c94b83SXin Li struct mixer_ctl_group *grp;
388*d0c94b83SXin Li unsigned int n;
389*d0c94b83SXin Li int hw_ctl_count;
390*d0c94b83SXin Li
391*d0c94b83SXin Li if (!mixer || !name)
392*d0c94b83SXin Li return NULL;
393*d0c94b83SXin Li
394*d0c94b83SXin Li hw_ctl_count = mixer_grp_get_count(mixer->hw_grp);
395*d0c94b83SXin Li if (mixer->hw_grp) {
396*d0c94b83SXin Li grp = mixer->hw_grp;
397*d0c94b83SXin Li
398*d0c94b83SXin Li for (n = 0; n < grp->count; n++)
399*d0c94b83SXin Li if (!strcmp(name, (char*) grp->elem_info[n].id.name))
400*d0c94b83SXin Li return mixer_get_ctl(mixer, n);
401*d0c94b83SXin Li }
402*d0c94b83SXin Li
403*d0c94b83SXin Li if (mixer->virt_grp) {
404*d0c94b83SXin Li grp = mixer->virt_grp;
405*d0c94b83SXin Li
406*d0c94b83SXin Li for (n = 0; n < grp->count; n++)
407*d0c94b83SXin Li if (!strcmp(name, (char*) grp->elem_info[n].id.name))
408*d0c94b83SXin Li return mixer_get_ctl(mixer, n + hw_ctl_count);
409*d0c94b83SXin Li }
410*d0c94b83SXin Li
411*d0c94b83SXin Li return NULL;
412*d0c94b83SXin Li }
413*d0c94b83SXin Li
mixer_ctl_update(struct mixer_ctl * ctl)414*d0c94b83SXin Li void mixer_ctl_update(struct mixer_ctl *ctl)
415*d0c94b83SXin Li {
416*d0c94b83SXin Li struct mixer_ctl_group *grp;
417*d0c94b83SXin Li
418*d0c94b83SXin Li if (!ctl || !ctl->grp)
419*d0c94b83SXin Li return;
420*d0c94b83SXin Li
421*d0c94b83SXin Li grp = ctl->grp;
422*d0c94b83SXin Li grp->ops->ioctl(grp->data, SNDRV_CTL_IOCTL_ELEM_INFO, ctl->info);
423*d0c94b83SXin Li }
424*d0c94b83SXin Li
mixer_ctl_get_name(struct mixer_ctl * ctl)425*d0c94b83SXin Li const char *mixer_ctl_get_name(struct mixer_ctl *ctl)
426*d0c94b83SXin Li {
427*d0c94b83SXin Li if (!ctl)
428*d0c94b83SXin Li return NULL;
429*d0c94b83SXin Li
430*d0c94b83SXin Li return (const char *)ctl->info->id.name;
431*d0c94b83SXin Li }
432*d0c94b83SXin Li
mixer_ctl_get_type(struct mixer_ctl * ctl)433*d0c94b83SXin Li enum mixer_ctl_type mixer_ctl_get_type(struct mixer_ctl *ctl)
434*d0c94b83SXin Li {
435*d0c94b83SXin Li if (!ctl)
436*d0c94b83SXin Li return MIXER_CTL_TYPE_UNKNOWN;
437*d0c94b83SXin Li
438*d0c94b83SXin Li switch (ctl->info->type) {
439*d0c94b83SXin Li case SNDRV_CTL_ELEM_TYPE_BOOLEAN: return MIXER_CTL_TYPE_BOOL;
440*d0c94b83SXin Li case SNDRV_CTL_ELEM_TYPE_INTEGER: return MIXER_CTL_TYPE_INT;
441*d0c94b83SXin Li case SNDRV_CTL_ELEM_TYPE_ENUMERATED: return MIXER_CTL_TYPE_ENUM;
442*d0c94b83SXin Li case SNDRV_CTL_ELEM_TYPE_BYTES: return MIXER_CTL_TYPE_BYTE;
443*d0c94b83SXin Li case SNDRV_CTL_ELEM_TYPE_IEC958: return MIXER_CTL_TYPE_IEC958;
444*d0c94b83SXin Li case SNDRV_CTL_ELEM_TYPE_INTEGER64: return MIXER_CTL_TYPE_INT64;
445*d0c94b83SXin Li default: return MIXER_CTL_TYPE_UNKNOWN;
446*d0c94b83SXin Li };
447*d0c94b83SXin Li }
448*d0c94b83SXin Li
mixer_ctl_get_type_string(struct mixer_ctl * ctl)449*d0c94b83SXin Li const char *mixer_ctl_get_type_string(struct mixer_ctl *ctl)
450*d0c94b83SXin Li {
451*d0c94b83SXin Li if (!ctl)
452*d0c94b83SXin Li return "";
453*d0c94b83SXin Li
454*d0c94b83SXin Li switch (ctl->info->type) {
455*d0c94b83SXin Li case SNDRV_CTL_ELEM_TYPE_BOOLEAN: return "BOOL";
456*d0c94b83SXin Li case SNDRV_CTL_ELEM_TYPE_INTEGER: return "INT";
457*d0c94b83SXin Li case SNDRV_CTL_ELEM_TYPE_ENUMERATED: return "ENUM";
458*d0c94b83SXin Li case SNDRV_CTL_ELEM_TYPE_BYTES: return "BYTE";
459*d0c94b83SXin Li case SNDRV_CTL_ELEM_TYPE_IEC958: return "IEC958";
460*d0c94b83SXin Li case SNDRV_CTL_ELEM_TYPE_INTEGER64: return "INT64";
461*d0c94b83SXin Li default: return "Unknown";
462*d0c94b83SXin Li };
463*d0c94b83SXin Li }
464*d0c94b83SXin Li
mixer_ctl_get_num_values(struct mixer_ctl * ctl)465*d0c94b83SXin Li unsigned int mixer_ctl_get_num_values(struct mixer_ctl *ctl)
466*d0c94b83SXin Li {
467*d0c94b83SXin Li if (!ctl)
468*d0c94b83SXin Li return 0;
469*d0c94b83SXin Li
470*d0c94b83SXin Li return ctl->info->count;
471*d0c94b83SXin Li }
472*d0c94b83SXin Li
percent_to_int(struct snd_ctl_elem_info * ei,int percent)473*d0c94b83SXin Li static int percent_to_int(struct snd_ctl_elem_info *ei, int percent)
474*d0c94b83SXin Li {
475*d0c94b83SXin Li int range;
476*d0c94b83SXin Li
477*d0c94b83SXin Li if (percent > 100)
478*d0c94b83SXin Li percent = 100;
479*d0c94b83SXin Li else if (percent < 0)
480*d0c94b83SXin Li percent = 0;
481*d0c94b83SXin Li
482*d0c94b83SXin Li range = (ei->value.integer.max - ei->value.integer.min);
483*d0c94b83SXin Li
484*d0c94b83SXin Li return ei->value.integer.min + (range * percent) / 100;
485*d0c94b83SXin Li }
486*d0c94b83SXin Li
int_to_percent(struct snd_ctl_elem_info * ei,int value)487*d0c94b83SXin Li static int int_to_percent(struct snd_ctl_elem_info *ei, int value)
488*d0c94b83SXin Li {
489*d0c94b83SXin Li int range = (ei->value.integer.max - ei->value.integer.min);
490*d0c94b83SXin Li
491*d0c94b83SXin Li if (range == 0)
492*d0c94b83SXin Li return 0;
493*d0c94b83SXin Li
494*d0c94b83SXin Li return ((value - ei->value.integer.min) / range) * 100;
495*d0c94b83SXin Li }
496*d0c94b83SXin Li
mixer_ctl_get_percent(struct mixer_ctl * ctl,unsigned int id)497*d0c94b83SXin Li int mixer_ctl_get_percent(struct mixer_ctl *ctl, unsigned int id)
498*d0c94b83SXin Li {
499*d0c94b83SXin Li if (!ctl || (ctl->info->type != SNDRV_CTL_ELEM_TYPE_INTEGER))
500*d0c94b83SXin Li return -EINVAL;
501*d0c94b83SXin Li
502*d0c94b83SXin Li return int_to_percent(ctl->info, mixer_ctl_get_value(ctl, id));
503*d0c94b83SXin Li }
504*d0c94b83SXin Li
mixer_ctl_set_percent(struct mixer_ctl * ctl,unsigned int id,int percent)505*d0c94b83SXin Li int mixer_ctl_set_percent(struct mixer_ctl *ctl, unsigned int id, int percent)
506*d0c94b83SXin Li {
507*d0c94b83SXin Li if (!ctl || (ctl->info->type != SNDRV_CTL_ELEM_TYPE_INTEGER))
508*d0c94b83SXin Li return -EINVAL;
509*d0c94b83SXin Li
510*d0c94b83SXin Li return mixer_ctl_set_value(ctl, id, percent_to_int(ctl->info, percent));
511*d0c94b83SXin Li }
512*d0c94b83SXin Li
mixer_ctl_get_value(struct mixer_ctl * ctl,unsigned int id)513*d0c94b83SXin Li int mixer_ctl_get_value(struct mixer_ctl *ctl, unsigned int id)
514*d0c94b83SXin Li {
515*d0c94b83SXin Li struct mixer_ctl_group *grp;
516*d0c94b83SXin Li struct snd_ctl_elem_value ev;
517*d0c94b83SXin Li int ret;
518*d0c94b83SXin Li
519*d0c94b83SXin Li if (!ctl || (id >= ctl->info->count) || !ctl->grp)
520*d0c94b83SXin Li return -EINVAL;
521*d0c94b83SXin Li
522*d0c94b83SXin Li grp = ctl->grp;
523*d0c94b83SXin Li memset(&ev, 0, sizeof(ev));
524*d0c94b83SXin Li ev.id.numid = ctl->info->id.numid;
525*d0c94b83SXin Li ret = grp->ops->ioctl(grp->data, SNDRV_CTL_IOCTL_ELEM_READ, &ev);
526*d0c94b83SXin Li if (ret < 0)
527*d0c94b83SXin Li return ret;
528*d0c94b83SXin Li
529*d0c94b83SXin Li switch (ctl->info->type) {
530*d0c94b83SXin Li case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
531*d0c94b83SXin Li return !!ev.value.integer.value[id];
532*d0c94b83SXin Li
533*d0c94b83SXin Li case SNDRV_CTL_ELEM_TYPE_INTEGER:
534*d0c94b83SXin Li return ev.value.integer.value[id];
535*d0c94b83SXin Li
536*d0c94b83SXin Li case SNDRV_CTL_ELEM_TYPE_ENUMERATED:
537*d0c94b83SXin Li return ev.value.enumerated.item[id];
538*d0c94b83SXin Li
539*d0c94b83SXin Li case SNDRV_CTL_ELEM_TYPE_BYTES:
540*d0c94b83SXin Li return ev.value.bytes.data[id];
541*d0c94b83SXin Li
542*d0c94b83SXin Li default:
543*d0c94b83SXin Li return -EINVAL;
544*d0c94b83SXin Li }
545*d0c94b83SXin Li
546*d0c94b83SXin Li return 0;
547*d0c94b83SXin Li }
548*d0c94b83SXin Li
mixer_ctl_is_access_tlv_rw(struct mixer_ctl * ctl)549*d0c94b83SXin Li int mixer_ctl_is_access_tlv_rw(struct mixer_ctl *ctl)
550*d0c94b83SXin Li {
551*d0c94b83SXin Li return (ctl->info->access & SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE);
552*d0c94b83SXin Li }
553*d0c94b83SXin Li
mixer_ctl_get_array(struct mixer_ctl * ctl,void * array,size_t count)554*d0c94b83SXin Li int mixer_ctl_get_array(struct mixer_ctl *ctl, void *array, size_t count)
555*d0c94b83SXin Li {
556*d0c94b83SXin Li struct mixer_ctl_group *grp;
557*d0c94b83SXin Li struct snd_ctl_elem_value ev;
558*d0c94b83SXin Li int ret = 0;
559*d0c94b83SXin Li size_t size;
560*d0c94b83SXin Li void *source;
561*d0c94b83SXin Li size_t total_count;
562*d0c94b83SXin Li
563*d0c94b83SXin Li if ((!ctl) || !count || !array || !ctl->grp)
564*d0c94b83SXin Li return -EINVAL;
565*d0c94b83SXin Li
566*d0c94b83SXin Li grp = ctl->grp;
567*d0c94b83SXin Li total_count = ctl->info->count;
568*d0c94b83SXin Li
569*d0c94b83SXin Li if ((ctl->info->type == SNDRV_CTL_ELEM_TYPE_BYTES) &&
570*d0c94b83SXin Li mixer_ctl_is_access_tlv_rw(ctl)) {
571*d0c94b83SXin Li /* Additional two words is for the TLV header */
572*d0c94b83SXin Li total_count += TLV_HEADER_SIZE;
573*d0c94b83SXin Li }
574*d0c94b83SXin Li
575*d0c94b83SXin Li if (count > total_count)
576*d0c94b83SXin Li return -EINVAL;
577*d0c94b83SXin Li
578*d0c94b83SXin Li memset(&ev, 0, sizeof(ev));
579*d0c94b83SXin Li ev.id.numid = ctl->info->id.numid;
580*d0c94b83SXin Li
581*d0c94b83SXin Li switch (ctl->info->type) {
582*d0c94b83SXin Li case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
583*d0c94b83SXin Li case SNDRV_CTL_ELEM_TYPE_INTEGER:
584*d0c94b83SXin Li ret = grp->ops->ioctl(grp->data, SNDRV_CTL_IOCTL_ELEM_READ, &ev);
585*d0c94b83SXin Li if (ret < 0)
586*d0c94b83SXin Li return ret;
587*d0c94b83SXin Li size = sizeof(ev.value.integer.value[0]);
588*d0c94b83SXin Li source = ev.value.integer.value;
589*d0c94b83SXin Li break;
590*d0c94b83SXin Li
591*d0c94b83SXin Li case SNDRV_CTL_ELEM_TYPE_BYTES:
592*d0c94b83SXin Li /* check if this is new bytes TLV */
593*d0c94b83SXin Li if (mixer_ctl_is_access_tlv_rw(ctl)) {
594*d0c94b83SXin Li struct snd_ctl_tlv *tlv;
595*d0c94b83SXin Li int ret;
596*d0c94b83SXin Li
597*d0c94b83SXin Li if (count > SIZE_MAX - sizeof(*tlv))
598*d0c94b83SXin Li return -EINVAL;
599*d0c94b83SXin Li tlv = calloc(1, sizeof(*tlv) + count);
600*d0c94b83SXin Li if (!tlv)
601*d0c94b83SXin Li return -ENOMEM;
602*d0c94b83SXin Li tlv->numid = ctl->info->id.numid;
603*d0c94b83SXin Li tlv->length = count;
604*d0c94b83SXin Li ret = grp->ops->ioctl(grp->data, SNDRV_CTL_IOCTL_TLV_READ, tlv);
605*d0c94b83SXin Li
606*d0c94b83SXin Li source = tlv->tlv;
607*d0c94b83SXin Li memcpy(array, source, count);
608*d0c94b83SXin Li
609*d0c94b83SXin Li free(tlv);
610*d0c94b83SXin Li
611*d0c94b83SXin Li return ret;
612*d0c94b83SXin Li } else {
613*d0c94b83SXin Li ret = grp->ops->ioctl(grp->data, SNDRV_CTL_IOCTL_ELEM_READ, &ev);
614*d0c94b83SXin Li if (ret < 0)
615*d0c94b83SXin Li return ret;
616*d0c94b83SXin Li size = sizeof(ev.value.bytes.data[0]);
617*d0c94b83SXin Li source = ev.value.bytes.data;
618*d0c94b83SXin Li break;
619*d0c94b83SXin Li }
620*d0c94b83SXin Li
621*d0c94b83SXin Li case SNDRV_CTL_ELEM_TYPE_IEC958:
622*d0c94b83SXin Li ret = grp->ops->ioctl(grp->data, SNDRV_CTL_IOCTL_ELEM_READ, &ev);
623*d0c94b83SXin Li if (ret < 0)
624*d0c94b83SXin Li return ret;
625*d0c94b83SXin Li size = sizeof(ev.value.iec958);
626*d0c94b83SXin Li source = &ev.value.iec958;
627*d0c94b83SXin Li break;
628*d0c94b83SXin Li
629*d0c94b83SXin Li default:
630*d0c94b83SXin Li return -EINVAL;
631*d0c94b83SXin Li }
632*d0c94b83SXin Li
633*d0c94b83SXin Li memcpy(array, source, size * count);
634*d0c94b83SXin Li
635*d0c94b83SXin Li return 0;
636*d0c94b83SXin Li }
637*d0c94b83SXin Li
mixer_ctl_set_value(struct mixer_ctl * ctl,unsigned int id,int value)638*d0c94b83SXin Li int mixer_ctl_set_value(struct mixer_ctl *ctl, unsigned int id, int value)
639*d0c94b83SXin Li {
640*d0c94b83SXin Li struct mixer_ctl_group *grp;
641*d0c94b83SXin Li struct snd_ctl_elem_value ev;
642*d0c94b83SXin Li int ret;
643*d0c94b83SXin Li
644*d0c94b83SXin Li if (!ctl || (id >= ctl->info->count) || !ctl->grp)
645*d0c94b83SXin Li return -EINVAL;
646*d0c94b83SXin Li
647*d0c94b83SXin Li grp = ctl->grp;
648*d0c94b83SXin Li memset(&ev, 0, sizeof(ev));
649*d0c94b83SXin Li ev.id.numid = ctl->info->id.numid;
650*d0c94b83SXin Li ret = grp->ops->ioctl(grp->data, SNDRV_CTL_IOCTL_ELEM_READ, &ev);
651*d0c94b83SXin Li if (ret < 0)
652*d0c94b83SXin Li return ret;
653*d0c94b83SXin Li
654*d0c94b83SXin Li switch (ctl->info->type) {
655*d0c94b83SXin Li case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
656*d0c94b83SXin Li ev.value.integer.value[id] = !!value;
657*d0c94b83SXin Li break;
658*d0c94b83SXin Li
659*d0c94b83SXin Li case SNDRV_CTL_ELEM_TYPE_INTEGER:
660*d0c94b83SXin Li ev.value.integer.value[id] = value;
661*d0c94b83SXin Li break;
662*d0c94b83SXin Li
663*d0c94b83SXin Li case SNDRV_CTL_ELEM_TYPE_ENUMERATED:
664*d0c94b83SXin Li ev.value.enumerated.item[id] = value;
665*d0c94b83SXin Li break;
666*d0c94b83SXin Li
667*d0c94b83SXin Li case SNDRV_CTL_ELEM_TYPE_BYTES:
668*d0c94b83SXin Li ev.value.bytes.data[id] = value;
669*d0c94b83SXin Li break;
670*d0c94b83SXin Li
671*d0c94b83SXin Li default:
672*d0c94b83SXin Li return -EINVAL;
673*d0c94b83SXin Li }
674*d0c94b83SXin Li
675*d0c94b83SXin Li return grp->ops->ioctl(grp->data, SNDRV_CTL_IOCTL_ELEM_WRITE, &ev);
676*d0c94b83SXin Li }
677*d0c94b83SXin Li
mixer_ctl_set_array(struct mixer_ctl * ctl,const void * array,size_t count)678*d0c94b83SXin Li int mixer_ctl_set_array(struct mixer_ctl *ctl, const void *array, size_t count)
679*d0c94b83SXin Li {
680*d0c94b83SXin Li struct mixer_ctl_group *grp;
681*d0c94b83SXin Li struct snd_ctl_elem_value ev;
682*d0c94b83SXin Li size_t size;
683*d0c94b83SXin Li void *dest;
684*d0c94b83SXin Li size_t total_count;
685*d0c94b83SXin Li
686*d0c94b83SXin Li if ((!ctl) || !count || !array || !ctl->grp)
687*d0c94b83SXin Li return -EINVAL;
688*d0c94b83SXin Li
689*d0c94b83SXin Li grp = ctl->grp;
690*d0c94b83SXin Li total_count = ctl->info->count;
691*d0c94b83SXin Li
692*d0c94b83SXin Li if ((ctl->info->type == SNDRV_CTL_ELEM_TYPE_BYTES) &&
693*d0c94b83SXin Li mixer_ctl_is_access_tlv_rw(ctl)) {
694*d0c94b83SXin Li /* Additional two words is for the TLV header */
695*d0c94b83SXin Li total_count += TLV_HEADER_SIZE;
696*d0c94b83SXin Li }
697*d0c94b83SXin Li
698*d0c94b83SXin Li if (count > total_count)
699*d0c94b83SXin Li return -EINVAL;
700*d0c94b83SXin Li
701*d0c94b83SXin Li memset(&ev, 0, sizeof(ev));
702*d0c94b83SXin Li ev.id.numid = ctl->info->id.numid;
703*d0c94b83SXin Li
704*d0c94b83SXin Li switch (ctl->info->type) {
705*d0c94b83SXin Li case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
706*d0c94b83SXin Li case SNDRV_CTL_ELEM_TYPE_INTEGER:
707*d0c94b83SXin Li size = sizeof(ev.value.integer.value[0]);
708*d0c94b83SXin Li dest = ev.value.integer.value;
709*d0c94b83SXin Li break;
710*d0c94b83SXin Li
711*d0c94b83SXin Li case SNDRV_CTL_ELEM_TYPE_BYTES:
712*d0c94b83SXin Li /* check if this is new bytes TLV */
713*d0c94b83SXin Li if (mixer_ctl_is_access_tlv_rw(ctl)) {
714*d0c94b83SXin Li struct snd_ctl_tlv *tlv;
715*d0c94b83SXin Li int ret = 0;
716*d0c94b83SXin Li if (count > SIZE_MAX - sizeof(*tlv))
717*d0c94b83SXin Li return -EINVAL;
718*d0c94b83SXin Li tlv = calloc(1, sizeof(*tlv) + count);
719*d0c94b83SXin Li if (!tlv)
720*d0c94b83SXin Li return -ENOMEM;
721*d0c94b83SXin Li tlv->numid = ctl->info->id.numid;
722*d0c94b83SXin Li tlv->length = count;
723*d0c94b83SXin Li memcpy(tlv->tlv, array, count);
724*d0c94b83SXin Li
725*d0c94b83SXin Li ret = grp->ops->ioctl(grp->data, SNDRV_CTL_IOCTL_TLV_WRITE, tlv);
726*d0c94b83SXin Li free(tlv);
727*d0c94b83SXin Li
728*d0c94b83SXin Li return ret;
729*d0c94b83SXin Li } else {
730*d0c94b83SXin Li size = sizeof(ev.value.bytes.data[0]);
731*d0c94b83SXin Li dest = ev.value.bytes.data;
732*d0c94b83SXin Li }
733*d0c94b83SXin Li break;
734*d0c94b83SXin Li
735*d0c94b83SXin Li case SNDRV_CTL_ELEM_TYPE_IEC958:
736*d0c94b83SXin Li size = sizeof(ev.value.iec958);
737*d0c94b83SXin Li dest = &ev.value.iec958;
738*d0c94b83SXin Li break;
739*d0c94b83SXin Li
740*d0c94b83SXin Li default:
741*d0c94b83SXin Li return -EINVAL;
742*d0c94b83SXin Li }
743*d0c94b83SXin Li
744*d0c94b83SXin Li memcpy(dest, array, size * count);
745*d0c94b83SXin Li
746*d0c94b83SXin Li return grp->ops->ioctl(grp->data, SNDRV_CTL_IOCTL_ELEM_WRITE, &ev);
747*d0c94b83SXin Li }
748*d0c94b83SXin Li
mixer_ctl_get_range_min(struct mixer_ctl * ctl)749*d0c94b83SXin Li int mixer_ctl_get_range_min(struct mixer_ctl *ctl)
750*d0c94b83SXin Li {
751*d0c94b83SXin Li if (!ctl || (ctl->info->type != SNDRV_CTL_ELEM_TYPE_INTEGER))
752*d0c94b83SXin Li return -EINVAL;
753*d0c94b83SXin Li
754*d0c94b83SXin Li return ctl->info->value.integer.min;
755*d0c94b83SXin Li }
756*d0c94b83SXin Li
mixer_ctl_get_range_max(struct mixer_ctl * ctl)757*d0c94b83SXin Li int mixer_ctl_get_range_max(struct mixer_ctl *ctl)
758*d0c94b83SXin Li {
759*d0c94b83SXin Li if (!ctl || (ctl->info->type != SNDRV_CTL_ELEM_TYPE_INTEGER))
760*d0c94b83SXin Li return -EINVAL;
761*d0c94b83SXin Li
762*d0c94b83SXin Li return ctl->info->value.integer.max;
763*d0c94b83SXin Li }
764*d0c94b83SXin Li
mixer_ctl_get_num_enums(struct mixer_ctl * ctl)765*d0c94b83SXin Li unsigned int mixer_ctl_get_num_enums(struct mixer_ctl *ctl)
766*d0c94b83SXin Li {
767*d0c94b83SXin Li if (!ctl)
768*d0c94b83SXin Li return 0;
769*d0c94b83SXin Li
770*d0c94b83SXin Li return ctl->info->value.enumerated.items;
771*d0c94b83SXin Li }
772*d0c94b83SXin Li
mixer_ctl_get_enum_string(struct mixer_ctl * ctl,unsigned int enum_id)773*d0c94b83SXin Li const char *mixer_ctl_get_enum_string(struct mixer_ctl *ctl,
774*d0c94b83SXin Li unsigned int enum_id)
775*d0c94b83SXin Li {
776*d0c94b83SXin Li if (!ctl || (ctl->info->type != SNDRV_CTL_ELEM_TYPE_ENUMERATED) ||
777*d0c94b83SXin Li (enum_id >= ctl->info->value.enumerated.items))
778*d0c94b83SXin Li return NULL;
779*d0c94b83SXin Li
780*d0c94b83SXin Li return (const char *)ctl->ename[enum_id];
781*d0c94b83SXin Li }
782*d0c94b83SXin Li
mixer_ctl_set_enum_by_string(struct mixer_ctl * ctl,const char * string)783*d0c94b83SXin Li int mixer_ctl_set_enum_by_string(struct mixer_ctl *ctl, const char *string)
784*d0c94b83SXin Li {
785*d0c94b83SXin Li struct mixer_ctl_group *grp;
786*d0c94b83SXin Li unsigned int i, num_enums;
787*d0c94b83SXin Li struct snd_ctl_elem_value ev;
788*d0c94b83SXin Li int ret;
789*d0c94b83SXin Li
790*d0c94b83SXin Li if (!ctl || (ctl->info->type != SNDRV_CTL_ELEM_TYPE_ENUMERATED) || !ctl->grp)
791*d0c94b83SXin Li return -EINVAL;
792*d0c94b83SXin Li
793*d0c94b83SXin Li grp = ctl->grp;
794*d0c94b83SXin Li num_enums = ctl->info->value.enumerated.items;
795*d0c94b83SXin Li for (i = 0; i < num_enums; i++) {
796*d0c94b83SXin Li if (!strcmp(string, ctl->ename[i])) {
797*d0c94b83SXin Li memset(&ev, 0, sizeof(ev));
798*d0c94b83SXin Li ev.value.enumerated.item[0] = i;
799*d0c94b83SXin Li ev.id.numid = ctl->info->id.numid;
800*d0c94b83SXin Li ret = grp->ops->ioctl(grp->data, SNDRV_CTL_IOCTL_ELEM_WRITE, &ev);
801*d0c94b83SXin Li if (ret < 0)
802*d0c94b83SXin Li return ret;
803*d0c94b83SXin Li return 0;
804*d0c94b83SXin Li }
805*d0c94b83SXin Li }
806*d0c94b83SXin Li
807*d0c94b83SXin Li return -EINVAL;
808*d0c94b83SXin Li }
809*d0c94b83SXin Li
810*d0c94b83SXin Li /** Subscribes for the mixer events.
811*d0c94b83SXin Li * @param mixer A mixer handle.
812*d0c94b83SXin Li * @param subscribe value indicating subscribe or unsubscribe for events
813*d0c94b83SXin Li * @returns On success, zero.
814*d0c94b83SXin Li * On failure, non-zero.
815*d0c94b83SXin Li * @ingroup libtinyalsa-mixer
816*d0c94b83SXin Li */
mixer_subscribe_events(struct mixer * mixer,int subscribe)817*d0c94b83SXin Li int mixer_subscribe_events(struct mixer *mixer, int subscribe)
818*d0c94b83SXin Li {
819*d0c94b83SXin Li struct mixer_ctl_group *grp;
820*d0c94b83SXin Li
821*d0c94b83SXin Li if (mixer->hw_grp) {
822*d0c94b83SXin Li grp = mixer->hw_grp;
823*d0c94b83SXin Li if (!subscribe)
824*d0c94b83SXin Li grp->event_cnt = 0;
825*d0c94b83SXin Li if (grp->ops->ioctl(grp->data, SNDRV_CTL_IOCTL_SUBSCRIBE_EVENTS, &subscribe) < 0)
826*d0c94b83SXin Li return -1;
827*d0c94b83SXin Li }
828*d0c94b83SXin Li
829*d0c94b83SXin Li if (mixer->virt_grp) {
830*d0c94b83SXin Li grp = mixer->virt_grp;
831*d0c94b83SXin Li if (!subscribe)
832*d0c94b83SXin Li grp->event_cnt = 0;
833*d0c94b83SXin Li if (grp->ops->ioctl(grp->data, SNDRV_CTL_IOCTL_SUBSCRIBE_EVENTS, &subscribe) < 0)
834*d0c94b83SXin Li return -1;
835*d0c94b83SXin Li }
836*d0c94b83SXin Li return 0;
837*d0c94b83SXin Li }
838*d0c94b83SXin Li
839*d0c94b83SXin Li /** Wait for mixer events.
840*d0c94b83SXin Li * @param mixer A mixer handle.
841*d0c94b83SXin Li * @param timeout timout value
842*d0c94b83SXin Li * @returns On success, 1.
843*d0c94b83SXin Li * On failure, -errno.
844*d0c94b83SXin Li * On timeout, 0
845*d0c94b83SXin Li * @ingroup libtinyalsa-mixer
846*d0c94b83SXin Li */
mixer_wait_event(struct mixer * mixer,int timeout)847*d0c94b83SXin Li int mixer_wait_event(struct mixer *mixer, int timeout)
848*d0c94b83SXin Li {
849*d0c94b83SXin Li struct pollfd pfd[2];
850*d0c94b83SXin Li struct mixer_ctl_group *grp;
851*d0c94b83SXin Li int count = 0, num_fds = 0, i;
852*d0c94b83SXin Li
853*d0c94b83SXin Li memset(pfd, 0, sizeof(struct pollfd) * 2);
854*d0c94b83SXin Li
855*d0c94b83SXin Li if (mixer->fd >= 0)
856*d0c94b83SXin Li num_fds++;
857*d0c94b83SXin Li
858*d0c94b83SXin Li if (mixer->virt_grp)
859*d0c94b83SXin Li num_fds++;
860*d0c94b83SXin Li
861*d0c94b83SXin Li
862*d0c94b83SXin Li /* TODO wait events for virt fd */
863*d0c94b83SXin Li if (mixer->fd >= 0) {
864*d0c94b83SXin Li pfd[count].fd = mixer->fd;
865*d0c94b83SXin Li pfd[count].events = POLLIN | POLLOUT | POLLERR | POLLNVAL;
866*d0c94b83SXin Li count++;
867*d0c94b83SXin Li }
868*d0c94b83SXin Li
869*d0c94b83SXin Li if (mixer->virt_grp) {
870*d0c94b83SXin Li grp = mixer->virt_grp;
871*d0c94b83SXin Li if (!grp->ops->get_poll_fd(grp->data, pfd, count)) {
872*d0c94b83SXin Li pfd[count].events = POLLIN | POLLERR | POLLNVAL;
873*d0c94b83SXin Li count++;
874*d0c94b83SXin Li }
875*d0c94b83SXin Li }
876*d0c94b83SXin Li
877*d0c94b83SXin Li if (!count)
878*d0c94b83SXin Li return 0;
879*d0c94b83SXin Li
880*d0c94b83SXin Li for (;;) {
881*d0c94b83SXin Li int err;
882*d0c94b83SXin Li err = poll(pfd, count, timeout);
883*d0c94b83SXin Li if (err < 0)
884*d0c94b83SXin Li return -errno;
885*d0c94b83SXin Li if (!err)
886*d0c94b83SXin Li return 0;
887*d0c94b83SXin Li for (i = 0; i < count; i++) {
888*d0c94b83SXin Li if (pfd[i].revents & (POLLERR | POLLNVAL))
889*d0c94b83SXin Li return -EIO;
890*d0c94b83SXin Li if (pfd[i].revents & (POLLIN | POLLOUT)) {
891*d0c94b83SXin Li if ((i == 0) && mixer->fd >= 0) {
892*d0c94b83SXin Li grp = mixer->hw_grp;
893*d0c94b83SXin Li grp->event_cnt++;
894*d0c94b83SXin Li } else {
895*d0c94b83SXin Li grp = mixer->virt_grp;
896*d0c94b83SXin Li grp->event_cnt++;
897*d0c94b83SXin Li }
898*d0c94b83SXin Li return 1;
899*d0c94b83SXin Li }
900*d0c94b83SXin Li }
901*d0c94b83SXin Li }
902*d0c94b83SXin Li }
903*d0c94b83SXin Li
904*d0c94b83SXin Li /** Consume a mixer event.
905*d0c94b83SXin Li * If mixer_subscribe_events has been called,
906*d0c94b83SXin Li * mixer_wait_event will identify when a control value has changed.
907*d0c94b83SXin Li * This function will clear a single event from the mixer so that
908*d0c94b83SXin Li * further events can be alerted.
909*d0c94b83SXin Li *
910*d0c94b83SXin Li * @param mixer A mixer handle.
911*d0c94b83SXin Li * @returns 0 on success. -errno on failure.
912*d0c94b83SXin Li * @ingroup libtinyalsa-mixer
913*d0c94b83SXin Li */
mixer_consume_event(struct mixer * mixer)914*d0c94b83SXin Li int mixer_consume_event(struct mixer *mixer)
915*d0c94b83SXin Li {
916*d0c94b83SXin Li struct snd_ctl_event ev;
917*d0c94b83SXin Li struct mixer_ctl_group *grp;
918*d0c94b83SXin Li ssize_t count = 0;
919*d0c94b83SXin Li
920*d0c94b83SXin Li // Exporting the actual event would require exposing snd_ctl_event
921*d0c94b83SXin Li // via the header file, and all associated structs.
922*d0c94b83SXin Li // The events generally tell you exactly which value changed,
923*d0c94b83SXin Li // but reading values you're interested isn't hard and simplifies
924*d0c94b83SXin Li // the interface greatly.
925*d0c94b83SXin Li if (mixer->hw_grp) {
926*d0c94b83SXin Li grp = mixer->hw_grp;
927*d0c94b83SXin Li if (grp->event_cnt) {
928*d0c94b83SXin Li grp->event_cnt--;
929*d0c94b83SXin Li count = grp->ops->read_event(grp->data, &ev, sizeof(ev));
930*d0c94b83SXin Li return (count >= 0) ? 0 : -errno;
931*d0c94b83SXin Li }
932*d0c94b83SXin Li }
933*d0c94b83SXin Li
934*d0c94b83SXin Li if (mixer->virt_grp) {
935*d0c94b83SXin Li grp = mixer->virt_grp;
936*d0c94b83SXin Li if (grp->event_cnt) {
937*d0c94b83SXin Li grp->event_cnt--;
938*d0c94b83SXin Li count += grp->ops->read_event(grp->data, &ev, sizeof(ev));
939*d0c94b83SXin Li return (count >= 0) ? 0 : -errno;
940*d0c94b83SXin Li }
941*d0c94b83SXin Li }
942*d0c94b83SXin Li return 0;
943*d0c94b83SXin Li }
944*d0c94b83SXin Li
945*d0c94b83SXin Li /** Read a mixer event.
946*d0c94b83SXin Li * If mixer_subscribe_events has been called,
947*d0c94b83SXin Li * mixer_wait_event will identify when a control value has changed.
948*d0c94b83SXin Li * This function will read and clear a single event from the mixer
949*d0c94b83SXin Li * so that further events can be alerted.
950*d0c94b83SXin Li *
951*d0c94b83SXin Li * @param mixer A mixer handle.
952*d0c94b83SXin Li * @param ev snd_ctl_event pointer where event needs to be read
953*d0c94b83SXin Li * @returns 0 on success. -errno on failure.
954*d0c94b83SXin Li * @ingroup libtinyalsa-mixer
955*d0c94b83SXin Li */
mixer_read_event(struct mixer * mixer,struct ctl_event * ev)956*d0c94b83SXin Li int mixer_read_event(struct mixer *mixer, struct ctl_event *ev)
957*d0c94b83SXin Li {
958*d0c94b83SXin Li struct mixer_ctl_group *grp;
959*d0c94b83SXin Li ssize_t count = 0;
960*d0c94b83SXin Li
961*d0c94b83SXin Li if (mixer->hw_grp) {
962*d0c94b83SXin Li grp = mixer->hw_grp;
963*d0c94b83SXin Li if (grp->event_cnt) {
964*d0c94b83SXin Li grp->event_cnt--;
965*d0c94b83SXin Li count = grp->ops->read_event(grp->data, (struct snd_ctl_event *)ev, sizeof(*ev));
966*d0c94b83SXin Li return (count >= 0) ? 0 : -errno;
967*d0c94b83SXin Li }
968*d0c94b83SXin Li }
969*d0c94b83SXin Li
970*d0c94b83SXin Li if (mixer->virt_grp) {
971*d0c94b83SXin Li grp = mixer->virt_grp;
972*d0c94b83SXin Li if (grp->event_cnt) {
973*d0c94b83SXin Li grp->event_cnt--;
974*d0c94b83SXin Li count = grp->ops->read_event(grp->data, (struct snd_ctl_event *)ev, sizeof(*ev));
975*d0c94b83SXin Li return (count >= 0) ? 0 : -errno;
976*d0c94b83SXin Li }
977*d0c94b83SXin Li }
978*d0c94b83SXin Li return 0;
979*d0c94b83SXin Li }
980