xref: /aosp_15_r20/external/tinyalsa/mixer.c (revision d0c94b832dfb3062bf15d9baaf64123fc670b06c)
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