~binli/ubuntu/vivid/pulseaudio/load-extcon-module

« back to all changes in this revision

Viewing changes to src/modules/alsa/alsa-mixer.c

  • Committer: Bin Li
  • Date: 2016-01-23 15:04:48 UTC
  • Revision ID: bin.li@canonical.com-20160123150448-5ockvw4p5xxntda4
init the 1:6.0-0ubuntu9.15 from silo 12

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/***
 
2
  This file is part of PulseAudio.
 
3
 
 
4
  Copyright 2004-2009 Lennart Poettering
 
5
  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
 
6
 
 
7
  PulseAudio is free software; you can redistribute it and/or modify
 
8
  it under the terms of the GNU Lesser General Public License as published
 
9
  by the Free Software Foundation; either version 2.1 of the License,
 
10
  or (at your option) any later version.
 
11
 
 
12
  PulseAudio is distributed in the hope that it will be useful, but
 
13
  WITHOUT ANY WARRANTY; without even the implied warranty of
 
14
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 
15
  General Public License for more details.
 
16
 
 
17
  You should have received a copy of the GNU Lesser General Public License
 
18
  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
 
19
***/
 
20
 
 
21
#ifdef HAVE_CONFIG_H
 
22
#include <config.h>
 
23
#endif
 
24
 
 
25
#include <sys/types.h>
 
26
#include <asoundlib.h>
 
27
#include <math.h>
 
28
 
 
29
#ifdef HAVE_VALGRIND_MEMCHECK_H
 
30
#include <valgrind/memcheck.h>
 
31
#endif
 
32
 
 
33
#include <pulse/mainloop-api.h>
 
34
#include <pulse/sample.h>
 
35
#include <pulse/timeval.h>
 
36
#include <pulse/util.h>
 
37
#include <pulse/volume.h>
 
38
#include <pulse/xmalloc.h>
 
39
#include <pulse/utf8.h>
 
40
 
 
41
#include <pulsecore/i18n.h>
 
42
#include <pulsecore/log.h>
 
43
#include <pulsecore/macro.h>
 
44
#include <pulsecore/core-util.h>
 
45
#include <pulsecore/conf-parser.h>
 
46
#include <pulsecore/strbuf.h>
 
47
 
 
48
#include "alsa-mixer.h"
 
49
#include "alsa-util.h"
 
50
 
 
51
#ifdef HAVE_VALGRIND_MEMCHECK_H
 
52
/* These macros are workarounds for a bug in valgrind, which is not handling the
 
53
 * ALSA TLV syscalls correctly. See
 
54
 * http://valgrind.10908.n7.nabble.com/Missing-ioctl-for-SNDRV-CTL-IOCTL-TLV-READ-td42711.html */
 
55
 
 
56
static inline int vgfix_get_capture_dB(snd_mixer_elem_t *a, snd_mixer_selem_channel_id_t b, long *c) {
 
57
    int r = snd_mixer_selem_get_capture_dB(a, b, c);
 
58
    VALGRIND_MAKE_MEM_DEFINED(c, sizeof(*c));
 
59
    return r;
 
60
}
 
61
 
 
62
static inline int vgfix_get_playback_dB(snd_mixer_elem_t *a, snd_mixer_selem_channel_id_t b, long *c) {
 
63
    int r = snd_mixer_selem_get_playback_dB(a, b, c);
 
64
    VALGRIND_MAKE_MEM_DEFINED(c, sizeof(*c));
 
65
    return r;
 
66
}
 
67
 
 
68
static inline int vgfix_ask_capture_vol_dB(snd_mixer_elem_t *a, long b, long *c) {
 
69
    int r = snd_mixer_selem_ask_capture_vol_dB(a, b, c);
 
70
    VALGRIND_MAKE_MEM_DEFINED(c, sizeof(*c));
 
71
    return r;
 
72
}
 
73
 
 
74
static inline int vgfix_ask_playback_vol_dB(snd_mixer_elem_t *a, long b, long *c) {
 
75
    int r = snd_mixer_selem_ask_playback_vol_dB(a, b, c);
 
76
    VALGRIND_MAKE_MEM_DEFINED(c, sizeof(*c));
 
77
    return r;
 
78
}
 
79
 
 
80
static inline int vgfix_get_capture_dB_range(snd_mixer_elem_t *a, long *b, long *c) {
 
81
    int r = snd_mixer_selem_get_capture_dB_range(a, b, c);
 
82
    VALGRIND_MAKE_MEM_DEFINED(b, sizeof(*b));
 
83
    VALGRIND_MAKE_MEM_DEFINED(c, sizeof(*c));
 
84
    return r;
 
85
}
 
86
 
 
87
static inline int vgfix_get_playback_dB_range(snd_mixer_elem_t *a, long *b, long *c) {
 
88
    int r = snd_mixer_selem_get_playback_dB_range(a, b, c);
 
89
    VALGRIND_MAKE_MEM_DEFINED(b, sizeof(*b));
 
90
    VALGRIND_MAKE_MEM_DEFINED(c, sizeof(*c));
 
91
    return r;
 
92
}
 
93
 
 
94
#define snd_mixer_selem_get_capture_dB(a, b, c) vgfix_get_capture_dB(a, b, c)
 
95
#define snd_mixer_selem_get_playback_dB(a, b, c) vgfix_get_playback_dB(a, b, c)
 
96
#define snd_mixer_selem_ask_capture_vol_dB(a, b, c) vgfix_ask_capture_vol_dB(a, b, c)
 
97
#define snd_mixer_selem_ask_playback_vol_dB(a, b, c) vgfix_ask_playback_vol_dB(a, b, c)
 
98
#define snd_mixer_selem_get_capture_dB_range(a, b, c) vgfix_get_capture_dB_range(a, b, c)
 
99
#define snd_mixer_selem_get_playback_dB_range(a, b, c) vgfix_get_playback_dB_range(a, b, c)
 
100
 
 
101
#endif
 
102
 
 
103
static int setting_select(pa_alsa_setting *s, snd_mixer_t *m);
 
104
 
 
105
struct description_map {
 
106
    const char *key;
 
107
    const char *description;
 
108
};
 
109
 
 
110
static const char *lookup_description(const char *key, const struct description_map dm[], unsigned n) {
 
111
    unsigned i;
 
112
 
 
113
    if (!key)
 
114
        return NULL;
 
115
 
 
116
    for (i = 0; i < n; i++)
 
117
        if (pa_streq(dm[i].key, key))
 
118
            return _(dm[i].description);
 
119
 
 
120
    return NULL;
 
121
}
 
122
 
 
123
struct pa_alsa_fdlist {
 
124
    unsigned num_fds;
 
125
    struct pollfd *fds;
 
126
    /* This is a temporary buffer used to avoid lots of mallocs */
 
127
    struct pollfd *work_fds;
 
128
 
 
129
    snd_mixer_t *mixer;
 
130
    snd_hctl_t *hctl;
 
131
 
 
132
    pa_mainloop_api *m;
 
133
    pa_defer_event *defer;
 
134
    pa_io_event **ios;
 
135
 
 
136
    bool polled;
 
137
 
 
138
    void (*cb)(void *userdata);
 
139
    void *userdata;
 
140
};
 
141
 
 
142
static void io_cb(pa_mainloop_api *a, pa_io_event *e, int fd, pa_io_event_flags_t events, void *userdata) {
 
143
 
 
144
    struct pa_alsa_fdlist *fdl = userdata;
 
145
    int err;
 
146
    unsigned i;
 
147
    unsigned short revents;
 
148
 
 
149
    pa_assert(a);
 
150
    pa_assert(fdl);
 
151
    pa_assert(fdl->mixer || fdl->hctl);
 
152
    pa_assert(fdl->fds);
 
153
    pa_assert(fdl->work_fds);
 
154
 
 
155
    if (fdl->polled)
 
156
        return;
 
157
 
 
158
    fdl->polled = true;
 
159
 
 
160
    memcpy(fdl->work_fds, fdl->fds, sizeof(struct pollfd) * fdl->num_fds);
 
161
 
 
162
    for (i = 0; i < fdl->num_fds; i++) {
 
163
        if (e == fdl->ios[i]) {
 
164
            if (events & PA_IO_EVENT_INPUT)
 
165
                fdl->work_fds[i].revents |= POLLIN;
 
166
            if (events & PA_IO_EVENT_OUTPUT)
 
167
                fdl->work_fds[i].revents |= POLLOUT;
 
168
            if (events & PA_IO_EVENT_ERROR)
 
169
                fdl->work_fds[i].revents |= POLLERR;
 
170
            if (events & PA_IO_EVENT_HANGUP)
 
171
                fdl->work_fds[i].revents |= POLLHUP;
 
172
            break;
 
173
        }
 
174
    }
 
175
 
 
176
    pa_assert(i != fdl->num_fds);
 
177
 
 
178
    if (fdl->hctl)
 
179
        err = snd_hctl_poll_descriptors_revents(fdl->hctl, fdl->work_fds, fdl->num_fds, &revents);
 
180
    else
 
181
        err = snd_mixer_poll_descriptors_revents(fdl->mixer, fdl->work_fds, fdl->num_fds, &revents);
 
182
 
 
183
    if (err < 0) {
 
184
        pa_log_error("Unable to get poll revent: %s", pa_alsa_strerror(err));
 
185
        return;
 
186
    }
 
187
 
 
188
    a->defer_enable(fdl->defer, 1);
 
189
 
 
190
    if (revents) {
 
191
        if (fdl->hctl)
 
192
            snd_hctl_handle_events(fdl->hctl);
 
193
        else
 
194
            snd_mixer_handle_events(fdl->mixer);
 
195
    }
 
196
}
 
197
 
 
198
static void defer_cb(pa_mainloop_api *a, pa_defer_event *e, void *userdata) {
 
199
    struct pa_alsa_fdlist *fdl = userdata;
 
200
    unsigned num_fds, i;
 
201
    int err, n;
 
202
    struct pollfd *temp;
 
203
 
 
204
    pa_assert(a);
 
205
    pa_assert(fdl);
 
206
    pa_assert(fdl->mixer || fdl->hctl);
 
207
 
 
208
    a->defer_enable(fdl->defer, 0);
 
209
 
 
210
    if (fdl->hctl)
 
211
        n = snd_hctl_poll_descriptors_count(fdl->hctl);
 
212
    else
 
213
        n = snd_mixer_poll_descriptors_count(fdl->mixer);
 
214
 
 
215
    if (n < 0) {
 
216
        pa_log("snd_mixer_poll_descriptors_count() failed: %s", pa_alsa_strerror(n));
 
217
        return;
 
218
    } else if (n == 0) {
 
219
        pa_log("snd_mixer_poll_descriptors_count() equal 0");
 
220
        return;
 
221
    }
 
222
    num_fds = (unsigned) n;
 
223
 
 
224
    if (num_fds != fdl->num_fds) {
 
225
        if (fdl->fds)
 
226
            pa_xfree(fdl->fds);
 
227
        if (fdl->work_fds)
 
228
            pa_xfree(fdl->work_fds);
 
229
        fdl->fds = pa_xnew0(struct pollfd, num_fds);
 
230
        fdl->work_fds = pa_xnew(struct pollfd, num_fds);
 
231
    }
 
232
 
 
233
    memset(fdl->work_fds, 0, sizeof(struct pollfd) * num_fds);
 
234
 
 
235
    if (fdl->hctl)
 
236
        err = snd_hctl_poll_descriptors(fdl->hctl, fdl->work_fds, num_fds);
 
237
    else
 
238
        err = snd_mixer_poll_descriptors(fdl->mixer, fdl->work_fds, num_fds);
 
239
 
 
240
    if (err < 0) {
 
241
        pa_log_error("Unable to get poll descriptors: %s", pa_alsa_strerror(err));
 
242
        return;
 
243
    }
 
244
 
 
245
    fdl->polled = false;
 
246
 
 
247
    if (memcmp(fdl->fds, fdl->work_fds, sizeof(struct pollfd) * num_fds) == 0)
 
248
        return;
 
249
 
 
250
    if (fdl->ios) {
 
251
        for (i = 0; i < fdl->num_fds; i++)
 
252
            a->io_free(fdl->ios[i]);
 
253
 
 
254
        if (num_fds != fdl->num_fds) {
 
255
            pa_xfree(fdl->ios);
 
256
            fdl->ios = NULL;
 
257
        }
 
258
    }
 
259
 
 
260
    if (!fdl->ios)
 
261
        fdl->ios = pa_xnew(pa_io_event*, num_fds);
 
262
 
 
263
    /* Swap pointers */
 
264
    temp = fdl->work_fds;
 
265
    fdl->work_fds = fdl->fds;
 
266
    fdl->fds = temp;
 
267
 
 
268
    fdl->num_fds = num_fds;
 
269
 
 
270
    for (i = 0;i < num_fds;i++)
 
271
        fdl->ios[i] = a->io_new(a, fdl->fds[i].fd,
 
272
            ((fdl->fds[i].events & POLLIN) ? PA_IO_EVENT_INPUT : 0) |
 
273
            ((fdl->fds[i].events & POLLOUT) ? PA_IO_EVENT_OUTPUT : 0),
 
274
            io_cb, fdl);
 
275
}
 
276
 
 
277
struct pa_alsa_fdlist *pa_alsa_fdlist_new(void) {
 
278
    struct pa_alsa_fdlist *fdl;
 
279
 
 
280
    fdl = pa_xnew0(struct pa_alsa_fdlist, 1);
 
281
 
 
282
    return fdl;
 
283
}
 
284
 
 
285
void pa_alsa_fdlist_free(struct pa_alsa_fdlist *fdl) {
 
286
    pa_assert(fdl);
 
287
 
 
288
    if (fdl->defer) {
 
289
        pa_assert(fdl->m);
 
290
        fdl->m->defer_free(fdl->defer);
 
291
    }
 
292
 
 
293
    if (fdl->ios) {
 
294
        unsigned i;
 
295
        pa_assert(fdl->m);
 
296
        for (i = 0; i < fdl->num_fds; i++)
 
297
            fdl->m->io_free(fdl->ios[i]);
 
298
        pa_xfree(fdl->ios);
 
299
    }
 
300
 
 
301
    if (fdl->fds)
 
302
        pa_xfree(fdl->fds);
 
303
    if (fdl->work_fds)
 
304
        pa_xfree(fdl->work_fds);
 
305
 
 
306
    pa_xfree(fdl);
 
307
}
 
308
 
 
309
/* We can listen to either a snd_hctl_t or a snd_mixer_t, but not both */
 
310
int pa_alsa_fdlist_set_handle(struct pa_alsa_fdlist *fdl, snd_mixer_t *mixer_handle, snd_hctl_t *hctl_handle, pa_mainloop_api *m) {
 
311
    pa_assert(fdl);
 
312
    pa_assert(hctl_handle || mixer_handle);
 
313
    pa_assert(!(hctl_handle && mixer_handle));
 
314
    pa_assert(m);
 
315
    pa_assert(!fdl->m);
 
316
 
 
317
    fdl->hctl = hctl_handle;
 
318
    fdl->mixer = mixer_handle;
 
319
    fdl->m = m;
 
320
    fdl->defer = m->defer_new(m, defer_cb, fdl);
 
321
 
 
322
    return 0;
 
323
}
 
324
 
 
325
struct pa_alsa_mixer_pdata {
 
326
    pa_rtpoll *rtpoll;
 
327
    pa_rtpoll_item *poll_item;
 
328
    snd_mixer_t *mixer;
 
329
};
 
330
 
 
331
struct pa_alsa_mixer_pdata *pa_alsa_mixer_pdata_new(void) {
 
332
    struct pa_alsa_mixer_pdata *pd;
 
333
 
 
334
    pd = pa_xnew0(struct pa_alsa_mixer_pdata, 1);
 
335
 
 
336
    return pd;
 
337
}
 
338
 
 
339
void pa_alsa_mixer_pdata_free(struct pa_alsa_mixer_pdata *pd) {
 
340
    pa_assert(pd);
 
341
 
 
342
    if (pd->poll_item) {
 
343
        pa_rtpoll_item_free(pd->poll_item);
 
344
    }
 
345
 
 
346
    pa_xfree(pd);
 
347
}
 
348
 
 
349
static int rtpoll_work_cb(pa_rtpoll_item *i) {
 
350
    struct pa_alsa_mixer_pdata *pd;
 
351
    struct pollfd *p;
 
352
    unsigned n_fds;
 
353
    unsigned short revents = 0;
 
354
    int err, ret = 0;
 
355
 
 
356
    pd = pa_rtpoll_item_get_userdata(i);
 
357
    pa_assert_fp(pd);
 
358
    pa_assert_fp(i == pd->poll_item);
 
359
 
 
360
    p = pa_rtpoll_item_get_pollfd(i, &n_fds);
 
361
 
 
362
    if ((err = snd_mixer_poll_descriptors_revents(pd->mixer, p, n_fds, &revents)) < 0) {
 
363
        pa_log_error("Unable to get poll revent: %s", pa_alsa_strerror(err));
 
364
        ret = -1;
 
365
        goto fail;
 
366
    }
 
367
 
 
368
    if (revents) {
 
369
        if (revents & (POLLNVAL | POLLERR)) {
 
370
            pa_log_debug("Device disconnected, stopping poll on mixer");
 
371
            goto fail;
 
372
        } else if (revents & POLLERR) {
 
373
            /* This shouldn't happen. */
 
374
            pa_log_error("Got a POLLERR (revents = %04x), stopping poll on mixer", revents);
 
375
            goto fail;
 
376
        }
 
377
 
 
378
        err = snd_mixer_handle_events(pd->mixer);
 
379
 
 
380
        if (PA_LIKELY(err >= 0)) {
 
381
            pa_rtpoll_item_free(i);
 
382
            pa_alsa_set_mixer_rtpoll(pd, pd->mixer, pd->rtpoll);
 
383
        } else {
 
384
            pa_log_error("Error handling mixer event: %s", pa_alsa_strerror(err));
 
385
            ret = -1;
 
386
            goto fail;
 
387
        }
 
388
    }
 
389
 
 
390
    return ret;
 
391
 
 
392
fail:
 
393
    pa_rtpoll_item_free(i);
 
394
 
 
395
    pd->poll_item = NULL;
 
396
    pd->rtpoll = NULL;
 
397
    pd->mixer = NULL;
 
398
 
 
399
    return ret;
 
400
}
 
401
 
 
402
int pa_alsa_set_mixer_rtpoll(struct pa_alsa_mixer_pdata *pd, snd_mixer_t *mixer, pa_rtpoll *rtp) {
 
403
    pa_rtpoll_item *i;
 
404
    struct pollfd *p;
 
405
    int err, n;
 
406
 
 
407
    pa_assert(pd);
 
408
    pa_assert(mixer);
 
409
    pa_assert(rtp);
 
410
 
 
411
    if ((n = snd_mixer_poll_descriptors_count(mixer)) < 0) {
 
412
        pa_log("snd_mixer_poll_descriptors_count() failed: %s", pa_alsa_strerror(n));
 
413
        return -1;
 
414
    }
 
415
 
 
416
    i = pa_rtpoll_item_new(rtp, PA_RTPOLL_LATE, (unsigned) n);
 
417
 
 
418
    p = pa_rtpoll_item_get_pollfd(i, NULL);
 
419
 
 
420
    memset(p, 0, sizeof(struct pollfd) * n);
 
421
 
 
422
    if ((err = snd_mixer_poll_descriptors(mixer, p, (unsigned) n)) < 0) {
 
423
        pa_log_error("Unable to get poll descriptors: %s", pa_alsa_strerror(err));
 
424
        pa_rtpoll_item_free(i);
 
425
        return -1;
 
426
    }
 
427
 
 
428
    pd->rtpoll = rtp;
 
429
    pd->poll_item = i;
 
430
    pd->mixer = mixer;
 
431
 
 
432
    pa_rtpoll_item_set_userdata(i, pd);
 
433
    pa_rtpoll_item_set_work_callback(i, rtpoll_work_cb);
 
434
 
 
435
    return 0;
 
436
}
 
437
 
 
438
static const snd_mixer_selem_channel_id_t alsa_channel_ids[PA_CHANNEL_POSITION_MAX] = {
 
439
    [PA_CHANNEL_POSITION_MONO] = SND_MIXER_SCHN_MONO, /* The ALSA name is just an alias! */
 
440
 
 
441
    [PA_CHANNEL_POSITION_FRONT_CENTER] = SND_MIXER_SCHN_FRONT_CENTER,
 
442
    [PA_CHANNEL_POSITION_FRONT_LEFT] = SND_MIXER_SCHN_FRONT_LEFT,
 
443
    [PA_CHANNEL_POSITION_FRONT_RIGHT] = SND_MIXER_SCHN_FRONT_RIGHT,
 
444
 
 
445
    [PA_CHANNEL_POSITION_REAR_CENTER] = SND_MIXER_SCHN_REAR_CENTER,
 
446
    [PA_CHANNEL_POSITION_REAR_LEFT] = SND_MIXER_SCHN_REAR_LEFT,
 
447
    [PA_CHANNEL_POSITION_REAR_RIGHT] = SND_MIXER_SCHN_REAR_RIGHT,
 
448
 
 
449
    [PA_CHANNEL_POSITION_LFE] = SND_MIXER_SCHN_WOOFER,
 
450
 
 
451
    [PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER] = SND_MIXER_SCHN_UNKNOWN,
 
452
    [PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER] = SND_MIXER_SCHN_UNKNOWN,
 
453
 
 
454
    [PA_CHANNEL_POSITION_SIDE_LEFT] = SND_MIXER_SCHN_SIDE_LEFT,
 
455
    [PA_CHANNEL_POSITION_SIDE_RIGHT] = SND_MIXER_SCHN_SIDE_RIGHT,
 
456
 
 
457
    [PA_CHANNEL_POSITION_AUX0] = SND_MIXER_SCHN_UNKNOWN,
 
458
    [PA_CHANNEL_POSITION_AUX1] = SND_MIXER_SCHN_UNKNOWN,
 
459
    [PA_CHANNEL_POSITION_AUX2] = SND_MIXER_SCHN_UNKNOWN,
 
460
    [PA_CHANNEL_POSITION_AUX3] = SND_MIXER_SCHN_UNKNOWN,
 
461
    [PA_CHANNEL_POSITION_AUX4] = SND_MIXER_SCHN_UNKNOWN,
 
462
    [PA_CHANNEL_POSITION_AUX5] = SND_MIXER_SCHN_UNKNOWN,
 
463
    [PA_CHANNEL_POSITION_AUX6] = SND_MIXER_SCHN_UNKNOWN,
 
464
    [PA_CHANNEL_POSITION_AUX7] = SND_MIXER_SCHN_UNKNOWN,
 
465
    [PA_CHANNEL_POSITION_AUX8] = SND_MIXER_SCHN_UNKNOWN,
 
466
    [PA_CHANNEL_POSITION_AUX9] =  SND_MIXER_SCHN_UNKNOWN,
 
467
    [PA_CHANNEL_POSITION_AUX10] = SND_MIXER_SCHN_UNKNOWN,
 
468
    [PA_CHANNEL_POSITION_AUX11] = SND_MIXER_SCHN_UNKNOWN,
 
469
    [PA_CHANNEL_POSITION_AUX12] = SND_MIXER_SCHN_UNKNOWN,
 
470
    [PA_CHANNEL_POSITION_AUX13] = SND_MIXER_SCHN_UNKNOWN,
 
471
    [PA_CHANNEL_POSITION_AUX14] = SND_MIXER_SCHN_UNKNOWN,
 
472
    [PA_CHANNEL_POSITION_AUX15] = SND_MIXER_SCHN_UNKNOWN,
 
473
    [PA_CHANNEL_POSITION_AUX16] = SND_MIXER_SCHN_UNKNOWN,
 
474
    [PA_CHANNEL_POSITION_AUX17] = SND_MIXER_SCHN_UNKNOWN,
 
475
    [PA_CHANNEL_POSITION_AUX18] = SND_MIXER_SCHN_UNKNOWN,
 
476
    [PA_CHANNEL_POSITION_AUX19] = SND_MIXER_SCHN_UNKNOWN,
 
477
    [PA_CHANNEL_POSITION_AUX20] = SND_MIXER_SCHN_UNKNOWN,
 
478
    [PA_CHANNEL_POSITION_AUX21] = SND_MIXER_SCHN_UNKNOWN,
 
479
    [PA_CHANNEL_POSITION_AUX22] = SND_MIXER_SCHN_UNKNOWN,
 
480
    [PA_CHANNEL_POSITION_AUX23] = SND_MIXER_SCHN_UNKNOWN,
 
481
    [PA_CHANNEL_POSITION_AUX24] = SND_MIXER_SCHN_UNKNOWN,
 
482
    [PA_CHANNEL_POSITION_AUX25] = SND_MIXER_SCHN_UNKNOWN,
 
483
    [PA_CHANNEL_POSITION_AUX26] = SND_MIXER_SCHN_UNKNOWN,
 
484
    [PA_CHANNEL_POSITION_AUX27] = SND_MIXER_SCHN_UNKNOWN,
 
485
    [PA_CHANNEL_POSITION_AUX28] = SND_MIXER_SCHN_UNKNOWN,
 
486
    [PA_CHANNEL_POSITION_AUX29] = SND_MIXER_SCHN_UNKNOWN,
 
487
    [PA_CHANNEL_POSITION_AUX30] = SND_MIXER_SCHN_UNKNOWN,
 
488
    [PA_CHANNEL_POSITION_AUX31] = SND_MIXER_SCHN_UNKNOWN,
 
489
 
 
490
    [PA_CHANNEL_POSITION_TOP_CENTER] = SND_MIXER_SCHN_UNKNOWN,
 
491
 
 
492
    [PA_CHANNEL_POSITION_TOP_FRONT_CENTER] = SND_MIXER_SCHN_UNKNOWN,
 
493
    [PA_CHANNEL_POSITION_TOP_FRONT_LEFT] = SND_MIXER_SCHN_UNKNOWN,
 
494
    [PA_CHANNEL_POSITION_TOP_FRONT_RIGHT] = SND_MIXER_SCHN_UNKNOWN,
 
495
 
 
496
    [PA_CHANNEL_POSITION_TOP_REAR_CENTER] = SND_MIXER_SCHN_UNKNOWN,
 
497
    [PA_CHANNEL_POSITION_TOP_REAR_LEFT] = SND_MIXER_SCHN_UNKNOWN,
 
498
    [PA_CHANNEL_POSITION_TOP_REAR_RIGHT] = SND_MIXER_SCHN_UNKNOWN
 
499
};
 
500
 
 
501
static void setting_free(pa_alsa_setting *s) {
 
502
    pa_assert(s);
 
503
 
 
504
    if (s->options)
 
505
        pa_idxset_free(s->options, NULL);
 
506
 
 
507
    pa_xfree(s->name);
 
508
    pa_xfree(s->description);
 
509
    pa_xfree(s);
 
510
}
 
511
 
 
512
static void option_free(pa_alsa_option *o) {
 
513
    pa_assert(o);
 
514
 
 
515
    pa_xfree(o->alsa_name);
 
516
    pa_xfree(o->name);
 
517
    pa_xfree(o->description);
 
518
    pa_xfree(o);
 
519
}
 
520
 
 
521
static void decibel_fix_free(pa_alsa_decibel_fix *db_fix) {
 
522
    pa_assert(db_fix);
 
523
 
 
524
    pa_xfree(db_fix->name);
 
525
    pa_xfree(db_fix->db_values);
 
526
 
 
527
    pa_xfree(db_fix);
 
528
}
 
529
 
 
530
static void jack_free(pa_alsa_jack *j) {
 
531
    pa_assert(j);
 
532
 
 
533
    pa_xfree(j->alsa_name);
 
534
    pa_xfree(j->name);
 
535
    pa_xfree(j);
 
536
}
 
537
 
 
538
static void element_free(pa_alsa_element *e) {
 
539
    pa_alsa_option *o;
 
540
    pa_assert(e);
 
541
 
 
542
    while ((o = e->options)) {
 
543
        PA_LLIST_REMOVE(pa_alsa_option, e->options, o);
 
544
        option_free(o);
 
545
    }
 
546
 
 
547
    if (e->db_fix)
 
548
        decibel_fix_free(e->db_fix);
 
549
 
 
550
    pa_xfree(e->alsa_name);
 
551
    pa_xfree(e);
 
552
}
 
553
 
 
554
void pa_alsa_path_free(pa_alsa_path *p) {
 
555
    pa_alsa_jack *j;
 
556
    pa_alsa_element *e;
 
557
    pa_alsa_setting *s;
 
558
 
 
559
    pa_assert(p);
 
560
 
 
561
    while ((j = p->jacks)) {
 
562
        PA_LLIST_REMOVE(pa_alsa_jack, p->jacks, j);
 
563
        jack_free(j);
 
564
    }
 
565
 
 
566
    while ((e = p->elements)) {
 
567
        PA_LLIST_REMOVE(pa_alsa_element, p->elements, e);
 
568
        element_free(e);
 
569
    }
 
570
 
 
571
    while ((s = p->settings)) {
 
572
        PA_LLIST_REMOVE(pa_alsa_setting, p->settings, s);
 
573
        setting_free(s);
 
574
    }
 
575
 
 
576
    pa_proplist_free(p->proplist);
 
577
    pa_xfree(p->name);
 
578
    pa_xfree(p->description);
 
579
    pa_xfree(p->description_key);
 
580
    pa_xfree(p);
 
581
}
 
582
 
 
583
void pa_alsa_path_set_free(pa_alsa_path_set *ps) {
 
584
    pa_assert(ps);
 
585
 
 
586
    if (ps->paths)
 
587
        pa_hashmap_free(ps->paths);
 
588
 
 
589
    pa_xfree(ps);
 
590
}
 
591
 
 
592
static long to_alsa_dB(pa_volume_t v) {
 
593
    return (long) (pa_sw_volume_to_dB(v) * 100.0);
 
594
}
 
595
 
 
596
static pa_volume_t from_alsa_dB(long v) {
 
597
    return pa_sw_volume_from_dB((double) v / 100.0);
 
598
}
 
599
 
 
600
static long to_alsa_volume(pa_volume_t v, long min, long max) {
 
601
    long w;
 
602
 
 
603
    w = (long) round(((double) v * (double) (max - min)) / PA_VOLUME_NORM) + min;
 
604
    return PA_CLAMP_UNLIKELY(w, min, max);
 
605
}
 
606
 
 
607
static pa_volume_t from_alsa_volume(long v, long min, long max) {
 
608
    return (pa_volume_t) round(((double) (v - min) * PA_VOLUME_NORM) / (double) (max - min));
 
609
}
 
610
 
 
611
#define SELEM_INIT(sid, name)                           \
 
612
    do {                                                \
 
613
        snd_mixer_selem_id_alloca(&(sid));              \
 
614
        snd_mixer_selem_id_set_name((sid), (name));     \
 
615
        snd_mixer_selem_id_set_index((sid), 0);         \
 
616
    } while(false)
 
617
 
 
618
static int element_get_volume(pa_alsa_element *e, snd_mixer_t *m, const pa_channel_map *cm, pa_cvolume *v) {
 
619
    snd_mixer_selem_id_t *sid;
 
620
    snd_mixer_elem_t *me;
 
621
    snd_mixer_selem_channel_id_t c;
 
622
    pa_channel_position_mask_t mask = 0;
 
623
    unsigned k;
 
624
 
 
625
    pa_assert(m);
 
626
    pa_assert(e);
 
627
    pa_assert(cm);
 
628
    pa_assert(v);
 
629
 
 
630
    SELEM_INIT(sid, e->alsa_name);
 
631
    if (!(me = snd_mixer_find_selem(m, sid))) {
 
632
        pa_log_warn("Element %s seems to have disappeared.", e->alsa_name);
 
633
        return -1;
 
634
    }
 
635
 
 
636
    pa_cvolume_mute(v, cm->channels);
 
637
 
 
638
    /* We take the highest volume of all channels that match */
 
639
 
 
640
    for (c = 0; c <= SND_MIXER_SCHN_LAST; c++) {
 
641
        int r;
 
642
        pa_volume_t f;
 
643
 
 
644
        if (e->has_dB) {
 
645
            long value = 0;
 
646
 
 
647
            if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
 
648
                if (snd_mixer_selem_has_playback_channel(me, c)) {
 
649
                    if (e->db_fix) {
 
650
                        if ((r = snd_mixer_selem_get_playback_volume(me, c, &value)) >= 0) {
 
651
                            /* If the channel volume is outside the limits set
 
652
                             * by the dB fix, we clamp the hw volume to be
 
653
                             * within the limits. */
 
654
                            if (value < e->db_fix->min_step) {
 
655
                                value = e->db_fix->min_step;
 
656
                                snd_mixer_selem_set_playback_volume(me, c, value);
 
657
                                pa_log_debug("Playback volume for element %s channel %i was below the dB fix limit. "
 
658
                                             "Volume reset to %0.2f dB.", e->alsa_name, c,
 
659
                                             e->db_fix->db_values[value - e->db_fix->min_step] / 100.0);
 
660
                            } else if (value > e->db_fix->max_step) {
 
661
                                value = e->db_fix->max_step;
 
662
                                snd_mixer_selem_set_playback_volume(me, c, value);
 
663
                                pa_log_debug("Playback volume for element %s channel %i was over the dB fix limit. "
 
664
                                             "Volume reset to %0.2f dB.", e->alsa_name, c,
 
665
                                             e->db_fix->db_values[value - e->db_fix->min_step] / 100.0);
 
666
                            }
 
667
 
 
668
                            /* Volume step -> dB value conversion. */
 
669
                            value = e->db_fix->db_values[value - e->db_fix->min_step];
 
670
                        }
 
671
                    } else
 
672
                        r = snd_mixer_selem_get_playback_dB(me, c, &value);
 
673
                } else
 
674
                    r = -1;
 
675
            } else {
 
676
                if (snd_mixer_selem_has_capture_channel(me, c)) {
 
677
                    if (e->db_fix) {
 
678
                        if ((r = snd_mixer_selem_get_capture_volume(me, c, &value)) >= 0) {
 
679
                            /* If the channel volume is outside the limits set
 
680
                             * by the dB fix, we clamp the hw volume to be
 
681
                             * within the limits. */
 
682
                            if (value < e->db_fix->min_step) {
 
683
                                value = e->db_fix->min_step;
 
684
                                snd_mixer_selem_set_capture_volume(me, c, value);
 
685
                                pa_log_debug("Capture volume for element %s channel %i was below the dB fix limit. "
 
686
                                             "Volume reset to %0.2f dB.", e->alsa_name, c,
 
687
                                             e->db_fix->db_values[value - e->db_fix->min_step] / 100.0);
 
688
                            } else if (value > e->db_fix->max_step) {
 
689
                                value = e->db_fix->max_step;
 
690
                                snd_mixer_selem_set_capture_volume(me, c, value);
 
691
                                pa_log_debug("Capture volume for element %s channel %i was over the dB fix limit. "
 
692
                                             "Volume reset to %0.2f dB.", e->alsa_name, c,
 
693
                                             e->db_fix->db_values[value - e->db_fix->min_step] / 100.0);
 
694
                            }
 
695
 
 
696
                            /* Volume step -> dB value conversion. */
 
697
                            value = e->db_fix->db_values[value - e->db_fix->min_step];
 
698
                        }
 
699
                    } else
 
700
                        r = snd_mixer_selem_get_capture_dB(me, c, &value);
 
701
                } else
 
702
                    r = -1;
 
703
            }
 
704
 
 
705
            if (r < 0)
 
706
                continue;
 
707
 
 
708
#ifdef HAVE_VALGRIND_MEMCHECK_H
 
709
                VALGRIND_MAKE_MEM_DEFINED(&value, sizeof(value));
 
710
#endif
 
711
 
 
712
            f = from_alsa_dB(value);
 
713
 
 
714
        } else {
 
715
            long value = 0;
 
716
 
 
717
            if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
 
718
                if (snd_mixer_selem_has_playback_channel(me, c))
 
719
                    r = snd_mixer_selem_get_playback_volume(me, c, &value);
 
720
                else
 
721
                    r = -1;
 
722
            } else {
 
723
                if (snd_mixer_selem_has_capture_channel(me, c))
 
724
                    r = snd_mixer_selem_get_capture_volume(me, c, &value);
 
725
                else
 
726
                    r = -1;
 
727
            }
 
728
 
 
729
            if (r < 0)
 
730
                continue;
 
731
 
 
732
            f = from_alsa_volume(value, e->min_volume, e->max_volume);
 
733
        }
 
734
 
 
735
        for (k = 0; k < cm->channels; k++)
 
736
            if (e->masks[c][e->n_channels-1] & PA_CHANNEL_POSITION_MASK(cm->map[k]))
 
737
                if (v->values[k] < f)
 
738
                    v->values[k] = f;
 
739
 
 
740
        mask |= e->masks[c][e->n_channels-1];
 
741
    }
 
742
 
 
743
    for (k = 0; k < cm->channels; k++)
 
744
        if (!(mask & PA_CHANNEL_POSITION_MASK(cm->map[k])))
 
745
            v->values[k] = PA_VOLUME_NORM;
 
746
 
 
747
    return 0;
 
748
}
 
749
 
 
750
int pa_alsa_path_get_volume(pa_alsa_path *p, snd_mixer_t *m, const pa_channel_map *cm, pa_cvolume *v) {
 
751
    pa_alsa_element *e;
 
752
 
 
753
    pa_assert(m);
 
754
    pa_assert(p);
 
755
    pa_assert(cm);
 
756
    pa_assert(v);
 
757
 
 
758
    if (!p->has_volume)
 
759
        return -1;
 
760
 
 
761
    pa_cvolume_reset(v, cm->channels);
 
762
 
 
763
    PA_LLIST_FOREACH(e, p->elements) {
 
764
        pa_cvolume ev;
 
765
 
 
766
        if (e->volume_use != PA_ALSA_VOLUME_MERGE)
 
767
            continue;
 
768
 
 
769
        pa_assert(!p->has_dB || e->has_dB);
 
770
 
 
771
        if (element_get_volume(e, m, cm, &ev) < 0)
 
772
            return -1;
 
773
 
 
774
        /* If we have no dB information all we can do is take the first element and leave */
 
775
        if (!p->has_dB) {
 
776
            *v = ev;
 
777
            return 0;
 
778
        }
 
779
 
 
780
        pa_sw_cvolume_multiply(v, v, &ev);
 
781
    }
 
782
 
 
783
    return 0;
 
784
}
 
785
 
 
786
static int element_get_switch(pa_alsa_element *e, snd_mixer_t *m, bool *b) {
 
787
    snd_mixer_selem_id_t *sid;
 
788
    snd_mixer_elem_t *me;
 
789
    snd_mixer_selem_channel_id_t c;
 
790
 
 
791
    pa_assert(m);
 
792
    pa_assert(e);
 
793
    pa_assert(b);
 
794
 
 
795
    SELEM_INIT(sid, e->alsa_name);
 
796
    if (!(me = snd_mixer_find_selem(m, sid))) {
 
797
        pa_log_warn("Element %s seems to have disappeared.", e->alsa_name);
 
798
        return -1;
 
799
    }
 
800
 
 
801
    /* We return muted if at least one channel is muted */
 
802
 
 
803
    for (c = 0; c <= SND_MIXER_SCHN_LAST; c++) {
 
804
        int r;
 
805
        int value = 0;
 
806
 
 
807
        if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
 
808
            if (snd_mixer_selem_has_playback_channel(me, c))
 
809
                r = snd_mixer_selem_get_playback_switch(me, c, &value);
 
810
            else
 
811
                r = -1;
 
812
        } else {
 
813
            if (snd_mixer_selem_has_capture_channel(me, c))
 
814
                r = snd_mixer_selem_get_capture_switch(me, c, &value);
 
815
            else
 
816
                r = -1;
 
817
        }
 
818
 
 
819
        if (r < 0)
 
820
            continue;
 
821
 
 
822
        if (!value) {
 
823
            *b = false;
 
824
            return 0;
 
825
        }
 
826
    }
 
827
 
 
828
    *b = true;
 
829
    return 0;
 
830
}
 
831
 
 
832
int pa_alsa_path_get_mute(pa_alsa_path *p, snd_mixer_t *m, bool *muted) {
 
833
    pa_alsa_element *e;
 
834
 
 
835
    pa_assert(m);
 
836
    pa_assert(p);
 
837
    pa_assert(muted);
 
838
 
 
839
    if (!p->has_mute)
 
840
        return -1;
 
841
 
 
842
    PA_LLIST_FOREACH(e, p->elements) {
 
843
        bool b;
 
844
 
 
845
        if (e->switch_use != PA_ALSA_SWITCH_MUTE)
 
846
            continue;
 
847
 
 
848
        if (element_get_switch(e, m, &b) < 0)
 
849
            return -1;
 
850
 
 
851
        if (!b) {
 
852
            *muted = true;
 
853
            return 0;
 
854
        }
 
855
    }
 
856
 
 
857
    *muted = false;
 
858
    return 0;
 
859
}
 
860
 
 
861
/* Finds the closest item in db_fix->db_values and returns the corresponding
 
862
 * step. *db_value is replaced with the value from the db_values table.
 
863
 * Rounding is done based on the rounding parameter: -1 means rounding down and
 
864
 * +1 means rounding up. */
 
865
static long decibel_fix_get_step(pa_alsa_decibel_fix *db_fix, long *db_value, int rounding) {
 
866
    unsigned i = 0;
 
867
    unsigned max_i = 0;
 
868
 
 
869
    pa_assert(db_fix);
 
870
    pa_assert(db_value);
 
871
    pa_assert(rounding != 0);
 
872
 
 
873
    max_i = db_fix->max_step - db_fix->min_step;
 
874
 
 
875
    if (rounding > 0) {
 
876
        for (i = 0; i < max_i; i++) {
 
877
            if (db_fix->db_values[i] >= *db_value)
 
878
                break;
 
879
        }
 
880
    } else {
 
881
        for (i = 0; i < max_i; i++) {
 
882
            if (db_fix->db_values[i + 1] > *db_value)
 
883
                break;
 
884
        }
 
885
    }
 
886
 
 
887
    *db_value = db_fix->db_values[i];
 
888
 
 
889
    return i + db_fix->min_step;
 
890
}
 
891
 
 
892
/* Alsa lib documentation says for snd_mixer_selem_set_playback_dB() direction argument,
 
893
 * that "-1 = accurate or first below, 0 = accurate, 1 = accurate or first above".
 
894
 * But even with accurate nearest dB volume step is not selected, so that is why we need
 
895
 * this function. Returns 0 and nearest selectable volume in *value_dB on success or
 
896
 * negative error code if fails. */
 
897
static int element_get_nearest_alsa_dB(snd_mixer_elem_t *me, snd_mixer_selem_channel_id_t c, pa_alsa_direction_t d, long *value_dB) {
 
898
 
 
899
    long alsa_val;
 
900
    long value_high;
 
901
    long value_low;
 
902
    int r = -1;
 
903
 
 
904
    pa_assert(me);
 
905
    pa_assert(value_dB);
 
906
 
 
907
    if (d == PA_ALSA_DIRECTION_OUTPUT) {
 
908
        if ((r = snd_mixer_selem_ask_playback_dB_vol(me, *value_dB, +1, &alsa_val)) >= 0)
 
909
            r = snd_mixer_selem_ask_playback_vol_dB(me, alsa_val, &value_high);
 
910
 
 
911
        if (r < 0)
 
912
            return r;
 
913
 
 
914
        if (value_high == *value_dB)
 
915
            return r;
 
916
 
 
917
        if ((r = snd_mixer_selem_ask_playback_dB_vol(me, *value_dB, -1, &alsa_val)) >= 0)
 
918
            r = snd_mixer_selem_ask_playback_vol_dB(me, alsa_val, &value_low);
 
919
    } else {
 
920
        if ((r = snd_mixer_selem_ask_capture_dB_vol(me, *value_dB, +1, &alsa_val)) >= 0)
 
921
            r = snd_mixer_selem_ask_capture_vol_dB(me, alsa_val, &value_high);
 
922
 
 
923
        if (r < 0)
 
924
            return r;
 
925
 
 
926
        if (value_high == *value_dB)
 
927
            return r;
 
928
 
 
929
        if ((r = snd_mixer_selem_ask_capture_dB_vol(me, *value_dB, -1, &alsa_val)) >= 0)
 
930
            r = snd_mixer_selem_ask_capture_vol_dB(me, alsa_val, &value_low);
 
931
    }
 
932
 
 
933
    if (r < 0)
 
934
        return r;
 
935
 
 
936
    if (labs(value_high - *value_dB) < labs(value_low - *value_dB))
 
937
        *value_dB = value_high;
 
938
    else
 
939
        *value_dB = value_low;
 
940
 
 
941
    return r;
 
942
}
 
943
 
 
944
static int element_set_volume(pa_alsa_element *e, snd_mixer_t *m, const pa_channel_map *cm, pa_cvolume *v, bool deferred_volume, bool write_to_hw) {
 
945
 
 
946
    snd_mixer_selem_id_t *sid;
 
947
    pa_cvolume rv;
 
948
    snd_mixer_elem_t *me;
 
949
    snd_mixer_selem_channel_id_t c;
 
950
    pa_channel_position_mask_t mask = 0;
 
951
    unsigned k;
 
952
 
 
953
    pa_assert(m);
 
954
    pa_assert(e);
 
955
    pa_assert(cm);
 
956
    pa_assert(v);
 
957
    pa_assert(pa_cvolume_compatible_with_channel_map(v, cm));
 
958
 
 
959
    SELEM_INIT(sid, e->alsa_name);
 
960
    if (!(me = snd_mixer_find_selem(m, sid))) {
 
961
        pa_log_warn("Element %s seems to have disappeared.", e->alsa_name);
 
962
        return -1;
 
963
    }
 
964
 
 
965
    pa_cvolume_mute(&rv, cm->channels);
 
966
 
 
967
    for (c = 0; c <= SND_MIXER_SCHN_LAST; c++) {
 
968
        int r;
 
969
        pa_volume_t f = PA_VOLUME_MUTED;
 
970
        bool found = false;
 
971
 
 
972
        for (k = 0; k < cm->channels; k++)
 
973
            if (e->masks[c][e->n_channels-1] & PA_CHANNEL_POSITION_MASK(cm->map[k])) {
 
974
                found = true;
 
975
                if (v->values[k] > f)
 
976
                    f = v->values[k];
 
977
            }
 
978
 
 
979
        if (!found) {
 
980
            /* Hmm, so this channel does not exist in the volume
 
981
             * struct, so let's bind it to the overall max of the
 
982
             * volume. */
 
983
            f = pa_cvolume_max(v);
 
984
        }
 
985
 
 
986
        if (e->has_dB) {
 
987
            long value = to_alsa_dB(f);
 
988
            int rounding;
 
989
 
 
990
            if (e->volume_limit >= 0 && value > (e->max_dB * 100))
 
991
                value = e->max_dB * 100;
 
992
 
 
993
            if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
 
994
                /* If we call set_playback_volume() without checking first
 
995
                 * if the channel is available, ALSA behaves very
 
996
                 * strangely and doesn't fail the call */
 
997
                if (snd_mixer_selem_has_playback_channel(me, c)) {
 
998
                    rounding = +1;
 
999
                    if (e->db_fix) {
 
1000
                        if (write_to_hw)
 
1001
                            r = snd_mixer_selem_set_playback_volume(me, c, decibel_fix_get_step(e->db_fix, &value, rounding));
 
1002
                        else {
 
1003
                            decibel_fix_get_step(e->db_fix, &value, rounding);
 
1004
                            r = 0;
 
1005
                        }
 
1006
 
 
1007
                    } else {
 
1008
                        if (write_to_hw) {
 
1009
                            if (deferred_volume) {
 
1010
                                if ((r = element_get_nearest_alsa_dB(me, c, PA_ALSA_DIRECTION_OUTPUT, &value)) >= 0)
 
1011
                                    r = snd_mixer_selem_set_playback_dB(me, c, value, 0);
 
1012
                            } else {
 
1013
                                if ((r = snd_mixer_selem_set_playback_dB(me, c, value, rounding)) >= 0)
 
1014
                                    r = snd_mixer_selem_get_playback_dB(me, c, &value);
 
1015
                           }
 
1016
                        } else {
 
1017
                            long alsa_val;
 
1018
                            if ((r = snd_mixer_selem_ask_playback_dB_vol(me, value, rounding, &alsa_val)) >= 0)
 
1019
                                r = snd_mixer_selem_ask_playback_vol_dB(me, alsa_val, &value);
 
1020
                        }
 
1021
                    }
 
1022
                } else
 
1023
                    r = -1;
 
1024
            } else {
 
1025
                if (snd_mixer_selem_has_capture_channel(me, c)) {
 
1026
                    rounding = -1;
 
1027
                    if (e->db_fix) {
 
1028
                        if (write_to_hw)
 
1029
                            r = snd_mixer_selem_set_capture_volume(me, c, decibel_fix_get_step(e->db_fix, &value, rounding));
 
1030
                        else {
 
1031
                            decibel_fix_get_step(e->db_fix, &value, rounding);
 
1032
                            r = 0;
 
1033
                        }
 
1034
 
 
1035
                    } else {
 
1036
                        if (write_to_hw) {
 
1037
                            if (deferred_volume) {
 
1038
                                if ((r = element_get_nearest_alsa_dB(me, c, PA_ALSA_DIRECTION_INPUT, &value)) >= 0)
 
1039
                                    r = snd_mixer_selem_set_capture_dB(me, c, value, 0);
 
1040
                            } else {
 
1041
                                if ((r = snd_mixer_selem_set_capture_dB(me, c, value, rounding)) >= 0)
 
1042
                                    r = snd_mixer_selem_get_capture_dB(me, c, &value);
 
1043
                            }
 
1044
                        } else {
 
1045
                            long alsa_val;
 
1046
                            if ((r = snd_mixer_selem_ask_capture_dB_vol(me, value, rounding, &alsa_val)) >= 0)
 
1047
                                r = snd_mixer_selem_ask_capture_vol_dB(me, alsa_val, &value);
 
1048
                        }
 
1049
                    }
 
1050
                } else
 
1051
                    r = -1;
 
1052
            }
 
1053
 
 
1054
            if (r < 0)
 
1055
                continue;
 
1056
 
 
1057
            f = from_alsa_dB(value);
 
1058
 
 
1059
        } else {
 
1060
            long value;
 
1061
 
 
1062
            value = to_alsa_volume(f, e->min_volume, e->max_volume);
 
1063
 
 
1064
            if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
 
1065
                if (snd_mixer_selem_has_playback_channel(me, c)) {
 
1066
                    if ((r = snd_mixer_selem_set_playback_volume(me, c, value)) >= 0)
 
1067
                        r = snd_mixer_selem_get_playback_volume(me, c, &value);
 
1068
                } else
 
1069
                    r = -1;
 
1070
            } else {
 
1071
                if (snd_mixer_selem_has_capture_channel(me, c)) {
 
1072
                    if ((r = snd_mixer_selem_set_capture_volume(me, c, value)) >= 0)
 
1073
                        r = snd_mixer_selem_get_capture_volume(me, c, &value);
 
1074
                } else
 
1075
                    r = -1;
 
1076
            }
 
1077
 
 
1078
            if (r < 0)
 
1079
                continue;
 
1080
 
 
1081
            f = from_alsa_volume(value, e->min_volume, e->max_volume);
 
1082
        }
 
1083
 
 
1084
        for (k = 0; k < cm->channels; k++)
 
1085
            if (e->masks[c][e->n_channels-1] & PA_CHANNEL_POSITION_MASK(cm->map[k]))
 
1086
                if (rv.values[k] < f)
 
1087
                    rv.values[k] = f;
 
1088
 
 
1089
        mask |= e->masks[c][e->n_channels-1];
 
1090
    }
 
1091
 
 
1092
    for (k = 0; k < cm->channels; k++)
 
1093
        if (!(mask & PA_CHANNEL_POSITION_MASK(cm->map[k])))
 
1094
            rv.values[k] = PA_VOLUME_NORM;
 
1095
 
 
1096
    *v = rv;
 
1097
    return 0;
 
1098
}
 
1099
 
 
1100
int pa_alsa_path_set_volume(pa_alsa_path *p, snd_mixer_t *m, const pa_channel_map *cm, pa_cvolume *v, bool deferred_volume, bool write_to_hw) {
 
1101
 
 
1102
    pa_alsa_element *e;
 
1103
    pa_cvolume rv;
 
1104
 
 
1105
    pa_assert(m);
 
1106
    pa_assert(p);
 
1107
    pa_assert(cm);
 
1108
    pa_assert(v);
 
1109
    pa_assert(pa_cvolume_compatible_with_channel_map(v, cm));
 
1110
 
 
1111
    if (!p->has_volume)
 
1112
        return -1;
 
1113
 
 
1114
    rv = *v; /* Remaining adjustment */
 
1115
    pa_cvolume_reset(v, cm->channels); /* Adjustment done */
 
1116
 
 
1117
    PA_LLIST_FOREACH(e, p->elements) {
 
1118
        pa_cvolume ev;
 
1119
 
 
1120
        if (e->volume_use != PA_ALSA_VOLUME_MERGE)
 
1121
            continue;
 
1122
 
 
1123
        pa_assert(!p->has_dB || e->has_dB);
 
1124
 
 
1125
        ev = rv;
 
1126
        if (element_set_volume(e, m, cm, &ev, deferred_volume, write_to_hw) < 0)
 
1127
            return -1;
 
1128
 
 
1129
        if (!p->has_dB) {
 
1130
            *v = ev;
 
1131
            return 0;
 
1132
        }
 
1133
 
 
1134
        pa_sw_cvolume_multiply(v, v, &ev);
 
1135
        pa_sw_cvolume_divide(&rv, &rv, &ev);
 
1136
    }
 
1137
 
 
1138
    return 0;
 
1139
}
 
1140
 
 
1141
static int element_set_switch(pa_alsa_element *e, snd_mixer_t *m, bool b) {
 
1142
    snd_mixer_elem_t *me;
 
1143
    snd_mixer_selem_id_t *sid;
 
1144
    int r;
 
1145
 
 
1146
    pa_assert(m);
 
1147
    pa_assert(e);
 
1148
 
 
1149
    SELEM_INIT(sid, e->alsa_name);
 
1150
    if (!(me = snd_mixer_find_selem(m, sid))) {
 
1151
        pa_log_warn("Element %s seems to have disappeared.", e->alsa_name);
 
1152
        return -1;
 
1153
    }
 
1154
 
 
1155
    if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
 
1156
        r = snd_mixer_selem_set_playback_switch_all(me, b);
 
1157
    else
 
1158
        r = snd_mixer_selem_set_capture_switch_all(me, b);
 
1159
 
 
1160
    if (r < 0)
 
1161
        pa_log_warn("Failed to set switch of %s: %s", e->alsa_name, pa_alsa_strerror(errno));
 
1162
 
 
1163
    return r;
 
1164
}
 
1165
 
 
1166
int pa_alsa_path_set_mute(pa_alsa_path *p, snd_mixer_t *m, bool muted) {
 
1167
    pa_alsa_element *e;
 
1168
 
 
1169
    pa_assert(m);
 
1170
    pa_assert(p);
 
1171
 
 
1172
    if (!p->has_mute)
 
1173
        return -1;
 
1174
 
 
1175
    PA_LLIST_FOREACH(e, p->elements) {
 
1176
 
 
1177
        if (e->switch_use != PA_ALSA_SWITCH_MUTE)
 
1178
            continue;
 
1179
 
 
1180
        if (element_set_switch(e, m, !muted) < 0)
 
1181
            return -1;
 
1182
    }
 
1183
 
 
1184
    return 0;
 
1185
}
 
1186
 
 
1187
/* Depending on whether e->volume_use is _OFF, _ZERO or _CONSTANT, this
 
1188
 * function sets all channels of the volume element to e->min_volume, 0 dB or
 
1189
 * e->constant_volume. */
 
1190
static int element_set_constant_volume(pa_alsa_element *e, snd_mixer_t *m) {
 
1191
    snd_mixer_elem_t *me = NULL;
 
1192
    snd_mixer_selem_id_t *sid = NULL;
 
1193
    int r = 0;
 
1194
    long volume = -1;
 
1195
    bool volume_set = false;
 
1196
 
 
1197
    pa_assert(m);
 
1198
    pa_assert(e);
 
1199
 
 
1200
    SELEM_INIT(sid, e->alsa_name);
 
1201
    if (!(me = snd_mixer_find_selem(m, sid))) {
 
1202
        pa_log_warn("Element %s seems to have disappeared.", e->alsa_name);
 
1203
        return -1;
 
1204
    }
 
1205
 
 
1206
    switch (e->volume_use) {
 
1207
        case PA_ALSA_VOLUME_OFF:
 
1208
            volume = e->min_volume;
 
1209
            volume_set = true;
 
1210
            break;
 
1211
 
 
1212
        case PA_ALSA_VOLUME_ZERO:
 
1213
            if (e->db_fix) {
 
1214
                long dB = 0;
 
1215
 
 
1216
                volume = decibel_fix_get_step(e->db_fix, &dB, (e->direction == PA_ALSA_DIRECTION_OUTPUT ? +1 : -1));
 
1217
                volume_set = true;
 
1218
            }
 
1219
            break;
 
1220
 
 
1221
        case PA_ALSA_VOLUME_CONSTANT:
 
1222
            volume = e->constant_volume;
 
1223
            volume_set = true;
 
1224
            break;
 
1225
 
 
1226
        default:
 
1227
            pa_assert_not_reached();
 
1228
    }
 
1229
 
 
1230
    if (volume_set) {
 
1231
        if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
 
1232
            r = snd_mixer_selem_set_playback_volume_all(me, volume);
 
1233
        else
 
1234
            r = snd_mixer_selem_set_capture_volume_all(me, volume);
 
1235
    } else {
 
1236
        pa_assert(e->volume_use == PA_ALSA_VOLUME_ZERO);
 
1237
        pa_assert(!e->db_fix);
 
1238
 
 
1239
        if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
 
1240
            r = snd_mixer_selem_set_playback_dB_all(me, 0, +1);
 
1241
        else
 
1242
            r = snd_mixer_selem_set_capture_dB_all(me, 0, -1);
 
1243
    }
 
1244
 
 
1245
    if (r < 0)
 
1246
        pa_log_warn("Failed to set volume of %s: %s", e->alsa_name, pa_alsa_strerror(errno));
 
1247
 
 
1248
    return r;
 
1249
}
 
1250
 
 
1251
int pa_alsa_path_select(pa_alsa_path *p, pa_alsa_setting *s, snd_mixer_t *m, bool device_is_muted) {
 
1252
    pa_alsa_element *e;
 
1253
    int r = 0;
 
1254
 
 
1255
    pa_assert(m);
 
1256
    pa_assert(p);
 
1257
 
 
1258
    pa_log_debug("Activating path %s", p->name);
 
1259
    pa_alsa_path_dump(p);
 
1260
 
 
1261
    /* First turn on hw mute if available, to avoid noise
 
1262
     * when setting the mixer controls. */
 
1263
    if (p->mute_during_activation) {
 
1264
        PA_LLIST_FOREACH(e, p->elements) {
 
1265
            if (e->switch_use == PA_ALSA_SWITCH_MUTE)
 
1266
                /* If the muting fails here, that's not a critical problem for
 
1267
                 * selecting a path, so we ignore the return value.
 
1268
                 * element_set_switch() will print a warning anyway, so this
 
1269
                 * won't be a silent failure either. */
 
1270
                (void) element_set_switch(e, m, false);
 
1271
        }
 
1272
    }
 
1273
 
 
1274
    PA_LLIST_FOREACH(e, p->elements) {
 
1275
 
 
1276
        switch (e->switch_use) {
 
1277
            case PA_ALSA_SWITCH_OFF:
 
1278
                r = element_set_switch(e, m, false);
 
1279
                break;
 
1280
 
 
1281
            case PA_ALSA_SWITCH_ON:
 
1282
                r = element_set_switch(e, m, true);
 
1283
                break;
 
1284
 
 
1285
            case PA_ALSA_SWITCH_MUTE:
 
1286
            case PA_ALSA_SWITCH_IGNORE:
 
1287
            case PA_ALSA_SWITCH_SELECT:
 
1288
                r = 0;
 
1289
                break;
 
1290
        }
 
1291
 
 
1292
        if (r < 0)
 
1293
            return -1;
 
1294
 
 
1295
        switch (e->volume_use) {
 
1296
            case PA_ALSA_VOLUME_OFF:
 
1297
            case PA_ALSA_VOLUME_ZERO:
 
1298
            case PA_ALSA_VOLUME_CONSTANT:
 
1299
                r = element_set_constant_volume(e, m);
 
1300
                break;
 
1301
 
 
1302
            case PA_ALSA_VOLUME_MERGE:
 
1303
            case PA_ALSA_VOLUME_IGNORE:
 
1304
                r = 0;
 
1305
                break;
 
1306
        }
 
1307
 
 
1308
        if (r < 0)
 
1309
            return -1;
 
1310
    }
 
1311
 
 
1312
    if (s)
 
1313
        setting_select(s, m);
 
1314
 
 
1315
    /* Finally restore hw mute to the device mute status. */
 
1316
    if (p->mute_during_activation) {
 
1317
        PA_LLIST_FOREACH(e, p->elements) {
 
1318
            if (e->switch_use == PA_ALSA_SWITCH_MUTE) {
 
1319
                if (element_set_switch(e, m, !device_is_muted) < 0)
 
1320
                    return -1;
 
1321
            }
 
1322
        }
 
1323
    }
 
1324
 
 
1325
    return 0;
 
1326
}
 
1327
 
 
1328
static int check_required(pa_alsa_element *e, snd_mixer_elem_t *me) {
 
1329
    bool has_switch;
 
1330
    bool has_enumeration;
 
1331
    bool has_volume;
 
1332
 
 
1333
    pa_assert(e);
 
1334
    pa_assert(me);
 
1335
 
 
1336
    if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
 
1337
        has_switch =
 
1338
            snd_mixer_selem_has_playback_switch(me) ||
 
1339
            (e->direction_try_other && snd_mixer_selem_has_capture_switch(me));
 
1340
    } else {
 
1341
        has_switch =
 
1342
            snd_mixer_selem_has_capture_switch(me) ||
 
1343
            (e->direction_try_other && snd_mixer_selem_has_playback_switch(me));
 
1344
    }
 
1345
 
 
1346
    if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
 
1347
        has_volume =
 
1348
            snd_mixer_selem_has_playback_volume(me) ||
 
1349
            (e->direction_try_other && snd_mixer_selem_has_capture_volume(me));
 
1350
    } else {
 
1351
        has_volume =
 
1352
            snd_mixer_selem_has_capture_volume(me) ||
 
1353
            (e->direction_try_other && snd_mixer_selem_has_playback_volume(me));
 
1354
    }
 
1355
 
 
1356
    has_enumeration = snd_mixer_selem_is_enumerated(me);
 
1357
 
 
1358
    if ((e->required == PA_ALSA_REQUIRED_SWITCH && !has_switch) ||
 
1359
        (e->required == PA_ALSA_REQUIRED_VOLUME && !has_volume) ||
 
1360
        (e->required == PA_ALSA_REQUIRED_ENUMERATION && !has_enumeration))
 
1361
        return -1;
 
1362
 
 
1363
    if (e->required == PA_ALSA_REQUIRED_ANY && !(has_switch || has_volume || has_enumeration))
 
1364
        return -1;
 
1365
 
 
1366
    if ((e->required_absent == PA_ALSA_REQUIRED_SWITCH && has_switch) ||
 
1367
        (e->required_absent == PA_ALSA_REQUIRED_VOLUME && has_volume) ||
 
1368
        (e->required_absent == PA_ALSA_REQUIRED_ENUMERATION && has_enumeration))
 
1369
        return -1;
 
1370
 
 
1371
    if (e->required_absent == PA_ALSA_REQUIRED_ANY && (has_switch || has_volume || has_enumeration))
 
1372
        return -1;
 
1373
 
 
1374
    if (e->required_any != PA_ALSA_REQUIRED_IGNORE) {
 
1375
        switch (e->required_any) {
 
1376
            case PA_ALSA_REQUIRED_VOLUME:
 
1377
                e->path->req_any_present |= (e->volume_use != PA_ALSA_VOLUME_IGNORE);
 
1378
                break;
 
1379
            case PA_ALSA_REQUIRED_SWITCH:
 
1380
                e->path->req_any_present |= (e->switch_use != PA_ALSA_SWITCH_IGNORE);
 
1381
                break;
 
1382
            case PA_ALSA_REQUIRED_ENUMERATION:
 
1383
                e->path->req_any_present |= (e->enumeration_use != PA_ALSA_ENUMERATION_IGNORE);
 
1384
                break;
 
1385
            case PA_ALSA_REQUIRED_ANY:
 
1386
                e->path->req_any_present |=
 
1387
                    (e->volume_use != PA_ALSA_VOLUME_IGNORE) ||
 
1388
                    (e->switch_use != PA_ALSA_SWITCH_IGNORE) ||
 
1389
                    (e->enumeration_use != PA_ALSA_ENUMERATION_IGNORE);
 
1390
                break;
 
1391
            default:
 
1392
                pa_assert_not_reached();
 
1393
        }
 
1394
    }
 
1395
 
 
1396
    if (e->enumeration_use == PA_ALSA_ENUMERATION_SELECT) {
 
1397
        pa_alsa_option *o;
 
1398
        PA_LLIST_FOREACH(o, e->options) {
 
1399
            e->path->req_any_present |= (o->required_any != PA_ALSA_REQUIRED_IGNORE) &&
 
1400
                (o->alsa_idx >= 0);
 
1401
            if (o->required != PA_ALSA_REQUIRED_IGNORE && o->alsa_idx < 0)
 
1402
                return -1;
 
1403
            if (o->required_absent != PA_ALSA_REQUIRED_IGNORE && o->alsa_idx >= 0)
 
1404
                return -1;
 
1405
        }
 
1406
    }
 
1407
 
 
1408
    return 0;
 
1409
}
 
1410
 
 
1411
static int element_probe(pa_alsa_element *e, snd_mixer_t *m) {
 
1412
    snd_mixer_selem_id_t *sid;
 
1413
    snd_mixer_elem_t *me;
 
1414
 
 
1415
    pa_assert(m);
 
1416
    pa_assert(e);
 
1417
    pa_assert(e->path);
 
1418
 
 
1419
    SELEM_INIT(sid, e->alsa_name);
 
1420
 
 
1421
    if (!(me = snd_mixer_find_selem(m, sid))) {
 
1422
 
 
1423
        if (e->required != PA_ALSA_REQUIRED_IGNORE)
 
1424
            return -1;
 
1425
 
 
1426
        e->switch_use = PA_ALSA_SWITCH_IGNORE;
 
1427
        e->volume_use = PA_ALSA_VOLUME_IGNORE;
 
1428
        e->enumeration_use = PA_ALSA_ENUMERATION_IGNORE;
 
1429
 
 
1430
        return 0;
 
1431
    }
 
1432
 
 
1433
    if (e->switch_use != PA_ALSA_SWITCH_IGNORE) {
 
1434
        if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
 
1435
 
 
1436
            if (!snd_mixer_selem_has_playback_switch(me)) {
 
1437
                if (e->direction_try_other && snd_mixer_selem_has_capture_switch(me))
 
1438
                    e->direction = PA_ALSA_DIRECTION_INPUT;
 
1439
                else
 
1440
                    e->switch_use = PA_ALSA_SWITCH_IGNORE;
 
1441
            }
 
1442
 
 
1443
        } else {
 
1444
 
 
1445
            if (!snd_mixer_selem_has_capture_switch(me)) {
 
1446
                if (e->direction_try_other && snd_mixer_selem_has_playback_switch(me))
 
1447
                    e->direction = PA_ALSA_DIRECTION_OUTPUT;
 
1448
                else
 
1449
                    e->switch_use = PA_ALSA_SWITCH_IGNORE;
 
1450
            }
 
1451
        }
 
1452
 
 
1453
        if (e->switch_use != PA_ALSA_SWITCH_IGNORE)
 
1454
            e->direction_try_other = false;
 
1455
    }
 
1456
 
 
1457
    if (e->volume_use != PA_ALSA_VOLUME_IGNORE) {
 
1458
 
 
1459
        if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
 
1460
 
 
1461
            if (!snd_mixer_selem_has_playback_volume(me)) {
 
1462
                if (e->direction_try_other && snd_mixer_selem_has_capture_volume(me))
 
1463
                    e->direction = PA_ALSA_DIRECTION_INPUT;
 
1464
                else
 
1465
                    e->volume_use = PA_ALSA_VOLUME_IGNORE;
 
1466
            }
 
1467
 
 
1468
        } else {
 
1469
 
 
1470
            if (!snd_mixer_selem_has_capture_volume(me)) {
 
1471
                if (e->direction_try_other && snd_mixer_selem_has_playback_volume(me))
 
1472
                    e->direction = PA_ALSA_DIRECTION_OUTPUT;
 
1473
                else
 
1474
                    e->volume_use = PA_ALSA_VOLUME_IGNORE;
 
1475
            }
 
1476
        }
 
1477
 
 
1478
        if (e->volume_use != PA_ALSA_VOLUME_IGNORE) {
 
1479
            long min_dB = 0, max_dB = 0;
 
1480
            int r;
 
1481
 
 
1482
            e->direction_try_other = false;
 
1483
 
 
1484
            if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
 
1485
                r = snd_mixer_selem_get_playback_volume_range(me, &e->min_volume, &e->max_volume);
 
1486
            else
 
1487
                r = snd_mixer_selem_get_capture_volume_range(me, &e->min_volume, &e->max_volume);
 
1488
 
 
1489
            if (r < 0) {
 
1490
                pa_log_warn("Failed to get volume range of %s: %s", e->alsa_name, pa_alsa_strerror(r));
 
1491
                return -1;
 
1492
            }
 
1493
 
 
1494
            if (e->min_volume >= e->max_volume) {
 
1495
                pa_log_warn("Your kernel driver is broken: it reports a volume range from %li to %li which makes no sense.", e->min_volume, e->max_volume);
 
1496
                e->volume_use = PA_ALSA_VOLUME_IGNORE;
 
1497
 
 
1498
            } else if (e->volume_use == PA_ALSA_VOLUME_CONSTANT &&
 
1499
                       (e->min_volume > e->constant_volume || e->max_volume < e->constant_volume)) {
 
1500
                pa_log_warn("Constant volume %li configured for element %s, but the available range is from %li to %li.",
 
1501
                            e->constant_volume, e->alsa_name, e->min_volume, e->max_volume);
 
1502
                e->volume_use = PA_ALSA_VOLUME_IGNORE;
 
1503
 
 
1504
            } else {
 
1505
                bool is_mono;
 
1506
                pa_channel_position_t p;
 
1507
 
 
1508
                if (e->db_fix &&
 
1509
                        ((e->min_volume > e->db_fix->min_step) ||
 
1510
                         (e->max_volume < e->db_fix->max_step))) {
 
1511
                    pa_log_warn("The step range of the decibel fix for element %s (%li-%li) doesn't fit to the "
 
1512
                                "real hardware range (%li-%li). Disabling the decibel fix.", e->alsa_name,
 
1513
                                e->db_fix->min_step, e->db_fix->max_step,
 
1514
                                e->min_volume, e->max_volume);
 
1515
 
 
1516
                    decibel_fix_free(e->db_fix);
 
1517
                    e->db_fix = NULL;
 
1518
                }
 
1519
 
 
1520
                if (e->db_fix) {
 
1521
                    e->has_dB = true;
 
1522
                    e->min_volume = e->db_fix->min_step;
 
1523
                    e->max_volume = e->db_fix->max_step;
 
1524
                    min_dB = e->db_fix->db_values[0];
 
1525
                    max_dB = e->db_fix->db_values[e->db_fix->max_step - e->db_fix->min_step];
 
1526
                } else if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
 
1527
                    e->has_dB = snd_mixer_selem_get_playback_dB_range(me, &min_dB, &max_dB) >= 0;
 
1528
                else
 
1529
                    e->has_dB = snd_mixer_selem_get_capture_dB_range(me, &min_dB, &max_dB) >= 0;
 
1530
 
 
1531
                /* Check that the kernel driver returns consistent limits with
 
1532
                 * both _get_*_dB_range() and _ask_*_vol_dB(). */
 
1533
                if (e->has_dB && !e->db_fix) {
 
1534
                    long min_dB_checked = 0;
 
1535
                    long max_dB_checked = 0;
 
1536
 
 
1537
                    if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
 
1538
                        r = snd_mixer_selem_ask_playback_vol_dB(me, e->min_volume, &min_dB_checked);
 
1539
                    else
 
1540
                        r = snd_mixer_selem_ask_capture_vol_dB(me, e->min_volume, &min_dB_checked);
 
1541
 
 
1542
                    if (r < 0) {
 
1543
                        pa_log_warn("Failed to query the dB value for %s at volume level %li", e->alsa_name, e->min_volume);
 
1544
                        return -1;
 
1545
                    }
 
1546
 
 
1547
                    if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
 
1548
                        r = snd_mixer_selem_ask_playback_vol_dB(me, e->max_volume, &max_dB_checked);
 
1549
                    else
 
1550
                        r = snd_mixer_selem_ask_capture_vol_dB(me, e->max_volume, &max_dB_checked);
 
1551
 
 
1552
                    if (r < 0) {
 
1553
                        pa_log_warn("Failed to query the dB value for %s at volume level %li", e->alsa_name, e->max_volume);
 
1554
                        return -1;
 
1555
                    }
 
1556
 
 
1557
                    if (min_dB != min_dB_checked || max_dB != max_dB_checked) {
 
1558
                        pa_log_warn("Your kernel driver is broken: the reported dB range for %s (from %0.2f dB to %0.2f dB) "
 
1559
                                    "doesn't match the dB values at minimum and maximum volume levels: %0.2f dB at level %li, "
 
1560
                                    "%0.2f dB at level %li.",
 
1561
                                    e->alsa_name,
 
1562
                                    min_dB / 100.0, max_dB / 100.0,
 
1563
                                    min_dB_checked / 100.0, e->min_volume, max_dB_checked / 100.0, e->max_volume);
 
1564
                        return -1;
 
1565
                    }
 
1566
                }
 
1567
 
 
1568
                if (e->has_dB) {
 
1569
                    e->min_dB = ((double) min_dB) / 100.0;
 
1570
                    e->max_dB = ((double) max_dB) / 100.0;
 
1571
 
 
1572
                    if (min_dB >= max_dB) {
 
1573
                        pa_assert(!e->db_fix);
 
1574
                        pa_log_warn("Your kernel driver is broken: it reports a volume range from %0.2f dB to %0.2f dB which makes no sense.", e->min_dB, e->max_dB);
 
1575
                        e->has_dB = false;
 
1576
                    }
 
1577
                }
 
1578
 
 
1579
                if (e->volume_limit >= 0) {
 
1580
                    if (e->volume_limit <= e->min_volume || e->volume_limit > e->max_volume)
 
1581
                        pa_log_warn("Volume limit for element %s of path %s is invalid: %li isn't within the valid range "
 
1582
                                    "%li-%li. The volume limit is ignored.",
 
1583
                                    e->alsa_name, e->path->name, e->volume_limit, e->min_volume + 1, e->max_volume);
 
1584
 
 
1585
                    else {
 
1586
                        e->max_volume = e->volume_limit;
 
1587
 
 
1588
                        if (e->has_dB) {
 
1589
                            if (e->db_fix) {
 
1590
                                e->db_fix->max_step = e->max_volume;
 
1591
                                e->max_dB = ((double) e->db_fix->db_values[e->db_fix->max_step - e->db_fix->min_step]) / 100.0;
 
1592
 
 
1593
                            } else {
 
1594
                                if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
 
1595
                                    r = snd_mixer_selem_ask_playback_vol_dB(me, e->max_volume, &max_dB);
 
1596
                                else
 
1597
                                    r = snd_mixer_selem_ask_capture_vol_dB(me, e->max_volume, &max_dB);
 
1598
 
 
1599
                                if (r < 0) {
 
1600
                                    pa_log_warn("Failed to get dB value of %s: %s", e->alsa_name, pa_alsa_strerror(r));
 
1601
                                    e->has_dB = false;
 
1602
                                } else
 
1603
                                    e->max_dB = ((double) max_dB) / 100.0;
 
1604
                            }
 
1605
                        }
 
1606
                    }
 
1607
                }
 
1608
 
 
1609
                if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
 
1610
                    is_mono = snd_mixer_selem_is_playback_mono(me) > 0;
 
1611
                else
 
1612
                    is_mono = snd_mixer_selem_is_capture_mono(me) > 0;
 
1613
 
 
1614
                if (is_mono) {
 
1615
                    e->n_channels = 1;
 
1616
 
 
1617
                    if (!e->override_map) {
 
1618
                        for (p = PA_CHANNEL_POSITION_FRONT_LEFT; p < PA_CHANNEL_POSITION_MAX; p++) {
 
1619
                            if (alsa_channel_ids[p] == SND_MIXER_SCHN_UNKNOWN)
 
1620
                                continue;
 
1621
 
 
1622
                            e->masks[alsa_channel_ids[p]][e->n_channels-1] = 0;
 
1623
                        }
 
1624
 
 
1625
                        e->masks[SND_MIXER_SCHN_MONO][e->n_channels-1] = PA_CHANNEL_POSITION_MASK_ALL;
 
1626
                    }
 
1627
 
 
1628
                    e->merged_mask = e->masks[SND_MIXER_SCHN_MONO][e->n_channels-1];
 
1629
                } else {
 
1630
                    e->n_channels = 0;
 
1631
                    for (p = PA_CHANNEL_POSITION_FRONT_LEFT; p < PA_CHANNEL_POSITION_MAX; p++) {
 
1632
 
 
1633
                        if (alsa_channel_ids[p] == SND_MIXER_SCHN_UNKNOWN)
 
1634
                            continue;
 
1635
 
 
1636
                        if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
 
1637
                            e->n_channels += snd_mixer_selem_has_playback_channel(me, alsa_channel_ids[p]) > 0;
 
1638
                        else
 
1639
                            e->n_channels += snd_mixer_selem_has_capture_channel(me, alsa_channel_ids[p]) > 0;
 
1640
                    }
 
1641
 
 
1642
                    if (e->n_channels <= 0) {
 
1643
                        pa_log_warn("Volume element %s with no channels?", e->alsa_name);
 
1644
                        return -1;
 
1645
                    }
 
1646
 
 
1647
                    if (e->n_channels > 2) {
 
1648
                        /* FIXME: In some places code like this is used:
 
1649
                         *
 
1650
                         *     e->masks[alsa_channel_ids[p]][e->n_channels-1]
 
1651
                         *
 
1652
                         * The definition of e->masks is
 
1653
                         *
 
1654
                         *     pa_channel_position_mask_t masks[SND_MIXER_SCHN_LAST + 1][2];
 
1655
                         *
 
1656
                         * Since the array size is fixed at 2, we obviously
 
1657
                         * don't support elements with more than two
 
1658
                         * channels... */
 
1659
                        pa_log_warn("Volume element %s has %u channels. That's too much! I can't handle that!", e->alsa_name, e->n_channels);
 
1660
                        return -1;
 
1661
                    }
 
1662
 
 
1663
                    if (!e->override_map) {
 
1664
                        for (p = PA_CHANNEL_POSITION_FRONT_LEFT; p < PA_CHANNEL_POSITION_MAX; p++) {
 
1665
                            bool has_channel;
 
1666
 
 
1667
                            if (alsa_channel_ids[p] == SND_MIXER_SCHN_UNKNOWN)
 
1668
                                continue;
 
1669
 
 
1670
                            if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
 
1671
                                has_channel = snd_mixer_selem_has_playback_channel(me, alsa_channel_ids[p]) > 0;
 
1672
                            else
 
1673
                                has_channel = snd_mixer_selem_has_capture_channel(me, alsa_channel_ids[p]) > 0;
 
1674
 
 
1675
                            e->masks[alsa_channel_ids[p]][e->n_channels-1] = has_channel ? PA_CHANNEL_POSITION_MASK(p) : 0;
 
1676
                        }
 
1677
                    }
 
1678
 
 
1679
                    e->merged_mask = 0;
 
1680
                    for (p = PA_CHANNEL_POSITION_FRONT_LEFT; p < PA_CHANNEL_POSITION_MAX; p++) {
 
1681
                        if (alsa_channel_ids[p] == SND_MIXER_SCHN_UNKNOWN)
 
1682
                            continue;
 
1683
 
 
1684
                        e->merged_mask |= e->masks[alsa_channel_ids[p]][e->n_channels-1];
 
1685
                    }
 
1686
                }
 
1687
            }
 
1688
        }
 
1689
 
 
1690
    }
 
1691
 
 
1692
    if (e->switch_use == PA_ALSA_SWITCH_SELECT) {
 
1693
        pa_alsa_option *o;
 
1694
 
 
1695
        PA_LLIST_FOREACH(o, e->options)
 
1696
            o->alsa_idx = pa_streq(o->alsa_name, "on") ? 1 : 0;
 
1697
    } else if (e->enumeration_use == PA_ALSA_ENUMERATION_SELECT) {
 
1698
        int n;
 
1699
        pa_alsa_option *o;
 
1700
 
 
1701
        if ((n = snd_mixer_selem_get_enum_items(me)) < 0) {
 
1702
            pa_log("snd_mixer_selem_get_enum_items() failed: %s", pa_alsa_strerror(n));
 
1703
            return -1;
 
1704
        }
 
1705
 
 
1706
        PA_LLIST_FOREACH(o, e->options) {
 
1707
            int i;
 
1708
 
 
1709
            for (i = 0; i < n; i++) {
 
1710
                char buf[128];
 
1711
 
 
1712
                if (snd_mixer_selem_get_enum_item_name(me, i, sizeof(buf), buf) < 0)
 
1713
                    continue;
 
1714
 
 
1715
                if (!pa_streq(buf, o->alsa_name))
 
1716
                    continue;
 
1717
 
 
1718
                o->alsa_idx = i;
 
1719
            }
 
1720
        }
 
1721
    }
 
1722
 
 
1723
    if (check_required(e, me) < 0)
 
1724
        return -1;
 
1725
 
 
1726
    return 0;
 
1727
}
 
1728
 
 
1729
static int jack_probe(pa_alsa_jack *j, snd_mixer_t *m) {
 
1730
    pa_assert(j);
 
1731
    pa_assert(j->path);
 
1732
 
 
1733
    j->has_control = pa_alsa_mixer_find(m, j->alsa_name, 0) != NULL;
 
1734
 
 
1735
    if (j->has_control) {
 
1736
        if (j->required_absent != PA_ALSA_REQUIRED_IGNORE)
 
1737
            return -1;
 
1738
        if (j->required_any != PA_ALSA_REQUIRED_IGNORE)
 
1739
            j->path->req_any_present = true;
 
1740
    } else {
 
1741
        if (j->required != PA_ALSA_REQUIRED_IGNORE)
 
1742
            return -1;
 
1743
    }
 
1744
 
 
1745
    return 0;
 
1746
}
 
1747
 
 
1748
static pa_alsa_element* element_get(pa_alsa_path *p, const char *section, bool prefixed) {
 
1749
    pa_alsa_element *e;
 
1750
 
 
1751
    pa_assert(p);
 
1752
    pa_assert(section);
 
1753
 
 
1754
    if (prefixed) {
 
1755
        if (!pa_startswith(section, "Element "))
 
1756
            return NULL;
 
1757
 
 
1758
        section += 8;
 
1759
    }
 
1760
 
 
1761
    /* This is not an element section, but an enum section? */
 
1762
    if (strchr(section, ':'))
 
1763
        return NULL;
 
1764
 
 
1765
    if (p->last_element && pa_streq(p->last_element->alsa_name, section))
 
1766
        return p->last_element;
 
1767
 
 
1768
    PA_LLIST_FOREACH(e, p->elements)
 
1769
        if (pa_streq(e->alsa_name, section))
 
1770
            goto finish;
 
1771
 
 
1772
    e = pa_xnew0(pa_alsa_element, 1);
 
1773
    e->path = p;
 
1774
    e->alsa_name = pa_xstrdup(section);
 
1775
    e->direction = p->direction;
 
1776
    e->volume_limit = -1;
 
1777
 
 
1778
    PA_LLIST_INSERT_AFTER(pa_alsa_element, p->elements, p->last_element, e);
 
1779
 
 
1780
finish:
 
1781
    p->last_element = e;
 
1782
    return e;
 
1783
}
 
1784
 
 
1785
static pa_alsa_jack* jack_get(pa_alsa_path *p, const char *section) {
 
1786
    pa_alsa_jack *j;
 
1787
 
 
1788
    if (!pa_startswith(section, "Jack "))
 
1789
        return NULL;
 
1790
    section += 5;
 
1791
 
 
1792
    if (p->last_jack && pa_streq(p->last_jack->name, section))
 
1793
        return p->last_jack;
 
1794
 
 
1795
    PA_LLIST_FOREACH(j, p->jacks)
 
1796
        if (pa_streq(j->name, section))
 
1797
            goto finish;
 
1798
 
 
1799
    j = pa_xnew0(pa_alsa_jack, 1);
 
1800
    j->state_unplugged = PA_AVAILABLE_NO;
 
1801
    j->state_plugged = PA_AVAILABLE_YES;
 
1802
    j->path = p;
 
1803
    j->name = pa_xstrdup(section);
 
1804
    j->alsa_name = pa_sprintf_malloc("%s Jack", section);
 
1805
    PA_LLIST_INSERT_AFTER(pa_alsa_jack, p->jacks, p->last_jack, j);
 
1806
 
 
1807
finish:
 
1808
    p->last_jack = j;
 
1809
    return j;
 
1810
}
 
1811
 
 
1812
static pa_alsa_option* option_get(pa_alsa_path *p, const char *section) {
 
1813
    char *en;
 
1814
    const char *on;
 
1815
    pa_alsa_option *o;
 
1816
    pa_alsa_element *e;
 
1817
 
 
1818
    if (!pa_startswith(section, "Option "))
 
1819
        return NULL;
 
1820
 
 
1821
    section += 7;
 
1822
 
 
1823
    /* This is not an enum section, but an element section? */
 
1824
    if (!(on = strchr(section, ':')))
 
1825
        return NULL;
 
1826
 
 
1827
    en = pa_xstrndup(section, on - section);
 
1828
    on++;
 
1829
 
 
1830
    if (p->last_option &&
 
1831
        pa_streq(p->last_option->element->alsa_name, en) &&
 
1832
        pa_streq(p->last_option->alsa_name, on)) {
 
1833
        pa_xfree(en);
 
1834
        return p->last_option;
 
1835
    }
 
1836
 
 
1837
    pa_assert_se(e = element_get(p, en, false));
 
1838
    pa_xfree(en);
 
1839
 
 
1840
    PA_LLIST_FOREACH(o, e->options)
 
1841
        if (pa_streq(o->alsa_name, on))
 
1842
            goto finish;
 
1843
 
 
1844
    o = pa_xnew0(pa_alsa_option, 1);
 
1845
    o->element = e;
 
1846
    o->alsa_name = pa_xstrdup(on);
 
1847
    o->alsa_idx = -1;
 
1848
 
 
1849
    if (p->last_option && p->last_option->element == e)
 
1850
        PA_LLIST_INSERT_AFTER(pa_alsa_option, e->options, p->last_option, o);
 
1851
    else
 
1852
        PA_LLIST_PREPEND(pa_alsa_option, e->options, o);
 
1853
 
 
1854
finish:
 
1855
    p->last_option = o;
 
1856
    return o;
 
1857
}
 
1858
 
 
1859
static int element_parse_switch(pa_config_parser_state *state) {
 
1860
    pa_alsa_path *p;
 
1861
    pa_alsa_element *e;
 
1862
 
 
1863
    pa_assert(state);
 
1864
 
 
1865
    p = state->userdata;
 
1866
 
 
1867
    if (!(e = element_get(p, state->section, true))) {
 
1868
        pa_log("[%s:%u] Switch makes no sense in '%s'", state->filename, state->lineno, state->section);
 
1869
        return -1;
 
1870
    }
 
1871
 
 
1872
    if (pa_streq(state->rvalue, "ignore"))
 
1873
        e->switch_use = PA_ALSA_SWITCH_IGNORE;
 
1874
    else if (pa_streq(state->rvalue, "mute"))
 
1875
        e->switch_use = PA_ALSA_SWITCH_MUTE;
 
1876
    else if (pa_streq(state->rvalue, "off"))
 
1877
        e->switch_use = PA_ALSA_SWITCH_OFF;
 
1878
    else if (pa_streq(state->rvalue, "on"))
 
1879
        e->switch_use = PA_ALSA_SWITCH_ON;
 
1880
    else if (pa_streq(state->rvalue, "select"))
 
1881
        e->switch_use = PA_ALSA_SWITCH_SELECT;
 
1882
    else {
 
1883
        pa_log("[%s:%u] Switch invalid of '%s'", state->filename, state->lineno, state->section);
 
1884
        return -1;
 
1885
    }
 
1886
 
 
1887
    return 0;
 
1888
}
 
1889
 
 
1890
static int element_parse_volume(pa_config_parser_state *state) {
 
1891
    pa_alsa_path *p;
 
1892
    pa_alsa_element *e;
 
1893
 
 
1894
    pa_assert(state);
 
1895
 
 
1896
    p = state->userdata;
 
1897
 
 
1898
    if (!(e = element_get(p, state->section, true))) {
 
1899
        pa_log("[%s:%u] Volume makes no sense in '%s'", state->filename, state->lineno, state->section);
 
1900
        return -1;
 
1901
    }
 
1902
 
 
1903
    if (pa_streq(state->rvalue, "ignore"))
 
1904
        e->volume_use = PA_ALSA_VOLUME_IGNORE;
 
1905
    else if (pa_streq(state->rvalue, "merge"))
 
1906
        e->volume_use = PA_ALSA_VOLUME_MERGE;
 
1907
    else if (pa_streq(state->rvalue, "off"))
 
1908
        e->volume_use = PA_ALSA_VOLUME_OFF;
 
1909
    else if (pa_streq(state->rvalue, "zero"))
 
1910
        e->volume_use = PA_ALSA_VOLUME_ZERO;
 
1911
    else {
 
1912
        uint32_t constant;
 
1913
 
 
1914
        if (pa_atou(state->rvalue, &constant) >= 0) {
 
1915
            e->volume_use = PA_ALSA_VOLUME_CONSTANT;
 
1916
            e->constant_volume = constant;
 
1917
        } else {
 
1918
            pa_log("[%s:%u] Volume invalid of '%s'", state->filename, state->lineno, state->section);
 
1919
            return -1;
 
1920
        }
 
1921
    }
 
1922
 
 
1923
    return 0;
 
1924
}
 
1925
 
 
1926
static int element_parse_enumeration(pa_config_parser_state *state) {
 
1927
    pa_alsa_path *p;
 
1928
    pa_alsa_element *e;
 
1929
 
 
1930
    pa_assert(state);
 
1931
 
 
1932
    p = state->userdata;
 
1933
 
 
1934
    if (!(e = element_get(p, state->section, true))) {
 
1935
        pa_log("[%s:%u] Enumeration makes no sense in '%s'", state->filename, state->lineno, state->section);
 
1936
        return -1;
 
1937
    }
 
1938
 
 
1939
    if (pa_streq(state->rvalue, "ignore"))
 
1940
        e->enumeration_use = PA_ALSA_ENUMERATION_IGNORE;
 
1941
    else if (pa_streq(state->rvalue, "select"))
 
1942
        e->enumeration_use = PA_ALSA_ENUMERATION_SELECT;
 
1943
    else {
 
1944
        pa_log("[%s:%u] Enumeration invalid of '%s'", state->filename, state->lineno, state->section);
 
1945
        return -1;
 
1946
    }
 
1947
 
 
1948
    return 0;
 
1949
}
 
1950
 
 
1951
static int option_parse_priority(pa_config_parser_state *state) {
 
1952
    pa_alsa_path *p;
 
1953
    pa_alsa_option *o;
 
1954
    uint32_t prio;
 
1955
 
 
1956
    pa_assert(state);
 
1957
 
 
1958
    p = state->userdata;
 
1959
 
 
1960
    if (!(o = option_get(p, state->section))) {
 
1961
        pa_log("[%s:%u] Priority makes no sense in '%s'", state->filename, state->lineno, state->section);
 
1962
        return -1;
 
1963
    }
 
1964
 
 
1965
    if (pa_atou(state->rvalue, &prio) < 0) {
 
1966
        pa_log("[%s:%u] Priority invalid of '%s'", state->filename, state->lineno, state->section);
 
1967
        return -1;
 
1968
    }
 
1969
 
 
1970
    o->priority = prio;
 
1971
    return 0;
 
1972
}
 
1973
 
 
1974
static int option_parse_name(pa_config_parser_state *state) {
 
1975
    pa_alsa_path *p;
 
1976
    pa_alsa_option *o;
 
1977
 
 
1978
    pa_assert(state);
 
1979
 
 
1980
    p = state->userdata;
 
1981
 
 
1982
    if (!(o = option_get(p, state->section))) {
 
1983
        pa_log("[%s:%u] Name makes no sense in '%s'", state->filename, state->lineno, state->section);
 
1984
        return -1;
 
1985
    }
 
1986
 
 
1987
    pa_xfree(o->name);
 
1988
    o->name = pa_xstrdup(state->rvalue);
 
1989
 
 
1990
    return 0;
 
1991
}
 
1992
 
 
1993
static int element_parse_required(pa_config_parser_state *state) {
 
1994
    pa_alsa_path *p;
 
1995
    pa_alsa_element *e;
 
1996
    pa_alsa_option *o;
 
1997
    pa_alsa_jack *j;
 
1998
    pa_alsa_required_t req;
 
1999
 
 
2000
    pa_assert(state);
 
2001
 
 
2002
    p = state->userdata;
 
2003
 
 
2004
    e = element_get(p, state->section, true);
 
2005
    o = option_get(p, state->section);
 
2006
    j = jack_get(p, state->section);
 
2007
    if (!e && !o && !j) {
 
2008
        pa_log("[%s:%u] Required makes no sense in '%s'", state->filename, state->lineno, state->section);
 
2009
        return -1;
 
2010
    }
 
2011
 
 
2012
    if (pa_streq(state->rvalue, "ignore"))
 
2013
        req = PA_ALSA_REQUIRED_IGNORE;
 
2014
    else if (pa_streq(state->rvalue, "switch") && e)
 
2015
        req = PA_ALSA_REQUIRED_SWITCH;
 
2016
    else if (pa_streq(state->rvalue, "volume") && e)
 
2017
        req = PA_ALSA_REQUIRED_VOLUME;
 
2018
    else if (pa_streq(state->rvalue, "enumeration"))
 
2019
        req = PA_ALSA_REQUIRED_ENUMERATION;
 
2020
    else if (pa_streq(state->rvalue, "any"))
 
2021
        req = PA_ALSA_REQUIRED_ANY;
 
2022
    else {
 
2023
        pa_log("[%s:%u] Required invalid of '%s'", state->filename, state->lineno, state->section);
 
2024
        return -1;
 
2025
    }
 
2026
 
 
2027
    if (pa_streq(state->lvalue, "required-absent")) {
 
2028
        if (e)
 
2029
            e->required_absent = req;
 
2030
        if (o)
 
2031
            o->required_absent = req;
 
2032
        if (j)
 
2033
            j->required_absent = req;
 
2034
    }
 
2035
    else if (pa_streq(state->lvalue, "required-any")) {
 
2036
        if (e) {
 
2037
            e->required_any = req;
 
2038
            e->path->has_req_any |= (req != PA_ALSA_REQUIRED_IGNORE);
 
2039
        }
 
2040
        if (o) {
 
2041
            o->required_any = req;
 
2042
            o->element->path->has_req_any |= (req != PA_ALSA_REQUIRED_IGNORE);
 
2043
        }
 
2044
        if (j) {
 
2045
            j->required_any = req;
 
2046
            j->path->has_req_any |= (req != PA_ALSA_REQUIRED_IGNORE);
 
2047
        }
 
2048
 
 
2049
    }
 
2050
    else {
 
2051
        if (e)
 
2052
            e->required = req;
 
2053
        if (o)
 
2054
            o->required = req;
 
2055
        if (j)
 
2056
            j->required = req;
 
2057
    }
 
2058
 
 
2059
    return 0;
 
2060
}
 
2061
 
 
2062
static int element_parse_direction(pa_config_parser_state *state) {
 
2063
    pa_alsa_path *p;
 
2064
    pa_alsa_element *e;
 
2065
 
 
2066
    pa_assert(state);
 
2067
 
 
2068
    p = state->userdata;
 
2069
 
 
2070
    if (!(e = element_get(p, state->section, true))) {
 
2071
        pa_log("[%s:%u] Direction makes no sense in '%s'", state->filename, state->lineno, state->section);
 
2072
        return -1;
 
2073
    }
 
2074
 
 
2075
    if (pa_streq(state->rvalue, "playback"))
 
2076
        e->direction = PA_ALSA_DIRECTION_OUTPUT;
 
2077
    else if (pa_streq(state->rvalue, "capture"))
 
2078
        e->direction = PA_ALSA_DIRECTION_INPUT;
 
2079
    else {
 
2080
        pa_log("[%s:%u] Direction invalid of '%s'", state->filename, state->lineno, state->section);
 
2081
        return -1;
 
2082
    }
 
2083
 
 
2084
    return 0;
 
2085
}
 
2086
 
 
2087
static int element_parse_direction_try_other(pa_config_parser_state *state) {
 
2088
    pa_alsa_path *p;
 
2089
    pa_alsa_element *e;
 
2090
    int yes;
 
2091
 
 
2092
    pa_assert(state);
 
2093
 
 
2094
    p = state->userdata;
 
2095
 
 
2096
    if (!(e = element_get(p, state->section, true))) {
 
2097
        pa_log("[%s:%u] Direction makes no sense in '%s'", state->filename, state->lineno, state->section);
 
2098
        return -1;
 
2099
    }
 
2100
 
 
2101
    if ((yes = pa_parse_boolean(state->rvalue)) < 0) {
 
2102
        pa_log("[%s:%u] Direction invalid of '%s'", state->filename, state->lineno, state->section);
 
2103
        return -1;
 
2104
    }
 
2105
 
 
2106
    e->direction_try_other = !!yes;
 
2107
    return 0;
 
2108
}
 
2109
 
 
2110
static int element_parse_volume_limit(pa_config_parser_state *state) {
 
2111
    pa_alsa_path *p;
 
2112
    pa_alsa_element *e;
 
2113
    long volume_limit;
 
2114
 
 
2115
    pa_assert(state);
 
2116
 
 
2117
    p = state->userdata;
 
2118
 
 
2119
    if (!(e = element_get(p, state->section, true))) {
 
2120
        pa_log("[%s:%u] volume-limit makes no sense in '%s'", state->filename, state->lineno, state->section);
 
2121
        return -1;
 
2122
    }
 
2123
 
 
2124
    if (pa_atol(state->rvalue, &volume_limit) < 0 || volume_limit < 0) {
 
2125
        pa_log("[%s:%u] Invalid value for volume-limit", state->filename, state->lineno);
 
2126
        return -1;
 
2127
    }
 
2128
 
 
2129
    e->volume_limit = volume_limit;
 
2130
    return 0;
 
2131
}
 
2132
 
 
2133
static pa_channel_position_mask_t parse_mask(const char *m) {
 
2134
    pa_channel_position_mask_t v;
 
2135
 
 
2136
    if (pa_streq(m, "all-left"))
 
2137
        v = PA_CHANNEL_POSITION_MASK_LEFT;
 
2138
    else if (pa_streq(m, "all-right"))
 
2139
        v = PA_CHANNEL_POSITION_MASK_RIGHT;
 
2140
    else if (pa_streq(m, "all-center"))
 
2141
        v = PA_CHANNEL_POSITION_MASK_CENTER;
 
2142
    else if (pa_streq(m, "all-front"))
 
2143
        v = PA_CHANNEL_POSITION_MASK_FRONT;
 
2144
    else if (pa_streq(m, "all-rear"))
 
2145
        v = PA_CHANNEL_POSITION_MASK_REAR;
 
2146
    else if (pa_streq(m, "all-side"))
 
2147
        v = PA_CHANNEL_POSITION_MASK_SIDE_OR_TOP_CENTER;
 
2148
    else if (pa_streq(m, "all-top"))
 
2149
        v = PA_CHANNEL_POSITION_MASK_TOP;
 
2150
    else if (pa_streq(m, "all-no-lfe"))
 
2151
        v = PA_CHANNEL_POSITION_MASK_ALL ^ PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_LFE);
 
2152
    else if (pa_streq(m, "all"))
 
2153
        v = PA_CHANNEL_POSITION_MASK_ALL;
 
2154
    else {
 
2155
        pa_channel_position_t p;
 
2156
 
 
2157
        if ((p = pa_channel_position_from_string(m)) == PA_CHANNEL_POSITION_INVALID)
 
2158
            return 0;
 
2159
 
 
2160
        v = PA_CHANNEL_POSITION_MASK(p);
 
2161
    }
 
2162
 
 
2163
    return v;
 
2164
}
 
2165
 
 
2166
static int element_parse_override_map(pa_config_parser_state *state) {
 
2167
    pa_alsa_path *p;
 
2168
    pa_alsa_element *e;
 
2169
    const char *split_state = NULL;
 
2170
    unsigned i = 0;
 
2171
    char *n;
 
2172
 
 
2173
    pa_assert(state);
 
2174
 
 
2175
    p = state->userdata;
 
2176
 
 
2177
    if (!(e = element_get(p, state->section, true))) {
 
2178
        pa_log("[%s:%u] Override map makes no sense in '%s'", state->filename, state->lineno, state->section);
 
2179
        return -1;
 
2180
    }
 
2181
 
 
2182
    while ((n = pa_split(state->rvalue, ",", &split_state))) {
 
2183
        pa_channel_position_mask_t m;
 
2184
 
 
2185
        if (!*n)
 
2186
            m = 0;
 
2187
        else {
 
2188
            if ((m = parse_mask(n)) == 0) {
 
2189
                pa_log("[%s:%u] Override map '%s' invalid in '%s'", state->filename, state->lineno, n, state->section);
 
2190
                pa_xfree(n);
 
2191
                return -1;
 
2192
            }
 
2193
        }
 
2194
 
 
2195
        if (pa_streq(state->lvalue, "override-map.1"))
 
2196
            e->masks[i++][0] = m;
 
2197
        else
 
2198
            e->masks[i++][1] = m;
 
2199
 
 
2200
        /* Later on we might add override-map.3 and so on here ... */
 
2201
 
 
2202
        pa_xfree(n);
 
2203
    }
 
2204
 
 
2205
    e->override_map = true;
 
2206
 
 
2207
    return 0;
 
2208
}
 
2209
 
 
2210
static int jack_parse_state(pa_config_parser_state *state) {
 
2211
    pa_alsa_path *p;
 
2212
    pa_alsa_jack *j;
 
2213
    pa_available_t pa;
 
2214
 
 
2215
    pa_assert(state);
 
2216
 
 
2217
    p = state->userdata;
 
2218
 
 
2219
    if (!(j = jack_get(p, state->section))) {
 
2220
        pa_log("[%s:%u] state makes no sense in '%s'", state->filename, state->lineno, state->section);
 
2221
        return -1;
 
2222
    }
 
2223
 
 
2224
    if (pa_streq(state->rvalue, "yes"))
 
2225
        pa = PA_AVAILABLE_YES;
 
2226
    else if (pa_streq(state->rvalue, "no"))
 
2227
        pa = PA_AVAILABLE_NO;
 
2228
    else if (pa_streq(state->rvalue, "unknown"))
 
2229
        pa = PA_AVAILABLE_UNKNOWN;
 
2230
    else {
 
2231
        pa_log("[%s:%u] state must be 'yes', 'no' or 'unknown' in '%s'", state->filename, state->lineno, state->section);
 
2232
        return -1;
 
2233
    }
 
2234
 
 
2235
    if (pa_streq(state->lvalue, "state.unplugged"))
 
2236
        j->state_unplugged = pa;
 
2237
    else {
 
2238
        j->state_plugged = pa;
 
2239
        pa_assert(pa_streq(state->lvalue, "state.plugged"));
 
2240
    }
 
2241
 
 
2242
    return 0;
 
2243
}
 
2244
 
 
2245
static int element_set_option(pa_alsa_element *e, snd_mixer_t *m, int alsa_idx) {
 
2246
    snd_mixer_selem_id_t *sid;
 
2247
    snd_mixer_elem_t *me;
 
2248
    int r;
 
2249
 
 
2250
    pa_assert(e);
 
2251
    pa_assert(m);
 
2252
 
 
2253
    SELEM_INIT(sid, e->alsa_name);
 
2254
    if (!(me = snd_mixer_find_selem(m, sid))) {
 
2255
        pa_log_warn("Element %s seems to have disappeared.", e->alsa_name);
 
2256
        return -1;
 
2257
    }
 
2258
 
 
2259
    if (e->switch_use == PA_ALSA_SWITCH_SELECT) {
 
2260
 
 
2261
        if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
 
2262
            r = snd_mixer_selem_set_playback_switch_all(me, alsa_idx);
 
2263
        else
 
2264
            r = snd_mixer_selem_set_capture_switch_all(me, alsa_idx);
 
2265
 
 
2266
        if (r < 0)
 
2267
            pa_log_warn("Failed to set switch of %s: %s", e->alsa_name, pa_alsa_strerror(errno));
 
2268
 
 
2269
    } else {
 
2270
        pa_assert(e->enumeration_use == PA_ALSA_ENUMERATION_SELECT);
 
2271
 
 
2272
        if ((r = snd_mixer_selem_set_enum_item(me, 0, alsa_idx)) < 0)
 
2273
            pa_log_warn("Failed to set enumeration of %s: %s", e->alsa_name, pa_alsa_strerror(errno));
 
2274
    }
 
2275
 
 
2276
    return r;
 
2277
}
 
2278
 
 
2279
static int setting_select(pa_alsa_setting *s, snd_mixer_t *m) {
 
2280
    pa_alsa_option *o;
 
2281
    uint32_t idx;
 
2282
 
 
2283
    pa_assert(s);
 
2284
    pa_assert(m);
 
2285
 
 
2286
    PA_IDXSET_FOREACH(o, s->options, idx)
 
2287
        element_set_option(o->element, m, o->alsa_idx);
 
2288
 
 
2289
    return 0;
 
2290
}
 
2291
 
 
2292
static int option_verify(pa_alsa_option *o) {
 
2293
    static const struct description_map well_known_descriptions[] = {
 
2294
        { "input",                     N_("Input") },
 
2295
        { "input-docking",             N_("Docking Station Input") },
 
2296
        { "input-docking-microphone",  N_("Docking Station Microphone") },
 
2297
        { "input-docking-linein",      N_("Docking Station Line In") },
 
2298
        { "input-linein",              N_("Line In") },
 
2299
        { "input-microphone",          N_("Microphone") },
 
2300
        { "input-microphone-front",    N_("Front Microphone") },
 
2301
        { "input-microphone-rear",     N_("Rear Microphone") },
 
2302
        { "input-microphone-external", N_("External Microphone") },
 
2303
        { "input-microphone-internal", N_("Internal Microphone") },
 
2304
        { "input-radio",               N_("Radio") },
 
2305
        { "input-video",               N_("Video") },
 
2306
        { "input-agc-on",              N_("Automatic Gain Control") },
 
2307
        { "input-agc-off",             N_("No Automatic Gain Control") },
 
2308
        { "input-boost-on",            N_("Boost") },
 
2309
        { "input-boost-off",           N_("No Boost") },
 
2310
        { "output-amplifier-on",       N_("Amplifier") },
 
2311
        { "output-amplifier-off",      N_("No Amplifier") },
 
2312
        { "output-bass-boost-on",      N_("Bass Boost") },
 
2313
        { "output-bass-boost-off",     N_("No Bass Boost") },
 
2314
        { "output-speaker",            N_("Speaker") },
 
2315
        { "output-headphones",         N_("Headphones") }
 
2316
    };
 
2317
 
 
2318
    pa_assert(o);
 
2319
 
 
2320
    if (!o->name) {
 
2321
        pa_log("No name set for option %s", o->alsa_name);
 
2322
        return -1;
 
2323
    }
 
2324
 
 
2325
    if (o->element->enumeration_use != PA_ALSA_ENUMERATION_SELECT &&
 
2326
        o->element->switch_use != PA_ALSA_SWITCH_SELECT) {
 
2327
        pa_log("Element %s of option %s not set for select.", o->element->alsa_name, o->name);
 
2328
        return -1;
 
2329
    }
 
2330
 
 
2331
    if (o->element->switch_use == PA_ALSA_SWITCH_SELECT &&
 
2332
        !pa_streq(o->alsa_name, "on") &&
 
2333
        !pa_streq(o->alsa_name, "off")) {
 
2334
        pa_log("Switch %s options need be named off or on ", o->element->alsa_name);
 
2335
        return -1;
 
2336
    }
 
2337
 
 
2338
    if (!o->description)
 
2339
        o->description = pa_xstrdup(lookup_description(o->name,
 
2340
                                                       well_known_descriptions,
 
2341
                                                       PA_ELEMENTSOF(well_known_descriptions)));
 
2342
    if (!o->description)
 
2343
        o->description = pa_xstrdup(o->name);
 
2344
 
 
2345
    return 0;
 
2346
}
 
2347
 
 
2348
static int element_verify(pa_alsa_element *e) {
 
2349
    pa_alsa_option *o;
 
2350
 
 
2351
    pa_assert(e);
 
2352
 
 
2353
//    pa_log_debug("Element %s, path %s: r=%d, r-any=%d, r-abs=%d", e->alsa_name, e->path->name, e->required, e->required_any, e->required_absent);
 
2354
    if ((e->required != PA_ALSA_REQUIRED_IGNORE && e->required == e->required_absent) ||
 
2355
        (e->required_any != PA_ALSA_REQUIRED_IGNORE && e->required_any == e->required_absent) ||
 
2356
        (e->required_absent == PA_ALSA_REQUIRED_ANY && e->required_any != PA_ALSA_REQUIRED_IGNORE) ||
 
2357
        (e->required_absent == PA_ALSA_REQUIRED_ANY && e->required != PA_ALSA_REQUIRED_IGNORE)) {
 
2358
        pa_log("Element %s cannot be required and absent at the same time.", e->alsa_name);
 
2359
        return -1;
 
2360
    }
 
2361
 
 
2362
    if (e->switch_use == PA_ALSA_SWITCH_SELECT && e->enumeration_use == PA_ALSA_ENUMERATION_SELECT) {
 
2363
        pa_log("Element %s cannot set select for both switch and enumeration.", e->alsa_name);
 
2364
        return -1;
 
2365
    }
 
2366
 
 
2367
    PA_LLIST_FOREACH(o, e->options)
 
2368
        if (option_verify(o) < 0)
 
2369
            return -1;
 
2370
 
 
2371
    return 0;
 
2372
}
 
2373
 
 
2374
static int path_verify(pa_alsa_path *p) {
 
2375
    static const struct description_map well_known_descriptions[] = {
 
2376
        { "analog-input",               N_("Analog Input") },
 
2377
        { "analog-input-microphone",    N_("Microphone") },
 
2378
        { "analog-input-microphone-front",    N_("Front Microphone") },
 
2379
        { "analog-input-microphone-rear",     N_("Rear Microphone") },
 
2380
        { "analog-input-microphone-dock",     N_("Dock Microphone") },
 
2381
        { "analog-input-microphone-internal", N_("Internal Microphone") },
 
2382
        { "analog-input-microphone-headset",  N_("Headset Microphone") },
 
2383
        { "analog-input-linein",        N_("Line In") },
 
2384
        { "analog-input-radio",         N_("Radio") },
 
2385
        { "analog-input-video",         N_("Video") },
 
2386
        { "analog-output",              N_("Analog Output") },
 
2387
        { "analog-output-headphones",   N_("Headphones") },
 
2388
        { "analog-output-lfe-on-mono",  N_("LFE on Separate Mono Output") },
 
2389
        { "analog-output-lineout",      N_("Line Out") },
 
2390
        { "analog-output-mono",         N_("Analog Mono Output") },
 
2391
        { "analog-output-speaker",      N_("Speakers") },
 
2392
        { "hdmi-output",                N_("HDMI / DisplayPort") },
 
2393
        { "iec958-stereo-output",       N_("Digital Output (S/PDIF)") },
 
2394
        { "iec958-stereo-input",        N_("Digital Input (S/PDIF)") },
 
2395
        { "iec958-passthrough-output",  N_("Digital Passthrough (S/PDIF)") }
 
2396
    };
 
2397
 
 
2398
    pa_alsa_element *e;
 
2399
 
 
2400
    pa_assert(p);
 
2401
 
 
2402
    PA_LLIST_FOREACH(e, p->elements)
 
2403
        if (element_verify(e) < 0)
 
2404
            return -1;
 
2405
 
 
2406
    if (!p->description)
 
2407
        p->description = pa_xstrdup(lookup_description(p->description_key ? p->description_key : p->name,
 
2408
                                                       well_known_descriptions,
 
2409
                                                       PA_ELEMENTSOF(well_known_descriptions)));
 
2410
 
 
2411
    if (!p->description) {
 
2412
        if (p->description_key)
 
2413
            pa_log_warn("Path %s: Unrecognized description key: %s", p->name, p->description_key);
 
2414
 
 
2415
        p->description = pa_xstrdup(p->name);
 
2416
    }
 
2417
 
 
2418
    return 0;
 
2419
}
 
2420
 
 
2421
static const char *get_default_paths_dir(void) {
 
2422
    if (pa_run_from_build_tree())
 
2423
        return PA_SRCDIR "/modules/alsa/mixer/paths/";
 
2424
    else
 
2425
        return PA_ALSA_PATHS_DIR;
 
2426
}
 
2427
 
 
2428
pa_alsa_path* pa_alsa_path_new(const char *paths_dir, const char *fname, pa_alsa_direction_t direction) {
 
2429
    pa_alsa_path *p;
 
2430
    char *fn;
 
2431
    int r;
 
2432
    const char *n;
 
2433
    bool mute_during_activation = false;
 
2434
 
 
2435
    pa_config_item items[] = {
 
2436
        /* [General] */
 
2437
        { "priority",            pa_config_parse_unsigned,          NULL, "General" },
 
2438
        { "description-key",     pa_config_parse_string,            NULL, "General" },
 
2439
        { "description",         pa_config_parse_string,            NULL, "General" },
 
2440
        { "mute-during-activation", pa_config_parse_bool,           NULL, "General" },
 
2441
        { "eld-device",          pa_config_parse_int,               NULL, "General" },
 
2442
 
 
2443
        /* [Option ...] */
 
2444
        { "priority",            option_parse_priority,             NULL, NULL },
 
2445
        { "name",                option_parse_name,                 NULL, NULL },
 
2446
 
 
2447
        /* [Jack ...] */
 
2448
        { "state.plugged",       jack_parse_state,                  NULL, NULL },
 
2449
        { "state.unplugged",     jack_parse_state,                  NULL, NULL },
 
2450
 
 
2451
        /* [Element ...] */
 
2452
        { "switch",              element_parse_switch,              NULL, NULL },
 
2453
        { "volume",              element_parse_volume,              NULL, NULL },
 
2454
        { "enumeration",         element_parse_enumeration,         NULL, NULL },
 
2455
        { "override-map.1",      element_parse_override_map,        NULL, NULL },
 
2456
        { "override-map.2",      element_parse_override_map,        NULL, NULL },
 
2457
        /* ... later on we might add override-map.3 and so on here ... */
 
2458
        { "required",            element_parse_required,            NULL, NULL },
 
2459
        { "required-any",        element_parse_required,            NULL, NULL },
 
2460
        { "required-absent",     element_parse_required,            NULL, NULL },
 
2461
        { "direction",           element_parse_direction,           NULL, NULL },
 
2462
        { "direction-try-other", element_parse_direction_try_other, NULL, NULL },
 
2463
        { "volume-limit",        element_parse_volume_limit,        NULL, NULL },
 
2464
        { NULL, NULL, NULL, NULL }
 
2465
    };
 
2466
 
 
2467
    pa_assert(fname);
 
2468
 
 
2469
    p = pa_xnew0(pa_alsa_path, 1);
 
2470
    n = pa_path_get_filename(fname);
 
2471
    p->name = pa_xstrndup(n, strcspn(n, "."));
 
2472
    p->proplist = pa_proplist_new();
 
2473
    p->direction = direction;
 
2474
    p->eld_device = -1;
 
2475
 
 
2476
    items[0].data = &p->priority;
 
2477
    items[1].data = &p->description_key;
 
2478
    items[2].data = &p->description;
 
2479
    items[3].data = &mute_during_activation;
 
2480
    items[4].data = &p->eld_device;
 
2481
 
 
2482
    if (!paths_dir)
 
2483
        paths_dir = get_default_paths_dir();
 
2484
 
 
2485
    fn = pa_maybe_prefix_path(fname, paths_dir);
 
2486
 
 
2487
    r = pa_config_parse(fn, NULL, items, p->proplist, p);
 
2488
    pa_xfree(fn);
 
2489
 
 
2490
    if (r < 0)
 
2491
        goto fail;
 
2492
 
 
2493
    p->mute_during_activation = mute_during_activation;
 
2494
 
 
2495
    if (path_verify(p) < 0)
 
2496
        goto fail;
 
2497
 
 
2498
    return p;
 
2499
 
 
2500
fail:
 
2501
    pa_alsa_path_free(p);
 
2502
    return NULL;
 
2503
}
 
2504
 
 
2505
pa_alsa_path *pa_alsa_path_synthesize(const char *element, pa_alsa_direction_t direction) {
 
2506
    pa_alsa_path *p;
 
2507
    pa_alsa_element *e;
 
2508
 
 
2509
    pa_assert(element);
 
2510
 
 
2511
    p = pa_xnew0(pa_alsa_path, 1);
 
2512
    p->name = pa_xstrdup(element);
 
2513
    p->direction = direction;
 
2514
 
 
2515
    e = pa_xnew0(pa_alsa_element, 1);
 
2516
    e->path = p;
 
2517
    e->alsa_name = pa_xstrdup(element);
 
2518
    e->direction = direction;
 
2519
    e->volume_limit = -1;
 
2520
 
 
2521
    e->switch_use = PA_ALSA_SWITCH_MUTE;
 
2522
    e->volume_use = PA_ALSA_VOLUME_MERGE;
 
2523
 
 
2524
    PA_LLIST_PREPEND(pa_alsa_element, p->elements, e);
 
2525
    p->last_element = e;
 
2526
    return p;
 
2527
}
 
2528
 
 
2529
static bool element_drop_unsupported(pa_alsa_element *e) {
 
2530
    pa_alsa_option *o, *n;
 
2531
 
 
2532
    pa_assert(e);
 
2533
 
 
2534
    for (o = e->options; o; o = n) {
 
2535
        n = o->next;
 
2536
 
 
2537
        if (o->alsa_idx < 0) {
 
2538
            PA_LLIST_REMOVE(pa_alsa_option, e->options, o);
 
2539
            option_free(o);
 
2540
        }
 
2541
    }
 
2542
 
 
2543
    return
 
2544
        e->switch_use != PA_ALSA_SWITCH_IGNORE ||
 
2545
        e->volume_use != PA_ALSA_VOLUME_IGNORE ||
 
2546
        e->enumeration_use != PA_ALSA_ENUMERATION_IGNORE;
 
2547
}
 
2548
 
 
2549
static void path_drop_unsupported(pa_alsa_path *p) {
 
2550
    pa_alsa_element *e, *n;
 
2551
 
 
2552
    pa_assert(p);
 
2553
 
 
2554
    for (e = p->elements; e; e = n) {
 
2555
        n = e->next;
 
2556
 
 
2557
        if (!element_drop_unsupported(e)) {
 
2558
            PA_LLIST_REMOVE(pa_alsa_element, p->elements, e);
 
2559
            element_free(e);
 
2560
        }
 
2561
    }
 
2562
}
 
2563
 
 
2564
static void path_make_options_unique(pa_alsa_path *p) {
 
2565
    pa_alsa_element *e;
 
2566
    pa_alsa_option *o, *u;
 
2567
 
 
2568
    PA_LLIST_FOREACH(e, p->elements) {
 
2569
        PA_LLIST_FOREACH(o, e->options) {
 
2570
            unsigned i;
 
2571
            char *m;
 
2572
 
 
2573
            for (u = o->next; u; u = u->next)
 
2574
                if (pa_streq(u->name, o->name))
 
2575
                    break;
 
2576
 
 
2577
            if (!u)
 
2578
                continue;
 
2579
 
 
2580
            m = pa_xstrdup(o->name);
 
2581
 
 
2582
            /* OK, this name is not unique, hence let's rename */
 
2583
            for (i = 1, u = o; u; u = u->next) {
 
2584
                char *nn, *nd;
 
2585
 
 
2586
                if (!pa_streq(u->name, m))
 
2587
                    continue;
 
2588
 
 
2589
                nn = pa_sprintf_malloc("%s-%u", m, i);
 
2590
                pa_xfree(u->name);
 
2591
                u->name = nn;
 
2592
 
 
2593
                nd = pa_sprintf_malloc("%s %u", u->description, i);
 
2594
                pa_xfree(u->description);
 
2595
                u->description = nd;
 
2596
 
 
2597
                i++;
 
2598
            }
 
2599
 
 
2600
            pa_xfree(m);
 
2601
        }
 
2602
    }
 
2603
}
 
2604
 
 
2605
static bool element_create_settings(pa_alsa_element *e, pa_alsa_setting *template) {
 
2606
    pa_alsa_option *o;
 
2607
 
 
2608
    for (; e; e = e->next)
 
2609
        if (e->switch_use == PA_ALSA_SWITCH_SELECT ||
 
2610
            e->enumeration_use == PA_ALSA_ENUMERATION_SELECT)
 
2611
            break;
 
2612
 
 
2613
    if (!e)
 
2614
        return false;
 
2615
 
 
2616
    for (o = e->options; o; o = o->next) {
 
2617
        pa_alsa_setting *s;
 
2618
 
 
2619
        if (template) {
 
2620
            s = pa_xnewdup(pa_alsa_setting, template, 1);
 
2621
            s->options = pa_idxset_copy(template->options, NULL);
 
2622
            s->name = pa_sprintf_malloc("%s+%s", template->name, o->name);
 
2623
            s->description =
 
2624
                (template->description[0] && o->description[0])
 
2625
                ? pa_sprintf_malloc("%s / %s", template->description, o->description)
 
2626
                : (template->description[0]
 
2627
                   ? pa_xstrdup(template->description)
 
2628
                   : pa_xstrdup(o->description));
 
2629
 
 
2630
            s->priority = PA_MAX(template->priority, o->priority);
 
2631
        } else {
 
2632
            s = pa_xnew0(pa_alsa_setting, 1);
 
2633
            s->options = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
 
2634
            s->name = pa_xstrdup(o->name);
 
2635
            s->description = pa_xstrdup(o->description);
 
2636
            s->priority = o->priority;
 
2637
        }
 
2638
 
 
2639
        pa_idxset_put(s->options, o, NULL);
 
2640
 
 
2641
        if (element_create_settings(e->next, s))
 
2642
            /* This is not a leaf, so let's get rid of it */
 
2643
            setting_free(s);
 
2644
        else {
 
2645
            /* This is a leaf, so let's add it */
 
2646
            PA_LLIST_INSERT_AFTER(pa_alsa_setting, e->path->settings, e->path->last_setting, s);
 
2647
 
 
2648
            e->path->last_setting = s;
 
2649
        }
 
2650
    }
 
2651
 
 
2652
    return true;
 
2653
}
 
2654
 
 
2655
static void path_create_settings(pa_alsa_path *p) {
 
2656
    pa_assert(p);
 
2657
 
 
2658
    element_create_settings(p->elements, NULL);
 
2659
}
 
2660
 
 
2661
int pa_alsa_path_probe(pa_alsa_path *p, snd_mixer_t *m, bool ignore_dB) {
 
2662
    pa_alsa_element *e;
 
2663
    pa_alsa_jack *j;
 
2664
    double min_dB[PA_CHANNEL_POSITION_MAX], max_dB[PA_CHANNEL_POSITION_MAX];
 
2665
    pa_channel_position_t t;
 
2666
    pa_channel_position_mask_t path_volume_channels = 0;
 
2667
 
 
2668
    pa_assert(p);
 
2669
    pa_assert(m);
 
2670
 
 
2671
    if (p->probed)
 
2672
        return p->supported ? 0 : -1;
 
2673
    p->probed = true;
 
2674
 
 
2675
    pa_zero(min_dB);
 
2676
    pa_zero(max_dB);
 
2677
 
 
2678
    pa_log_debug("Probing path '%s'", p->name);
 
2679
 
 
2680
    PA_LLIST_FOREACH(j, p->jacks) {
 
2681
        if (jack_probe(j, m) < 0) {
 
2682
            p->supported = false;
 
2683
            pa_log_debug("Probe of jack '%s' failed.", j->alsa_name);
 
2684
            return -1;
 
2685
        }
 
2686
        pa_log_debug("Probe of jack '%s' succeeded (%s)", j->alsa_name, j->has_control ? "found!" : "not found");
 
2687
    }
 
2688
 
 
2689
    PA_LLIST_FOREACH(e, p->elements) {
 
2690
        if (element_probe(e, m) < 0) {
 
2691
            p->supported = false;
 
2692
            pa_log_debug("Probe of element '%s' failed.", e->alsa_name);
 
2693
            return -1;
 
2694
        }
 
2695
        pa_log_debug("Probe of element '%s' succeeded (volume=%d, switch=%d, enumeration=%d).", e->alsa_name, e->volume_use, e->switch_use, e->enumeration_use);
 
2696
 
 
2697
        if (ignore_dB)
 
2698
            e->has_dB = false;
 
2699
 
 
2700
        if (e->volume_use == PA_ALSA_VOLUME_MERGE) {
 
2701
 
 
2702
            if (!p->has_volume) {
 
2703
                p->min_volume = e->min_volume;
 
2704
                p->max_volume = e->max_volume;
 
2705
            }
 
2706
 
 
2707
            if (e->has_dB) {
 
2708
                if (!p->has_volume) {
 
2709
                    for (t = 0; t < PA_CHANNEL_POSITION_MAX; t++)
 
2710
                        if (PA_CHANNEL_POSITION_MASK(t) & e->merged_mask) {
 
2711
                            min_dB[t] = e->min_dB;
 
2712
                            max_dB[t] = e->max_dB;
 
2713
                            path_volume_channels |= PA_CHANNEL_POSITION_MASK(t);
 
2714
                        }
 
2715
 
 
2716
                    p->has_dB = true;
 
2717
                } else {
 
2718
 
 
2719
                    if (p->has_dB) {
 
2720
                        for (t = 0; t < PA_CHANNEL_POSITION_MAX; t++)
 
2721
                            if (PA_CHANNEL_POSITION_MASK(t) & e->merged_mask) {
 
2722
                                min_dB[t] += e->min_dB;
 
2723
                                max_dB[t] += e->max_dB;
 
2724
                                path_volume_channels |= PA_CHANNEL_POSITION_MASK(t);
 
2725
                            }
 
2726
                    } else {
 
2727
                        /* Hmm, there's another element before us
 
2728
                         * which cannot do dB volumes, so we we need
 
2729
                         * to 'neutralize' this slider */
 
2730
                        e->volume_use = PA_ALSA_VOLUME_ZERO;
 
2731
                        pa_log_info("Zeroing volume of '%s' on path '%s'", e->alsa_name, p->name);
 
2732
                    }
 
2733
                }
 
2734
            } else if (p->has_volume) {
 
2735
                /* We can't use this volume, so let's ignore it */
 
2736
                e->volume_use = PA_ALSA_VOLUME_IGNORE;
 
2737
                pa_log_info("Ignoring volume of '%s' on path '%s' (missing dB info)", e->alsa_name, p->name);
 
2738
            }
 
2739
            p->has_volume = true;
 
2740
        }
 
2741
 
 
2742
        if (e->switch_use == PA_ALSA_SWITCH_MUTE)
 
2743
            p->has_mute = true;
 
2744
    }
 
2745
 
 
2746
    if (p->has_req_any && !p->req_any_present) {
 
2747
        p->supported = false;
 
2748
        pa_log_debug("Skipping path '%s', none of required-any elements preset.", p->name);
 
2749
        return -1;
 
2750
    }
 
2751
 
 
2752
    path_drop_unsupported(p);
 
2753
    path_make_options_unique(p);
 
2754
    path_create_settings(p);
 
2755
 
 
2756
    p->supported = true;
 
2757
 
 
2758
    p->min_dB = INFINITY;
 
2759
    p->max_dB = -INFINITY;
 
2760
 
 
2761
    for (t = 0; t < PA_CHANNEL_POSITION_MAX; t++) {
 
2762
        if (path_volume_channels & PA_CHANNEL_POSITION_MASK(t)) {
 
2763
            if (p->min_dB > min_dB[t])
 
2764
                p->min_dB = min_dB[t];
 
2765
 
 
2766
            if (p->max_dB < max_dB[t])
 
2767
                p->max_dB = max_dB[t];
 
2768
        }
 
2769
    }
 
2770
 
 
2771
    return 0;
 
2772
}
 
2773
 
 
2774
void pa_alsa_setting_dump(pa_alsa_setting *s) {
 
2775
    pa_assert(s);
 
2776
 
 
2777
    pa_log_debug("Setting %s (%s) priority=%u",
 
2778
                 s->name,
 
2779
                 pa_strnull(s->description),
 
2780
                 s->priority);
 
2781
}
 
2782
 
 
2783
void pa_alsa_jack_dump(pa_alsa_jack *j) {
 
2784
    pa_assert(j);
 
2785
 
 
2786
    pa_log_debug("Jack %s, alsa_name='%s', detection %s", j->name, j->alsa_name, j->has_control ? "possible" : "unavailable");
 
2787
}
 
2788
 
 
2789
void pa_alsa_option_dump(pa_alsa_option *o) {
 
2790
    pa_assert(o);
 
2791
 
 
2792
    pa_log_debug("Option %s (%s/%s) index=%i, priority=%u",
 
2793
                 o->alsa_name,
 
2794
                 pa_strnull(o->name),
 
2795
                 pa_strnull(o->description),
 
2796
                 o->alsa_idx,
 
2797
                 o->priority);
 
2798
}
 
2799
 
 
2800
void pa_alsa_element_dump(pa_alsa_element *e) {
 
2801
    pa_alsa_option *o;
 
2802
    pa_assert(e);
 
2803
 
 
2804
    pa_log_debug("Element %s, direction=%i, switch=%i, volume=%i, volume_limit=%li, enumeration=%i, required=%i, required_any=%i, required_absent=%i, mask=0x%llx, n_channels=%u, override_map=%s",
 
2805
                 e->alsa_name,
 
2806
                 e->direction,
 
2807
                 e->switch_use,
 
2808
                 e->volume_use,
 
2809
                 e->volume_limit,
 
2810
                 e->enumeration_use,
 
2811
                 e->required,
 
2812
                 e->required_any,
 
2813
                 e->required_absent,
 
2814
                 (long long unsigned) e->merged_mask,
 
2815
                 e->n_channels,
 
2816
                 pa_yes_no(e->override_map));
 
2817
 
 
2818
    PA_LLIST_FOREACH(o, e->options)
 
2819
        pa_alsa_option_dump(o);
 
2820
}
 
2821
 
 
2822
void pa_alsa_path_dump(pa_alsa_path *p) {
 
2823
    pa_alsa_element *e;
 
2824
    pa_alsa_jack *j;
 
2825
    pa_alsa_setting *s;
 
2826
    pa_assert(p);
 
2827
 
 
2828
    pa_log_debug("Path %s (%s), direction=%i, priority=%u, probed=%s, supported=%s, has_mute=%s, has_volume=%s, "
 
2829
                 "has_dB=%s, min_volume=%li, max_volume=%li, min_dB=%g, max_dB=%g",
 
2830
                 p->name,
 
2831
                 pa_strnull(p->description),
 
2832
                 p->direction,
 
2833
                 p->priority,
 
2834
                 pa_yes_no(p->probed),
 
2835
                 pa_yes_no(p->supported),
 
2836
                 pa_yes_no(p->has_mute),
 
2837
                 pa_yes_no(p->has_volume),
 
2838
                 pa_yes_no(p->has_dB),
 
2839
                 p->min_volume, p->max_volume,
 
2840
                 p->min_dB, p->max_dB);
 
2841
 
 
2842
    PA_LLIST_FOREACH(e, p->elements)
 
2843
        pa_alsa_element_dump(e);
 
2844
 
 
2845
    PA_LLIST_FOREACH(j, p->jacks)
 
2846
        pa_alsa_jack_dump(j);
 
2847
 
 
2848
    PA_LLIST_FOREACH(s, p->settings)
 
2849
        pa_alsa_setting_dump(s);
 
2850
}
 
2851
 
 
2852
static void element_set_callback(pa_alsa_element *e, snd_mixer_t *m, snd_mixer_elem_callback_t cb, void *userdata) {
 
2853
    snd_mixer_selem_id_t *sid;
 
2854
    snd_mixer_elem_t *me;
 
2855
 
 
2856
    pa_assert(e);
 
2857
    pa_assert(m);
 
2858
    pa_assert(cb);
 
2859
 
 
2860
    SELEM_INIT(sid, e->alsa_name);
 
2861
    if (!(me = snd_mixer_find_selem(m, sid))) {
 
2862
        pa_log_warn("Element %s seems to have disappeared.", e->alsa_name);
 
2863
        return;
 
2864
    }
 
2865
 
 
2866
    snd_mixer_elem_set_callback(me, cb);
 
2867
    snd_mixer_elem_set_callback_private(me, userdata);
 
2868
}
 
2869
 
 
2870
void pa_alsa_path_set_callback(pa_alsa_path *p, snd_mixer_t *m, snd_mixer_elem_callback_t cb, void *userdata) {
 
2871
    pa_alsa_element *e;
 
2872
 
 
2873
    pa_assert(p);
 
2874
    pa_assert(m);
 
2875
    pa_assert(cb);
 
2876
 
 
2877
    PA_LLIST_FOREACH(e, p->elements)
 
2878
        element_set_callback(e, m, cb, userdata);
 
2879
}
 
2880
 
 
2881
void pa_alsa_path_set_set_callback(pa_alsa_path_set *ps, snd_mixer_t *m, snd_mixer_elem_callback_t cb, void *userdata) {
 
2882
    pa_alsa_path *p;
 
2883
    void *state;
 
2884
 
 
2885
    pa_assert(ps);
 
2886
    pa_assert(m);
 
2887
    pa_assert(cb);
 
2888
 
 
2889
    PA_HASHMAP_FOREACH(p, ps->paths, state)
 
2890
        pa_alsa_path_set_callback(p, m, cb, userdata);
 
2891
}
 
2892
 
 
2893
static pa_alsa_path *profile_set_get_path(pa_alsa_profile_set *ps, const char *path_name) {
 
2894
    pa_alsa_path *path;
 
2895
 
 
2896
    pa_assert(ps);
 
2897
    pa_assert(path_name);
 
2898
 
 
2899
    if ((path = pa_hashmap_get(ps->output_paths, path_name)))
 
2900
        return path;
 
2901
 
 
2902
    return pa_hashmap_get(ps->input_paths, path_name);
 
2903
}
 
2904
 
 
2905
static void profile_set_add_path(pa_alsa_profile_set *ps, pa_alsa_path *path) {
 
2906
    pa_assert(ps);
 
2907
    pa_assert(path);
 
2908
 
 
2909
    switch (path->direction) {
 
2910
        case PA_ALSA_DIRECTION_OUTPUT:
 
2911
            pa_assert_se(pa_hashmap_put(ps->output_paths, path->name, path) >= 0);
 
2912
            break;
 
2913
 
 
2914
        case PA_ALSA_DIRECTION_INPUT:
 
2915
            pa_assert_se(pa_hashmap_put(ps->input_paths, path->name, path) >= 0);
 
2916
            break;
 
2917
 
 
2918
        default:
 
2919
            pa_assert_not_reached();
 
2920
    }
 
2921
}
 
2922
 
 
2923
pa_alsa_path_set *pa_alsa_path_set_new(pa_alsa_mapping *m, pa_alsa_direction_t direction, const char *paths_dir) {
 
2924
    pa_alsa_path_set *ps;
 
2925
    char **pn = NULL, **en = NULL, **ie;
 
2926
    pa_alsa_decibel_fix *db_fix;
 
2927
    void *state, *state2;
 
2928
 
 
2929
    pa_assert(m);
 
2930
    pa_assert(m->profile_set);
 
2931
    pa_assert(m->profile_set->decibel_fixes);
 
2932
    pa_assert(direction == PA_ALSA_DIRECTION_OUTPUT || direction == PA_ALSA_DIRECTION_INPUT);
 
2933
 
 
2934
    if (m->direction != PA_ALSA_DIRECTION_ANY && m->direction != direction)
 
2935
        return NULL;
 
2936
 
 
2937
    ps = pa_xnew0(pa_alsa_path_set, 1);
 
2938
    ps->direction = direction;
 
2939
    ps->paths = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
 
2940
 
 
2941
    if (direction == PA_ALSA_DIRECTION_OUTPUT)
 
2942
        pn = m->output_path_names;
 
2943
    else
 
2944
        pn = m->input_path_names;
 
2945
 
 
2946
    if (pn) {
 
2947
        char **in;
 
2948
 
 
2949
        for (in = pn; *in; in++) {
 
2950
            pa_alsa_path *p = NULL;
 
2951
            bool duplicate = false;
 
2952
            char **kn;
 
2953
 
 
2954
            for (kn = pn; kn < in; kn++)
 
2955
                if (pa_streq(*kn, *in)) {
 
2956
                    duplicate = true;
 
2957
                    break;
 
2958
                }
 
2959
 
 
2960
            if (duplicate)
 
2961
                continue;
 
2962
 
 
2963
            p = profile_set_get_path(m->profile_set, *in);
 
2964
 
 
2965
            if (p && p->direction != direction) {
 
2966
                pa_log("Configuration error: Path %s is used both as an input and as an output path.", p->name);
 
2967
                goto fail;
 
2968
            }
 
2969
 
 
2970
            if (!p) {
 
2971
                char *fn = pa_sprintf_malloc("%s.conf", *in);
 
2972
                p = pa_alsa_path_new(paths_dir, fn, direction);
 
2973
                pa_xfree(fn);
 
2974
                if (p)
 
2975
                    profile_set_add_path(m->profile_set, p);
 
2976
            }
 
2977
 
 
2978
            if (p)
 
2979
                pa_hashmap_put(ps->paths, p, p);
 
2980
 
 
2981
        }
 
2982
 
 
2983
        goto finish;
 
2984
    }
 
2985
 
 
2986
    if (direction == PA_ALSA_DIRECTION_OUTPUT)
 
2987
        en = m->output_element;
 
2988
    else
 
2989
        en = m->input_element;
 
2990
 
 
2991
    if (!en)
 
2992
        goto fail;
 
2993
 
 
2994
    for (ie = en; *ie; ie++) {
 
2995
        char **je;
 
2996
        pa_alsa_path *p;
 
2997
 
 
2998
        p = pa_alsa_path_synthesize(*ie, direction);
 
2999
 
 
3000
        /* Mark all other passed elements for require-absent */
 
3001
        for (je = en; *je; je++) {
 
3002
            pa_alsa_element *e;
 
3003
 
 
3004
            if (je == ie)
 
3005
                continue;
 
3006
 
 
3007
            e = pa_xnew0(pa_alsa_element, 1);
 
3008
            e->path = p;
 
3009
            e->alsa_name = pa_xstrdup(*je);
 
3010
            e->direction = direction;
 
3011
            e->required_absent = PA_ALSA_REQUIRED_ANY;
 
3012
            e->volume_limit = -1;
 
3013
 
 
3014
            PA_LLIST_INSERT_AFTER(pa_alsa_element, p->elements, p->last_element, e);
 
3015
            p->last_element = e;
 
3016
        }
 
3017
 
 
3018
        pa_hashmap_put(ps->paths, *ie, p);
 
3019
    }
 
3020
 
 
3021
finish:
 
3022
    /* Assign decibel fixes to elements. */
 
3023
    PA_HASHMAP_FOREACH(db_fix, m->profile_set->decibel_fixes, state) {
 
3024
        pa_alsa_path *p;
 
3025
 
 
3026
        PA_HASHMAP_FOREACH(p, ps->paths, state2) {
 
3027
            pa_alsa_element *e;
 
3028
 
 
3029
            PA_LLIST_FOREACH(e, p->elements) {
 
3030
                if (e->volume_use != PA_ALSA_VOLUME_IGNORE && pa_streq(db_fix->name, e->alsa_name)) {
 
3031
                    /* The profile set that contains the dB fix may be freed
 
3032
                     * before the element, so we have to copy the dB fix
 
3033
                     * object. */
 
3034
                    e->db_fix = pa_xnewdup(pa_alsa_decibel_fix, db_fix, 1);
 
3035
                    e->db_fix->profile_set = NULL;
 
3036
                    e->db_fix->name = pa_xstrdup(db_fix->name);
 
3037
                    e->db_fix->db_values = pa_xmemdup(db_fix->db_values, (db_fix->max_step - db_fix->min_step + 1) * sizeof(long));
 
3038
                }
 
3039
            }
 
3040
        }
 
3041
    }
 
3042
 
 
3043
    return ps;
 
3044
 
 
3045
fail:
 
3046
    if (ps)
 
3047
        pa_alsa_path_set_free(ps);
 
3048
 
 
3049
    return NULL;
 
3050
}
 
3051
 
 
3052
void pa_alsa_path_set_dump(pa_alsa_path_set *ps) {
 
3053
    pa_alsa_path *p;
 
3054
    void *state;
 
3055
    pa_assert(ps);
 
3056
 
 
3057
    pa_log_debug("Path Set %p, direction=%i",
 
3058
                 (void*) ps,
 
3059
                 ps->direction);
 
3060
 
 
3061
    PA_HASHMAP_FOREACH(p, ps->paths, state)
 
3062
        pa_alsa_path_dump(p);
 
3063
}
 
3064
 
 
3065
static bool options_have_option(pa_alsa_option *options, const char *alsa_name) {
 
3066
    pa_alsa_option *o;
 
3067
 
 
3068
    pa_assert(options);
 
3069
    pa_assert(alsa_name);
 
3070
 
 
3071
    PA_LLIST_FOREACH(o, options) {
 
3072
        if (pa_streq(o->alsa_name, alsa_name))
 
3073
            return true;
 
3074
    }
 
3075
    return false;
 
3076
}
 
3077
 
 
3078
static bool enumeration_is_subset(pa_alsa_option *a_options, pa_alsa_option *b_options) {
 
3079
    pa_alsa_option *oa, *ob;
 
3080
 
 
3081
    if (!a_options) return true;
 
3082
    if (!b_options) return false;
 
3083
 
 
3084
    /* If there is an option A offers that B does not, then A is not a subset of B. */
 
3085
    PA_LLIST_FOREACH(oa, a_options) {
 
3086
        bool found = false;
 
3087
        PA_LLIST_FOREACH(ob, b_options) {
 
3088
            if (pa_streq(oa->alsa_name, ob->alsa_name)) {
 
3089
                found = true;
 
3090
                break;
 
3091
            }
 
3092
        }
 
3093
        if (!found)
 
3094
            return false;
 
3095
    }
 
3096
    return true;
 
3097
}
 
3098
 
 
3099
/**
 
3100
 *  Compares two elements to see if a is a subset of b
 
3101
 */
 
3102
static bool element_is_subset(pa_alsa_element *a, pa_alsa_element *b, snd_mixer_t *m) {
 
3103
    pa_assert(a);
 
3104
    pa_assert(b);
 
3105
    pa_assert(m);
 
3106
 
 
3107
    /* General rules:
 
3108
     * Every state is a subset of itself (with caveats for volume_limits and options)
 
3109
     * IGNORE is a subset of every other state */
 
3110
 
 
3111
    /* Check the volume_use */
 
3112
    if (a->volume_use != PA_ALSA_VOLUME_IGNORE) {
 
3113
 
 
3114
        /* "Constant" is subset of "Constant" only when their constant values are equal */
 
3115
        if (a->volume_use == PA_ALSA_VOLUME_CONSTANT && b->volume_use == PA_ALSA_VOLUME_CONSTANT && a->constant_volume != b->constant_volume)
 
3116
            return false;
 
3117
 
 
3118
        /* Different volume uses when b is not "Merge" means we are definitely not a subset */
 
3119
        if (a->volume_use != b->volume_use && b->volume_use != PA_ALSA_VOLUME_MERGE)
 
3120
            return false;
 
3121
 
 
3122
        /* "Constant" is a subset of "Merge", if there is not a "volume-limit" in "Merge" below the actual constant.
 
3123
         * "Zero" and "Off" are just special cases of "Constant" when comparing to "Merge"
 
3124
         * "Merge" with a "volume-limit" is a subset of "Merge" without a "volume-limit" or with a higher "volume-limit" */
 
3125
        if (b->volume_use == PA_ALSA_VOLUME_MERGE && b->volume_limit >= 0) {
 
3126
            long a_limit;
 
3127
 
 
3128
            if (a->volume_use == PA_ALSA_VOLUME_CONSTANT)
 
3129
                a_limit = a->constant_volume;
 
3130
            else if (a->volume_use == PA_ALSA_VOLUME_ZERO) {
 
3131
                long dB = 0;
 
3132
 
 
3133
                if (a->db_fix) {
 
3134
                    int rounding = (a->direction == PA_ALSA_DIRECTION_OUTPUT ? +1 : -1);
 
3135
                    a_limit = decibel_fix_get_step(a->db_fix, &dB, rounding);
 
3136
                } else {
 
3137
                    snd_mixer_selem_id_t *sid;
 
3138
                    snd_mixer_elem_t *me;
 
3139
 
 
3140
                    SELEM_INIT(sid, a->alsa_name);
 
3141
                    if (!(me = snd_mixer_find_selem(m, sid))) {
 
3142
                        pa_log_warn("Element %s seems to have disappeared.", a->alsa_name);
 
3143
                        return false;
 
3144
                    }
 
3145
 
 
3146
                    if (a->direction == PA_ALSA_DIRECTION_OUTPUT) {
 
3147
                        if (snd_mixer_selem_ask_playback_dB_vol(me, dB, +1, &a_limit) < 0)
 
3148
                            return false;
 
3149
                    } else {
 
3150
                        if (snd_mixer_selem_ask_capture_dB_vol(me, dB, -1, &a_limit) < 0)
 
3151
                            return false;
 
3152
                    }
 
3153
                }
 
3154
            } else if (a->volume_use == PA_ALSA_VOLUME_OFF)
 
3155
                a_limit = a->min_volume;
 
3156
            else if (a->volume_use == PA_ALSA_VOLUME_MERGE)
 
3157
                a_limit = a->volume_limit;
 
3158
            else
 
3159
                pa_assert_not_reached();
 
3160
 
 
3161
            if (a_limit > b->volume_limit)
 
3162
                return false;
 
3163
        }
 
3164
 
 
3165
        if (a->volume_use == PA_ALSA_VOLUME_MERGE) {
 
3166
            int s;
 
3167
            /* If override-maps are different, they're not subsets */
 
3168
            if (a->n_channels != b->n_channels)
 
3169
                return false;
 
3170
            for (s = 0; s <= SND_MIXER_SCHN_LAST; s++)
 
3171
                if (a->masks[s][a->n_channels-1] != b->masks[s][b->n_channels-1]) {
 
3172
                    pa_log_debug("Element %s is not a subset - mask a: 0x%" PRIx64 ", mask b: 0x%" PRIx64 ", at channel %d",
 
3173
                        a->alsa_name, a->masks[s][a->n_channels-1], b->masks[s][b->n_channels-1], s);
 
3174
                    return false;
 
3175
               }
 
3176
        }
 
3177
    }
 
3178
 
 
3179
    if (a->switch_use != PA_ALSA_SWITCH_IGNORE) {
 
3180
        /* "On" is a subset of "Mute".
 
3181
         * "Off" is a subset of "Mute".
 
3182
         * "On" is a subset of "Select", if there is an "Option:On" in B.
 
3183
         * "Off" is a subset of "Select", if there is an "Option:Off" in B.
 
3184
         * "Select" is a subset of "Select", if they have the same options (is this always true?). */
 
3185
 
 
3186
        if (a->switch_use != b->switch_use) {
 
3187
 
 
3188
            if (a->switch_use == PA_ALSA_SWITCH_SELECT || a->switch_use == PA_ALSA_SWITCH_MUTE
 
3189
                || b->switch_use == PA_ALSA_SWITCH_OFF || b->switch_use == PA_ALSA_SWITCH_ON)
 
3190
                return false;
 
3191
 
 
3192
            if (b->switch_use == PA_ALSA_SWITCH_SELECT) {
 
3193
                if (a->switch_use == PA_ALSA_SWITCH_ON) {
 
3194
                    if (!options_have_option(b->options, "on"))
 
3195
                        return false;
 
3196
                } else if (a->switch_use == PA_ALSA_SWITCH_OFF) {
 
3197
                    if (!options_have_option(b->options, "off"))
 
3198
                        return false;
 
3199
                }
 
3200
            }
 
3201
        } else if (a->switch_use == PA_ALSA_SWITCH_SELECT) {
 
3202
            if (!enumeration_is_subset(a->options, b->options))
 
3203
                return false;
 
3204
        }
 
3205
    }
 
3206
 
 
3207
    if (a->enumeration_use != PA_ALSA_ENUMERATION_IGNORE) {
 
3208
        if (b->enumeration_use == PA_ALSA_ENUMERATION_IGNORE)
 
3209
            return false;
 
3210
        if (!enumeration_is_subset(a->options, b->options))
 
3211
            return false;
 
3212
    }
 
3213
 
 
3214
    return true;
 
3215
}
 
3216
 
 
3217
static void path_set_condense(pa_alsa_path_set *ps, snd_mixer_t *m) {
 
3218
    pa_alsa_path *p;
 
3219
    void *state;
 
3220
 
 
3221
    pa_assert(ps);
 
3222
    pa_assert(m);
 
3223
 
 
3224
    /* If we only have one path, then don't bother */
 
3225
    if (pa_hashmap_size(ps->paths) < 2)
 
3226
        return;
 
3227
 
 
3228
    PA_HASHMAP_FOREACH(p, ps->paths, state) {
 
3229
        pa_alsa_path *p2;
 
3230
        void *state2;
 
3231
 
 
3232
        PA_HASHMAP_FOREACH(p2, ps->paths, state2) {
 
3233
            pa_alsa_element *ea, *eb;
 
3234
            pa_alsa_jack *ja, *jb;
 
3235
            bool is_subset = true;
 
3236
 
 
3237
            if (p == p2)
 
3238
                continue;
 
3239
 
 
3240
            /* If a has a jack that b does not have, a is not a subset */
 
3241
            PA_LLIST_FOREACH(ja, p->jacks) {
 
3242
                bool exists = false;
 
3243
 
 
3244
                if (!ja->has_control)
 
3245
                    continue;
 
3246
 
 
3247
                PA_LLIST_FOREACH(jb, p2->jacks) {
 
3248
                    if (jb->has_control && pa_streq(jb->alsa_name, ja->alsa_name) &&
 
3249
                       (ja->state_plugged == jb->state_plugged) &&
 
3250
                       (ja->state_unplugged == jb->state_unplugged)) {
 
3251
                        exists = true;
 
3252
                        break;
 
3253
                    }
 
3254
                }
 
3255
 
 
3256
                if (!exists) {
 
3257
                    is_subset = false;
 
3258
                    break;
 
3259
                }
 
3260
            }
 
3261
 
 
3262
            /* Compare the elements of each set... */
 
3263
            PA_LLIST_FOREACH(ea, p->elements) {
 
3264
                bool found_matching_element = false;
 
3265
 
 
3266
                if (!is_subset)
 
3267
                    break;
 
3268
 
 
3269
                PA_LLIST_FOREACH(eb, p2->elements) {
 
3270
                    if (pa_streq(ea->alsa_name, eb->alsa_name)) {
 
3271
                        found_matching_element = true;
 
3272
                        is_subset = element_is_subset(ea, eb, m);
 
3273
                        break;
 
3274
                    }
 
3275
                }
 
3276
 
 
3277
                if (!found_matching_element)
 
3278
                    is_subset = false;
 
3279
            }
 
3280
 
 
3281
            if (is_subset) {
 
3282
                pa_log_debug("Removing path '%s' as it is a subset of '%s'.", p->name, p2->name);
 
3283
                pa_hashmap_remove(ps->paths, p);
 
3284
                break;
 
3285
            }
 
3286
        }
 
3287
    }
 
3288
}
 
3289
 
 
3290
static pa_alsa_path* path_set_find_path_by_description(pa_alsa_path_set *ps, const char* description, pa_alsa_path *ignore) {
 
3291
    pa_alsa_path* p;
 
3292
    void *state;
 
3293
 
 
3294
    PA_HASHMAP_FOREACH(p, ps->paths, state)
 
3295
        if (p != ignore && pa_streq(p->description, description))
 
3296
            return p;
 
3297
 
 
3298
    return NULL;
 
3299
}
 
3300
 
 
3301
static void path_set_make_path_descriptions_unique(pa_alsa_path_set *ps) {
 
3302
    pa_alsa_path *p, *q;
 
3303
    void *state, *state2;
 
3304
 
 
3305
    PA_HASHMAP_FOREACH(p, ps->paths, state) {
 
3306
        unsigned i;
 
3307
        char *old_description;
 
3308
 
 
3309
        q = path_set_find_path_by_description(ps, p->description, p);
 
3310
 
 
3311
        if (!q)
 
3312
            continue;
 
3313
 
 
3314
        old_description = pa_xstrdup(p->description);
 
3315
 
 
3316
        /* OK, this description is not unique, hence let's rename */
 
3317
        i = 1;
 
3318
        PA_HASHMAP_FOREACH(q, ps->paths, state2) {
 
3319
            char *new_description;
 
3320
 
 
3321
            if (!pa_streq(q->description, old_description))
 
3322
                continue;
 
3323
 
 
3324
            new_description = pa_sprintf_malloc("%s %u", q->description, i);
 
3325
            pa_xfree(q->description);
 
3326
            q->description = new_description;
 
3327
 
 
3328
            i++;
 
3329
        }
 
3330
 
 
3331
        pa_xfree(old_description);
 
3332
    }
 
3333
}
 
3334
 
 
3335
static void mapping_free(pa_alsa_mapping *m) {
 
3336
    pa_assert(m);
 
3337
 
 
3338
    pa_xfree(m->name);
 
3339
    pa_xfree(m->description);
 
3340
 
 
3341
    pa_proplist_free(m->proplist);
 
3342
 
 
3343
    pa_xstrfreev(m->device_strings);
 
3344
    pa_xstrfreev(m->input_path_names);
 
3345
    pa_xstrfreev(m->output_path_names);
 
3346
    pa_xstrfreev(m->input_element);
 
3347
    pa_xstrfreev(m->output_element);
 
3348
    if (m->input_path_set)
 
3349
        pa_alsa_path_set_free(m->input_path_set);
 
3350
    if (m->output_path_set)
 
3351
        pa_alsa_path_set_free(m->output_path_set);
 
3352
 
 
3353
    pa_assert(!m->input_pcm);
 
3354
    pa_assert(!m->output_pcm);
 
3355
 
 
3356
    pa_alsa_ucm_mapping_context_free(&m->ucm_context);
 
3357
 
 
3358
    pa_xfree(m);
 
3359
}
 
3360
 
 
3361
static void profile_free(pa_alsa_profile *p) {
 
3362
    pa_assert(p);
 
3363
 
 
3364
    pa_xfree(p->name);
 
3365
    pa_xfree(p->description);
 
3366
 
 
3367
    pa_xstrfreev(p->input_mapping_names);
 
3368
    pa_xstrfreev(p->output_mapping_names);
 
3369
 
 
3370
    if (p->input_mappings)
 
3371
        pa_idxset_free(p->input_mappings, NULL);
 
3372
 
 
3373
    if (p->output_mappings)
 
3374
        pa_idxset_free(p->output_mappings, NULL);
 
3375
 
 
3376
    pa_xfree(p);
 
3377
}
 
3378
 
 
3379
void pa_alsa_profile_set_free(pa_alsa_profile_set *ps) {
 
3380
    pa_assert(ps);
 
3381
 
 
3382
    if (ps->input_paths)
 
3383
        pa_hashmap_free(ps->input_paths);
 
3384
 
 
3385
    if (ps->output_paths)
 
3386
        pa_hashmap_free(ps->output_paths);
 
3387
 
 
3388
    if (ps->profiles)
 
3389
        pa_hashmap_free(ps->profiles);
 
3390
 
 
3391
    if (ps->mappings)
 
3392
        pa_hashmap_free(ps->mappings);
 
3393
 
 
3394
    if (ps->decibel_fixes)
 
3395
        pa_hashmap_free(ps->decibel_fixes);
 
3396
 
 
3397
    pa_xfree(ps);
 
3398
}
 
3399
 
 
3400
pa_alsa_mapping *pa_alsa_mapping_get(pa_alsa_profile_set *ps, const char *name) {
 
3401
    pa_alsa_mapping *m;
 
3402
 
 
3403
    if (!pa_startswith(name, "Mapping "))
 
3404
        return NULL;
 
3405
 
 
3406
    name += 8;
 
3407
 
 
3408
    if ((m = pa_hashmap_get(ps->mappings, name)))
 
3409
        return m;
 
3410
 
 
3411
    m = pa_xnew0(pa_alsa_mapping, 1);
 
3412
    m->profile_set = ps;
 
3413
    m->exact_channels = true;
 
3414
    m->name = pa_xstrdup(name);
 
3415
    pa_sample_spec_init(&m->sample_spec);
 
3416
    pa_channel_map_init(&m->channel_map);
 
3417
    m->proplist = pa_proplist_new();
 
3418
 
 
3419
    pa_hashmap_put(ps->mappings, m->name, m);
 
3420
 
 
3421
    return m;
 
3422
}
 
3423
 
 
3424
static pa_alsa_profile *profile_get(pa_alsa_profile_set *ps, const char *name) {
 
3425
    pa_alsa_profile *p;
 
3426
 
 
3427
    if (!pa_startswith(name, "Profile "))
 
3428
        return NULL;
 
3429
 
 
3430
    name += 8;
 
3431
 
 
3432
    if ((p = pa_hashmap_get(ps->profiles, name)))
 
3433
        return p;
 
3434
 
 
3435
    p = pa_xnew0(pa_alsa_profile, 1);
 
3436
    p->profile_set = ps;
 
3437
    p->name = pa_xstrdup(name);
 
3438
 
 
3439
    pa_hashmap_put(ps->profiles, p->name, p);
 
3440
 
 
3441
    return p;
 
3442
}
 
3443
 
 
3444
static pa_alsa_decibel_fix *decibel_fix_get(pa_alsa_profile_set *ps, const char *name) {
 
3445
    pa_alsa_decibel_fix *db_fix;
 
3446
 
 
3447
    if (!pa_startswith(name, "DecibelFix "))
 
3448
        return NULL;
 
3449
 
 
3450
    name += 11;
 
3451
 
 
3452
    if ((db_fix = pa_hashmap_get(ps->decibel_fixes, name)))
 
3453
        return db_fix;
 
3454
 
 
3455
    db_fix = pa_xnew0(pa_alsa_decibel_fix, 1);
 
3456
    db_fix->profile_set = ps;
 
3457
    db_fix->name = pa_xstrdup(name);
 
3458
 
 
3459
    pa_hashmap_put(ps->decibel_fixes, db_fix->name, db_fix);
 
3460
 
 
3461
    return db_fix;
 
3462
}
 
3463
 
 
3464
static int mapping_parse_device_strings(pa_config_parser_state *state) {
 
3465
    pa_alsa_profile_set *ps;
 
3466
    pa_alsa_mapping *m;
 
3467
 
 
3468
    pa_assert(state);
 
3469
 
 
3470
    ps = state->userdata;
 
3471
 
 
3472
    if (!(m = pa_alsa_mapping_get(ps, state->section))) {
 
3473
        pa_log("[%s:%u] %s invalid in section %s", state->filename, state->lineno, state->lvalue, state->section);
 
3474
        return -1;
 
3475
    }
 
3476
 
 
3477
    pa_xstrfreev(m->device_strings);
 
3478
    if (!(m->device_strings = pa_split_spaces_strv(state->rvalue))) {
 
3479
        pa_log("[%s:%u] Device string list empty of '%s'", state->filename, state->lineno, state->section);
 
3480
        return -1;
 
3481
    }
 
3482
 
 
3483
    return 0;
 
3484
}
 
3485
 
 
3486
static int mapping_parse_channel_map(pa_config_parser_state *state) {
 
3487
    pa_alsa_profile_set *ps;
 
3488
    pa_alsa_mapping *m;
 
3489
 
 
3490
    pa_assert(state);
 
3491
 
 
3492
    ps = state->userdata;
 
3493
 
 
3494
    if (!(m = pa_alsa_mapping_get(ps, state->section))) {
 
3495
        pa_log("[%s:%u] %s invalid in section %s", state->filename, state->lineno, state->lvalue, state->section);
 
3496
        return -1;
 
3497
    }
 
3498
 
 
3499
    if (!(pa_channel_map_parse(&m->channel_map, state->rvalue))) {
 
3500
        pa_log("[%s:%u] Channel map invalid of '%s'", state->filename, state->lineno, state->section);
 
3501
        return -1;
 
3502
    }
 
3503
 
 
3504
    return 0;
 
3505
}
 
3506
 
 
3507
static int mapping_parse_paths(pa_config_parser_state *state) {
 
3508
    pa_alsa_profile_set *ps;
 
3509
    pa_alsa_mapping *m;
 
3510
 
 
3511
    pa_assert(state);
 
3512
 
 
3513
    ps = state->userdata;
 
3514
 
 
3515
    if (!(m = pa_alsa_mapping_get(ps, state->section))) {
 
3516
        pa_log("[%s:%u] %s invalid in section %s", state->filename, state->lineno, state->lvalue, state->section);
 
3517
        return -1;
 
3518
    }
 
3519
 
 
3520
    if (pa_streq(state->lvalue, "paths-input")) {
 
3521
        pa_xstrfreev(m->input_path_names);
 
3522
        m->input_path_names = pa_split_spaces_strv(state->rvalue);
 
3523
    } else {
 
3524
        pa_xstrfreev(m->output_path_names);
 
3525
        m->output_path_names = pa_split_spaces_strv(state->rvalue);
 
3526
    }
 
3527
 
 
3528
    return 0;
 
3529
}
 
3530
 
 
3531
static int mapping_parse_exact_channels(pa_config_parser_state *state) {
 
3532
    pa_alsa_profile_set *ps;
 
3533
    pa_alsa_mapping *m;
 
3534
    int b;
 
3535
 
 
3536
    pa_assert(state);
 
3537
 
 
3538
    ps = state->userdata;
 
3539
 
 
3540
    if (!(m = pa_alsa_mapping_get(ps, state->section))) {
 
3541
        pa_log("[%s:%u] %s invalid in section %s", state->filename, state->lineno, state->lvalue, state->section);
 
3542
        return -1;
 
3543
    }
 
3544
 
 
3545
    if ((b = pa_parse_boolean(state->rvalue)) < 0) {
 
3546
        pa_log("[%s:%u] %s has invalid value '%s'", state->filename, state->lineno, state->lvalue, state->section);
 
3547
        return -1;
 
3548
    }
 
3549
 
 
3550
    m->exact_channels = b;
 
3551
 
 
3552
    return 0;
 
3553
}
 
3554
 
 
3555
static int mapping_parse_element(pa_config_parser_state *state) {
 
3556
    pa_alsa_profile_set *ps;
 
3557
    pa_alsa_mapping *m;
 
3558
 
 
3559
    pa_assert(state);
 
3560
 
 
3561
    ps = state->userdata;
 
3562
 
 
3563
    if (!(m = pa_alsa_mapping_get(ps, state->section))) {
 
3564
        pa_log("[%s:%u] %s invalid in section %s", state->filename, state->lineno, state->lvalue, state->section);
 
3565
        return -1;
 
3566
    }
 
3567
 
 
3568
    if (pa_streq(state->lvalue, "element-input")) {
 
3569
        pa_xstrfreev(m->input_element);
 
3570
        m->input_element = pa_split_spaces_strv(state->rvalue);
 
3571
    } else {
 
3572
        pa_xstrfreev(m->output_element);
 
3573
        m->output_element = pa_split_spaces_strv(state->rvalue);
 
3574
    }
 
3575
 
 
3576
    return 0;
 
3577
}
 
3578
 
 
3579
static int mapping_parse_direction(pa_config_parser_state *state) {
 
3580
    pa_alsa_profile_set *ps;
 
3581
    pa_alsa_mapping *m;
 
3582
 
 
3583
    pa_assert(state);
 
3584
 
 
3585
    ps = state->userdata;
 
3586
 
 
3587
    if (!(m = pa_alsa_mapping_get(ps, state->section))) {
 
3588
        pa_log("[%s:%u] Section name %s invalid.", state->filename, state->lineno, state->section);
 
3589
        return -1;
 
3590
    }
 
3591
 
 
3592
    if (pa_streq(state->rvalue, "input"))
 
3593
        m->direction = PA_ALSA_DIRECTION_INPUT;
 
3594
    else if (pa_streq(state->rvalue, "output"))
 
3595
        m->direction = PA_ALSA_DIRECTION_OUTPUT;
 
3596
    else if (pa_streq(state->rvalue, "any"))
 
3597
        m->direction = PA_ALSA_DIRECTION_ANY;
 
3598
    else {
 
3599
        pa_log("[%s:%u] Direction %s invalid.", state->filename, state->lineno, state->rvalue);
 
3600
        return -1;
 
3601
    }
 
3602
 
 
3603
    return 0;
 
3604
}
 
3605
 
 
3606
static int mapping_parse_description(pa_config_parser_state *state) {
 
3607
    pa_alsa_profile_set *ps;
 
3608
    pa_alsa_profile *p;
 
3609
    pa_alsa_mapping *m;
 
3610
 
 
3611
    pa_assert(state);
 
3612
 
 
3613
    ps = state->userdata;
 
3614
 
 
3615
    if ((m = pa_alsa_mapping_get(ps, state->section))) {
 
3616
        pa_xfree(m->description);
 
3617
        m->description = pa_xstrdup(state->rvalue);
 
3618
    } else if ((p = profile_get(ps, state->section))) {
 
3619
        pa_xfree(p->description);
 
3620
        p->description = pa_xstrdup(state->rvalue);
 
3621
    } else {
 
3622
        pa_log("[%s:%u] Section name %s invalid.", state->filename, state->lineno, state->section);
 
3623
        return -1;
 
3624
    }
 
3625
 
 
3626
    return 0;
 
3627
}
 
3628
 
 
3629
static int mapping_parse_priority(pa_config_parser_state *state) {
 
3630
    pa_alsa_profile_set *ps;
 
3631
    pa_alsa_profile *p;
 
3632
    pa_alsa_mapping *m;
 
3633
    uint32_t prio;
 
3634
 
 
3635
    pa_assert(state);
 
3636
 
 
3637
    ps = state->userdata;
 
3638
 
 
3639
    if (pa_atou(state->rvalue, &prio) < 0) {
 
3640
        pa_log("[%s:%u] Priority invalid of '%s'", state->filename, state->lineno, state->section);
 
3641
        return -1;
 
3642
    }
 
3643
 
 
3644
    if ((m = pa_alsa_mapping_get(ps, state->section)))
 
3645
        m->priority = prio;
 
3646
    else if ((p = profile_get(ps, state->section)))
 
3647
        p->priority = prio;
 
3648
    else {
 
3649
        pa_log("[%s:%u] Section name %s invalid.", state->filename, state->lineno, state->section);
 
3650
        return -1;
 
3651
    }
 
3652
 
 
3653
    return 0;
 
3654
}
 
3655
 
 
3656
static int mapping_parse_fallback(pa_config_parser_state *state) {
 
3657
    pa_alsa_profile_set *ps;
 
3658
    pa_alsa_profile *p;
 
3659
    pa_alsa_mapping *m;
 
3660
    int k;
 
3661
 
 
3662
    pa_assert(state);
 
3663
 
 
3664
    ps = state->userdata;
 
3665
 
 
3666
    if ((k = pa_parse_boolean(state->rvalue)) < 0) {
 
3667
        pa_log("[%s:%u] Fallback invalid of '%s'", state->filename, state->lineno, state->section);
 
3668
        return -1;
 
3669
    }
 
3670
 
 
3671
    if ((m = pa_alsa_mapping_get(ps, state->section)))
 
3672
        m->fallback = k;
 
3673
    else if ((p = profile_get(ps, state->section)))
 
3674
        p->fallback_input = p->fallback_output = k;
 
3675
    else {
 
3676
        pa_log("[%s:%u] Section name %s invalid.", state->filename, state->lineno, state->section);
 
3677
        return -1;
 
3678
    }
 
3679
 
 
3680
    return 0;
 
3681
}
 
3682
 
 
3683
 
 
3684
static int profile_parse_mappings(pa_config_parser_state *state) {
 
3685
    pa_alsa_profile_set *ps;
 
3686
    pa_alsa_profile *p;
 
3687
 
 
3688
    pa_assert(state);
 
3689
 
 
3690
    ps = state->userdata;
 
3691
 
 
3692
    if (!(p = profile_get(ps, state->section))) {
 
3693
        pa_log("[%s:%u] %s invalid in section %s", state->filename, state->lineno, state->lvalue, state->section);
 
3694
        return -1;
 
3695
    }
 
3696
 
 
3697
    if (pa_streq(state->lvalue, "input-mappings")) {
 
3698
        pa_xstrfreev(p->input_mapping_names);
 
3699
        p->input_mapping_names = pa_split_spaces_strv(state->rvalue);
 
3700
    } else {
 
3701
        pa_xstrfreev(p->output_mapping_names);
 
3702
        p->output_mapping_names = pa_split_spaces_strv(state->rvalue);
 
3703
    }
 
3704
 
 
3705
    return 0;
 
3706
}
 
3707
 
 
3708
static int profile_parse_skip_probe(pa_config_parser_state *state) {
 
3709
    pa_alsa_profile_set *ps;
 
3710
    pa_alsa_profile *p;
 
3711
    int b;
 
3712
 
 
3713
    pa_assert(state);
 
3714
 
 
3715
    ps = state->userdata;
 
3716
 
 
3717
    if (!(p = profile_get(ps, state->section))) {
 
3718
        pa_log("[%s:%u] %s invalid in section %s", state->filename, state->lineno, state->lvalue, state->section);
 
3719
        return -1;
 
3720
    }
 
3721
 
 
3722
    if ((b = pa_parse_boolean(state->rvalue)) < 0) {
 
3723
        pa_log("[%s:%u] Skip probe invalid of '%s'", state->filename, state->lineno, state->section);
 
3724
        return -1;
 
3725
    }
 
3726
 
 
3727
    p->supported = b;
 
3728
 
 
3729
    return 0;
 
3730
}
 
3731
 
 
3732
static int decibel_fix_parse_db_values(pa_config_parser_state *state) {
 
3733
    pa_alsa_profile_set *ps;
 
3734
    pa_alsa_decibel_fix *db_fix;
 
3735
    char **items;
 
3736
    char *item;
 
3737
    long *db_values;
 
3738
    unsigned n = 8; /* Current size of the db_values table. */
 
3739
    unsigned min_step = 0;
 
3740
    unsigned max_step = 0;
 
3741
    unsigned i = 0; /* Index to the items table. */
 
3742
    unsigned prev_step = 0;
 
3743
    double prev_db = 0;
 
3744
 
 
3745
    pa_assert(state);
 
3746
 
 
3747
    ps = state->userdata;
 
3748
 
 
3749
    if (!(db_fix = decibel_fix_get(ps, state->section))) {
 
3750
        pa_log("[%s:%u] %s invalid in section %s", state->filename, state->lineno, state->lvalue, state->section);
 
3751
        return -1;
 
3752
    }
 
3753
 
 
3754
    if (!(items = pa_split_spaces_strv(state->rvalue))) {
 
3755
        pa_log("[%s:%u] Value missing", state->filename, state->lineno);
 
3756
        return -1;
 
3757
    }
 
3758
 
 
3759
    db_values = pa_xnew(long, n);
 
3760
 
 
3761
    while ((item = items[i++])) {
 
3762
        char *s = item; /* Step value string. */
 
3763
        char *d = item; /* dB value string. */
 
3764
        uint32_t step;
 
3765
        double db;
 
3766
 
 
3767
        /* Move d forward until it points to a colon or to the end of the item. */
 
3768
        for (; *d && *d != ':'; ++d);
 
3769
 
 
3770
        if (d == s) {
 
3771
            /* item started with colon. */
 
3772
            pa_log("[%s:%u] No step value found in %s", state->filename, state->lineno, item);
 
3773
            goto fail;
 
3774
        }
 
3775
 
 
3776
        if (!*d || !*(d + 1)) {
 
3777
            /* No colon found, or it was the last character in item. */
 
3778
            pa_log("[%s:%u] No dB value found in %s", state->filename, state->lineno, item);
 
3779
            goto fail;
 
3780
        }
 
3781
 
 
3782
        /* pa_atou() needs a null-terminating string. Let's replace the colon
 
3783
         * with a zero byte. */
 
3784
        *d++ = '\0';
 
3785
 
 
3786
        if (pa_atou(s, &step) < 0) {
 
3787
            pa_log("[%s:%u] Invalid step value: %s", state->filename, state->lineno, s);
 
3788
            goto fail;
 
3789
        }
 
3790
 
 
3791
        if (pa_atod(d, &db) < 0) {
 
3792
            pa_log("[%s:%u] Invalid dB value: %s", state->filename, state->lineno, d);
 
3793
            goto fail;
 
3794
        }
 
3795
 
 
3796
        if (step <= prev_step && i != 1) {
 
3797
            pa_log("[%s:%u] Step value %u not greater than the previous value %u", state->filename, state->lineno, step, prev_step);
 
3798
            goto fail;
 
3799
        }
 
3800
 
 
3801
        if (db < prev_db && i != 1) {
 
3802
            pa_log("[%s:%u] Decibel value %0.2f less than the previous value %0.2f", state->filename, state->lineno, db, prev_db);
 
3803
            goto fail;
 
3804
        }
 
3805
 
 
3806
        if (i == 1) {
 
3807
            min_step = step;
 
3808
            db_values[0] = (long) (db * 100.0);
 
3809
            prev_step = step;
 
3810
            prev_db = db;
 
3811
        } else {
 
3812
            /* Interpolate linearly. */
 
3813
            double db_increment = (db - prev_db) / (step - prev_step);
 
3814
 
 
3815
            for (; prev_step < step; ++prev_step, prev_db += db_increment) {
 
3816
 
 
3817
                /* Reallocate the db_values table if it's about to overflow. */
 
3818
                if (prev_step + 1 - min_step == n) {
 
3819
                    n *= 2;
 
3820
                    db_values = pa_xrenew(long, db_values, n);
 
3821
                }
 
3822
 
 
3823
                db_values[prev_step + 1 - min_step] = (long) ((prev_db + db_increment) * 100.0);
 
3824
            }
 
3825
        }
 
3826
 
 
3827
        max_step = step;
 
3828
    }
 
3829
 
 
3830
    db_fix->min_step = min_step;
 
3831
    db_fix->max_step = max_step;
 
3832
    pa_xfree(db_fix->db_values);
 
3833
    db_fix->db_values = db_values;
 
3834
 
 
3835
    pa_xstrfreev(items);
 
3836
 
 
3837
    return 0;
 
3838
 
 
3839
fail:
 
3840
    pa_xstrfreev(items);
 
3841
    pa_xfree(db_values);
 
3842
 
 
3843
    return -1;
 
3844
}
 
3845
 
 
3846
static void mapping_paths_probe(pa_alsa_mapping *m, pa_alsa_profile *profile,
 
3847
                                pa_alsa_direction_t direction, pa_hashmap *used_paths) {
 
3848
 
 
3849
    pa_alsa_path *p;
 
3850
    void *state;
 
3851
    snd_pcm_t *pcm_handle;
 
3852
    pa_alsa_path_set *ps;
 
3853
    snd_mixer_t *mixer_handle;
 
3854
 
 
3855
    if (direction == PA_ALSA_DIRECTION_OUTPUT) {
 
3856
        if (m->output_path_set)
 
3857
            return; /* Already probed */
 
3858
        m->output_path_set = ps = pa_alsa_path_set_new(m, direction, NULL); /* FIXME: Handle paths_dir */
 
3859
        pcm_handle = m->output_pcm;
 
3860
    } else {
 
3861
        if (m->input_path_set)
 
3862
            return; /* Already probed */
 
3863
        m->input_path_set = ps = pa_alsa_path_set_new(m, direction, NULL); /* FIXME: Handle paths_dir */
 
3864
        pcm_handle = m->input_pcm;
 
3865
    }
 
3866
 
 
3867
    if (!ps)
 
3868
        return; /* No paths */
 
3869
 
 
3870
    pa_assert(pcm_handle);
 
3871
 
 
3872
    mixer_handle = pa_alsa_open_mixer_for_pcm(pcm_handle, NULL);
 
3873
    if (!mixer_handle) {
 
3874
        /* Cannot open mixer, remove all entries */
 
3875
        pa_hashmap_remove_all(ps->paths);
 
3876
        return;
 
3877
    }
 
3878
 
 
3879
    PA_HASHMAP_FOREACH(p, ps->paths, state) {
 
3880
        if (pa_alsa_path_probe(p, mixer_handle, m->profile_set->ignore_dB) < 0) {
 
3881
            pa_hashmap_remove(ps->paths, p);
 
3882
        }
 
3883
    }
 
3884
 
 
3885
    path_set_condense(ps, mixer_handle);
 
3886
    path_set_make_path_descriptions_unique(ps);
 
3887
 
 
3888
    if (mixer_handle)
 
3889
        snd_mixer_close(mixer_handle);
 
3890
 
 
3891
    PA_HASHMAP_FOREACH(p, ps->paths, state)
 
3892
        pa_hashmap_put(used_paths, p, p);
 
3893
 
 
3894
    pa_log_debug("Available mixer paths (after tidying):");
 
3895
    pa_alsa_path_set_dump(ps);
 
3896
}
 
3897
 
 
3898
static int mapping_verify(pa_alsa_mapping *m, const pa_channel_map *bonus) {
 
3899
 
 
3900
    static const struct description_map well_known_descriptions[] = {
 
3901
        { "analog-mono",            N_("Analog Mono") },
 
3902
        { "analog-stereo",          N_("Analog Stereo") },
 
3903
        { "multichannel",           N_("Multichannel") },
 
3904
        { "analog-surround-21",     N_("Analog Surround 2.1") },
 
3905
        { "analog-surround-30",     N_("Analog Surround 3.0") },
 
3906
        { "analog-surround-31",     N_("Analog Surround 3.1") },
 
3907
        { "analog-surround-40",     N_("Analog Surround 4.0") },
 
3908
        { "analog-surround-41",     N_("Analog Surround 4.1") },
 
3909
        { "analog-surround-50",     N_("Analog Surround 5.0") },
 
3910
        { "analog-surround-51",     N_("Analog Surround 5.1") },
 
3911
        { "analog-surround-61",     N_("Analog Surround 6.0") },
 
3912
        { "analog-surround-61",     N_("Analog Surround 6.1") },
 
3913
        { "analog-surround-70",     N_("Analog Surround 7.0") },
 
3914
        { "analog-surround-71",     N_("Analog Surround 7.1") },
 
3915
        { "iec958-stereo",          N_("Digital Stereo (IEC958)") },
 
3916
        { "iec958-passthrough",     N_("Digital Passthrough  (IEC958)") },
 
3917
        { "iec958-ac3-surround-40", N_("Digital Surround 4.0 (IEC958/AC3)") },
 
3918
        { "iec958-ac3-surround-51", N_("Digital Surround 5.1 (IEC958/AC3)") },
 
3919
        { "iec958-dts-surround-51", N_("Digital Surround 5.1 (IEC958/DTS)") },
 
3920
        { "hdmi-stereo",            N_("Digital Stereo (HDMI)") },
 
3921
        { "hdmi-surround-51",       N_("Digital Surround 5.1 (HDMI)") }
 
3922
    };
 
3923
 
 
3924
    pa_assert(m);
 
3925
 
 
3926
    if (!pa_channel_map_valid(&m->channel_map)) {
 
3927
        pa_log("Mapping %s is missing channel map.", m->name);
 
3928
        return -1;
 
3929
    }
 
3930
 
 
3931
    if (!m->device_strings) {
 
3932
        pa_log("Mapping %s is missing device strings.", m->name);
 
3933
        return -1;
 
3934
    }
 
3935
 
 
3936
    if ((m->input_path_names && m->input_element) ||
 
3937
        (m->output_path_names && m->output_element)) {
 
3938
        pa_log("Mapping %s must have either mixer path or mixer element, not both.", m->name);
 
3939
        return -1;
 
3940
    }
 
3941
 
 
3942
    if (!m->description)
 
3943
        m->description = pa_xstrdup(lookup_description(m->name,
 
3944
                                                       well_known_descriptions,
 
3945
                                                       PA_ELEMENTSOF(well_known_descriptions)));
 
3946
 
 
3947
    if (!m->description)
 
3948
        m->description = pa_xstrdup(m->name);
 
3949
 
 
3950
    if (bonus) {
 
3951
        if (pa_channel_map_equal(&m->channel_map, bonus))
 
3952
            m->priority += 50;
 
3953
        else if (m->channel_map.channels == bonus->channels)
 
3954
            m->priority += 30;
 
3955
    }
 
3956
 
 
3957
    return 0;
 
3958
}
 
3959
 
 
3960
void pa_alsa_mapping_dump(pa_alsa_mapping *m) {
 
3961
    char cm[PA_CHANNEL_MAP_SNPRINT_MAX];
 
3962
 
 
3963
    pa_assert(m);
 
3964
 
 
3965
    pa_log_debug("Mapping %s (%s), priority=%u, channel_map=%s, supported=%s, direction=%i",
 
3966
                 m->name,
 
3967
                 pa_strnull(m->description),
 
3968
                 m->priority,
 
3969
                 pa_channel_map_snprint(cm, sizeof(cm), &m->channel_map),
 
3970
                 pa_yes_no(m->supported),
 
3971
                 m->direction);
 
3972
}
 
3973
 
 
3974
static void profile_set_add_auto_pair(
 
3975
        pa_alsa_profile_set *ps,
 
3976
        pa_alsa_mapping *m, /* output */
 
3977
        pa_alsa_mapping *n  /* input */) {
 
3978
 
 
3979
    char *name;
 
3980
    pa_alsa_profile *p;
 
3981
 
 
3982
    pa_assert(ps);
 
3983
    pa_assert(m || n);
 
3984
 
 
3985
    if (m && m->direction == PA_ALSA_DIRECTION_INPUT)
 
3986
        return;
 
3987
 
 
3988
    if (n && n->direction == PA_ALSA_DIRECTION_OUTPUT)
 
3989
        return;
 
3990
 
 
3991
    if (m && n)
 
3992
        name = pa_sprintf_malloc("output:%s+input:%s", m->name, n->name);
 
3993
    else if (m)
 
3994
        name = pa_sprintf_malloc("output:%s", m->name);
 
3995
    else
 
3996
        name = pa_sprintf_malloc("input:%s", n->name);
 
3997
 
 
3998
    if (pa_hashmap_get(ps->profiles, name)) {
 
3999
        pa_xfree(name);
 
4000
        return;
 
4001
    }
 
4002
 
 
4003
    p = pa_xnew0(pa_alsa_profile, 1);
 
4004
    p->profile_set = ps;
 
4005
    p->name = name;
 
4006
 
 
4007
    if (m) {
 
4008
        p->output_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
 
4009
        pa_idxset_put(p->output_mappings, m, NULL);
 
4010
        p->priority += m->priority * 100;
 
4011
        p->fallback_output = m->fallback;
 
4012
    }
 
4013
 
 
4014
    if (n) {
 
4015
        p->input_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
 
4016
        pa_idxset_put(p->input_mappings, n, NULL);
 
4017
        p->priority += n->priority;
 
4018
        p->fallback_input = n->fallback;
 
4019
    }
 
4020
 
 
4021
    pa_hashmap_put(ps->profiles, p->name, p);
 
4022
}
 
4023
 
 
4024
static void profile_set_add_auto(pa_alsa_profile_set *ps) {
 
4025
    pa_alsa_mapping *m, *n;
 
4026
    void *m_state, *n_state;
 
4027
 
 
4028
    pa_assert(ps);
 
4029
 
 
4030
    /* The order is important here:
 
4031
       1) try single inputs and outputs before trying their
 
4032
          combination, because if the half-duplex test failed, we don't have
 
4033
          to try full duplex.
 
4034
       2) try the output right before the input combinations with
 
4035
          that output, because then the output_pcm is not closed between tests.
 
4036
    */
 
4037
    PA_HASHMAP_FOREACH(n, ps->mappings, n_state)
 
4038
        profile_set_add_auto_pair(ps, NULL, n);
 
4039
 
 
4040
    PA_HASHMAP_FOREACH(m, ps->mappings, m_state) {
 
4041
        profile_set_add_auto_pair(ps, m, NULL);
 
4042
 
 
4043
        PA_HASHMAP_FOREACH(n, ps->mappings, n_state)
 
4044
            profile_set_add_auto_pair(ps, m, n);
 
4045
    }
 
4046
 
 
4047
}
 
4048
 
 
4049
static int profile_verify(pa_alsa_profile *p) {
 
4050
 
 
4051
    static const struct description_map well_known_descriptions[] = {
 
4052
        { "output:analog-mono+input:analog-mono",     N_("Analog Mono Duplex") },
 
4053
        { "output:analog-stereo+input:analog-stereo", N_("Analog Stereo Duplex") },
 
4054
        { "output:iec958-stereo+input:iec958-stereo", N_("Digital Stereo Duplex (IEC958)") },
 
4055
        { "off",                                      N_("Off") }
 
4056
    };
 
4057
 
 
4058
    pa_assert(p);
 
4059
 
 
4060
    /* Replace the output mapping names by the actual mappings */
 
4061
    if (p->output_mapping_names) {
 
4062
        char **name;
 
4063
 
 
4064
        pa_assert(!p->output_mappings);
 
4065
        p->output_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
 
4066
 
 
4067
        for (name = p->output_mapping_names; *name; name++) {
 
4068
            pa_alsa_mapping *m;
 
4069
            char **in;
 
4070
            bool duplicate = false;
 
4071
 
 
4072
            for (in = name + 1; *in; in++)
 
4073
                if (pa_streq(*name, *in)) {
 
4074
                    duplicate = true;
 
4075
                    break;
 
4076
                }
 
4077
 
 
4078
            if (duplicate)
 
4079
                continue;
 
4080
 
 
4081
            if (!(m = pa_hashmap_get(p->profile_set->mappings, *name)) || m->direction == PA_ALSA_DIRECTION_INPUT) {
 
4082
                pa_log("Profile '%s' refers to nonexistent mapping '%s'.", p->name, *name);
 
4083
                return -1;
 
4084
            }
 
4085
 
 
4086
            pa_idxset_put(p->output_mappings, m, NULL);
 
4087
 
 
4088
            if (p->supported)
 
4089
                m->supported++;
 
4090
        }
 
4091
 
 
4092
        pa_xstrfreev(p->output_mapping_names);
 
4093
        p->output_mapping_names = NULL;
 
4094
    }
 
4095
 
 
4096
    /* Replace the input mapping names by the actual mappings */
 
4097
    if (p->input_mapping_names) {
 
4098
        char **name;
 
4099
 
 
4100
        pa_assert(!p->input_mappings);
 
4101
        p->input_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
 
4102
 
 
4103
        for (name = p->input_mapping_names; *name; name++) {
 
4104
            pa_alsa_mapping *m;
 
4105
            char **in;
 
4106
            bool duplicate = false;
 
4107
 
 
4108
            for (in = name + 1; *in; in++)
 
4109
                if (pa_streq(*name, *in)) {
 
4110
                    duplicate = true;
 
4111
                    break;
 
4112
                }
 
4113
 
 
4114
            if (duplicate)
 
4115
                continue;
 
4116
 
 
4117
            if (!(m = pa_hashmap_get(p->profile_set->mappings, *name)) || m->direction == PA_ALSA_DIRECTION_OUTPUT) {
 
4118
                pa_log("Profile '%s' refers to nonexistent mapping '%s'.", p->name, *name);
 
4119
                return -1;
 
4120
            }
 
4121
 
 
4122
            pa_idxset_put(p->input_mappings, m, NULL);
 
4123
 
 
4124
            if (p->supported)
 
4125
                m->supported++;
 
4126
        }
 
4127
 
 
4128
        pa_xstrfreev(p->input_mapping_names);
 
4129
        p->input_mapping_names = NULL;
 
4130
    }
 
4131
 
 
4132
    if (!p->input_mappings && !p->output_mappings) {
 
4133
        pa_log("Profile '%s' lacks mappings.", p->name);
 
4134
        return -1;
 
4135
    }
 
4136
 
 
4137
    if (!p->description)
 
4138
        p->description = pa_xstrdup(lookup_description(p->name,
 
4139
                                                       well_known_descriptions,
 
4140
                                                       PA_ELEMENTSOF(well_known_descriptions)));
 
4141
 
 
4142
    if (!p->description) {
 
4143
        pa_strbuf *sb;
 
4144
        uint32_t idx;
 
4145
        pa_alsa_mapping *m;
 
4146
 
 
4147
        sb = pa_strbuf_new();
 
4148
 
 
4149
        if (p->output_mappings)
 
4150
            PA_IDXSET_FOREACH(m, p->output_mappings, idx) {
 
4151
                if (!pa_strbuf_isempty(sb))
 
4152
                    pa_strbuf_puts(sb, " + ");
 
4153
 
 
4154
                pa_strbuf_printf(sb, _("%s Output"), m->description);
 
4155
            }
 
4156
 
 
4157
        if (p->input_mappings)
 
4158
            PA_IDXSET_FOREACH(m, p->input_mappings, idx) {
 
4159
                if (!pa_strbuf_isempty(sb))
 
4160
                    pa_strbuf_puts(sb, " + ");
 
4161
 
 
4162
                pa_strbuf_printf(sb, _("%s Input"), m->description);
 
4163
            }
 
4164
 
 
4165
        p->description = pa_strbuf_tostring_free(sb);
 
4166
    }
 
4167
 
 
4168
    return 0;
 
4169
}
 
4170
 
 
4171
void pa_alsa_profile_dump(pa_alsa_profile *p) {
 
4172
    uint32_t idx;
 
4173
    pa_alsa_mapping *m;
 
4174
    pa_assert(p);
 
4175
 
 
4176
    pa_log_debug("Profile %s (%s), priority=%u, supported=%s n_input_mappings=%u, n_output_mappings=%u",
 
4177
                 p->name,
 
4178
                 pa_strnull(p->description),
 
4179
                 p->priority,
 
4180
                 pa_yes_no(p->supported),
 
4181
                 p->input_mappings ? pa_idxset_size(p->input_mappings) : 0,
 
4182
                 p->output_mappings ? pa_idxset_size(p->output_mappings) : 0);
 
4183
 
 
4184
    if (p->input_mappings)
 
4185
        PA_IDXSET_FOREACH(m, p->input_mappings, idx)
 
4186
            pa_log_debug("Input %s", m->name);
 
4187
 
 
4188
    if (p->output_mappings)
 
4189
        PA_IDXSET_FOREACH(m, p->output_mappings, idx)
 
4190
            pa_log_debug("Output %s", m->name);
 
4191
}
 
4192
 
 
4193
static int decibel_fix_verify(pa_alsa_decibel_fix *db_fix) {
 
4194
    pa_assert(db_fix);
 
4195
 
 
4196
    /* Check that the dB mapping has been configured. Since "db-values" is
 
4197
     * currently the only option in the DecibelFix section, and decibel fix
 
4198
     * objects don't get created if a DecibelFix section is empty, this is
 
4199
     * actually a redundant check. Having this may prevent future bugs,
 
4200
     * however. */
 
4201
    if (!db_fix->db_values) {
 
4202
        pa_log("Decibel fix for element %s lacks the dB values.", db_fix->name);
 
4203
        return -1;
 
4204
    }
 
4205
 
 
4206
    return 0;
 
4207
}
 
4208
 
 
4209
void pa_alsa_decibel_fix_dump(pa_alsa_decibel_fix *db_fix) {
 
4210
    char *db_values = NULL;
 
4211
 
 
4212
    pa_assert(db_fix);
 
4213
 
 
4214
    if (db_fix->db_values) {
 
4215
        pa_strbuf *buf;
 
4216
        unsigned long i, nsteps;
 
4217
 
 
4218
        pa_assert(db_fix->min_step <= db_fix->max_step);
 
4219
        nsteps = db_fix->max_step - db_fix->min_step + 1;
 
4220
 
 
4221
        buf = pa_strbuf_new();
 
4222
        for (i = 0; i < nsteps; ++i)
 
4223
            pa_strbuf_printf(buf, "[%li]:%0.2f ", i + db_fix->min_step, db_fix->db_values[i] / 100.0);
 
4224
 
 
4225
        db_values = pa_strbuf_tostring_free(buf);
 
4226
    }
 
4227
 
 
4228
    pa_log_debug("Decibel fix %s, min_step=%li, max_step=%li, db_values=%s",
 
4229
                 db_fix->name, db_fix->min_step, db_fix->max_step, pa_strnull(db_values));
 
4230
 
 
4231
    pa_xfree(db_values);
 
4232
}
 
4233
 
 
4234
pa_alsa_profile_set* pa_alsa_profile_set_new(const char *fname, const pa_channel_map *bonus) {
 
4235
    pa_alsa_profile_set *ps;
 
4236
    pa_alsa_profile *p;
 
4237
    pa_alsa_mapping *m;
 
4238
    pa_alsa_decibel_fix *db_fix;
 
4239
    char *fn;
 
4240
    int r;
 
4241
    void *state;
 
4242
 
 
4243
    static pa_config_item items[] = {
 
4244
        /* [General] */
 
4245
        { "auto-profiles",          pa_config_parse_bool,         NULL, "General" },
 
4246
 
 
4247
        /* [Mapping ...] */
 
4248
        { "device-strings",         mapping_parse_device_strings, NULL, NULL },
 
4249
        { "channel-map",            mapping_parse_channel_map,    NULL, NULL },
 
4250
        { "paths-input",            mapping_parse_paths,          NULL, NULL },
 
4251
        { "paths-output",           mapping_parse_paths,          NULL, NULL },
 
4252
        { "element-input",          mapping_parse_element,        NULL, NULL },
 
4253
        { "element-output",         mapping_parse_element,        NULL, NULL },
 
4254
        { "direction",              mapping_parse_direction,      NULL, NULL },
 
4255
        { "exact-channels",         mapping_parse_exact_channels, NULL, NULL },
 
4256
 
 
4257
        /* Shared by [Mapping ...] and [Profile ...] */
 
4258
        { "description",            mapping_parse_description,    NULL, NULL },
 
4259
        { "priority",               mapping_parse_priority,       NULL, NULL },
 
4260
        { "fallback",               mapping_parse_fallback,       NULL, NULL },
 
4261
 
 
4262
        /* [Profile ...] */
 
4263
        { "input-mappings",         profile_parse_mappings,       NULL, NULL },
 
4264
        { "output-mappings",        profile_parse_mappings,       NULL, NULL },
 
4265
        { "skip-probe",             profile_parse_skip_probe,     NULL, NULL },
 
4266
 
 
4267
        /* [DecibelFix ...] */
 
4268
        { "db-values",              decibel_fix_parse_db_values,  NULL, NULL },
 
4269
        { NULL, NULL, NULL, NULL }
 
4270
    };
 
4271
 
 
4272
    ps = pa_xnew0(pa_alsa_profile_set, 1);
 
4273
    ps->mappings = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL, (pa_free_cb_t) mapping_free);
 
4274
    ps->profiles = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL, (pa_free_cb_t) profile_free);
 
4275
    ps->decibel_fixes = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL, (pa_free_cb_t) decibel_fix_free);
 
4276
    ps->input_paths = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL, (pa_free_cb_t) pa_alsa_path_free);
 
4277
    ps->output_paths = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL, (pa_free_cb_t) pa_alsa_path_free);
 
4278
 
 
4279
    items[0].data = &ps->auto_profiles;
 
4280
 
 
4281
    if (!fname)
 
4282
        fname = "default.conf";
 
4283
 
 
4284
    fn = pa_maybe_prefix_path(fname,
 
4285
                              pa_run_from_build_tree() ? PA_SRCDIR "/modules/alsa/mixer/profile-sets/" :
 
4286
                              PA_ALSA_PROFILE_SETS_DIR);
 
4287
 
 
4288
    r = pa_config_parse(fn, NULL, items, NULL, ps);
 
4289
    pa_xfree(fn);
 
4290
 
 
4291
    if (r < 0)
 
4292
        goto fail;
 
4293
 
 
4294
    PA_HASHMAP_FOREACH(m, ps->mappings, state)
 
4295
        if (mapping_verify(m, bonus) < 0)
 
4296
            goto fail;
 
4297
 
 
4298
    if (ps->auto_profiles)
 
4299
        profile_set_add_auto(ps);
 
4300
 
 
4301
    PA_HASHMAP_FOREACH(p, ps->profiles, state)
 
4302
        if (profile_verify(p) < 0)
 
4303
            goto fail;
 
4304
 
 
4305
    PA_HASHMAP_FOREACH(db_fix, ps->decibel_fixes, state)
 
4306
        if (decibel_fix_verify(db_fix) < 0)
 
4307
            goto fail;
 
4308
 
 
4309
    return ps;
 
4310
 
 
4311
fail:
 
4312
    pa_alsa_profile_set_free(ps);
 
4313
    return NULL;
 
4314
}
 
4315
 
 
4316
static void profile_finalize_probing(pa_alsa_profile *to_be_finalized, pa_alsa_profile *next) {
 
4317
    pa_alsa_mapping *m;
 
4318
    uint32_t idx;
 
4319
 
 
4320
    if (!to_be_finalized)
 
4321
        return;
 
4322
 
 
4323
    if (to_be_finalized->output_mappings)
 
4324
        PA_IDXSET_FOREACH(m, to_be_finalized->output_mappings, idx) {
 
4325
 
 
4326
            if (!m->output_pcm)
 
4327
                continue;
 
4328
 
 
4329
            if (to_be_finalized->supported)
 
4330
                m->supported++;
 
4331
 
 
4332
            /* If this mapping is also in the next profile, we won't close the
 
4333
             * pcm handle here, because it would get immediately reopened
 
4334
             * anyway. */
 
4335
            if (next && next->output_mappings && pa_idxset_get_by_data(next->output_mappings, m, NULL))
 
4336
                continue;
 
4337
 
 
4338
            snd_pcm_close(m->output_pcm);
 
4339
            m->output_pcm = NULL;
 
4340
        }
 
4341
 
 
4342
    if (to_be_finalized->input_mappings)
 
4343
        PA_IDXSET_FOREACH(m, to_be_finalized->input_mappings, idx) {
 
4344
 
 
4345
            if (!m->input_pcm)
 
4346
                continue;
 
4347
 
 
4348
            if (to_be_finalized->supported)
 
4349
                m->supported++;
 
4350
 
 
4351
            /* If this mapping is also in the next profile, we won't close the
 
4352
             * pcm handle here, because it would get immediately reopened
 
4353
             * anyway. */
 
4354
            if (next && next->input_mappings && pa_idxset_get_by_data(next->input_mappings, m, NULL))
 
4355
                continue;
 
4356
 
 
4357
            snd_pcm_close(m->input_pcm);
 
4358
            m->input_pcm = NULL;
 
4359
        }
 
4360
}
 
4361
 
 
4362
static snd_pcm_t* mapping_open_pcm(pa_alsa_mapping *m,
 
4363
                                   const pa_sample_spec *ss,
 
4364
                                   const char *dev_id,
 
4365
                                   bool exact_channels,
 
4366
                                   int mode,
 
4367
                                   unsigned default_n_fragments,
 
4368
                                   unsigned default_fragment_size_msec) {
 
4369
 
 
4370
    snd_pcm_t* handle;
 
4371
    pa_sample_spec try_ss = *ss;
 
4372
    pa_channel_map try_map = m->channel_map;
 
4373
    snd_pcm_uframes_t try_period_size, try_buffer_size;
 
4374
 
 
4375
    try_ss.channels = try_map.channels;
 
4376
 
 
4377
    try_period_size =
 
4378
        pa_usec_to_bytes(default_fragment_size_msec * PA_USEC_PER_MSEC, &try_ss) /
 
4379
        pa_frame_size(&try_ss);
 
4380
    try_buffer_size = default_n_fragments * try_period_size;
 
4381
 
 
4382
    handle = pa_alsa_open_by_template(
 
4383
                              m->device_strings, dev_id, NULL, &try_ss,
 
4384
                              &try_map, mode, &try_period_size,
 
4385
                              &try_buffer_size, 0, NULL, NULL, exact_channels);
 
4386
    if (handle && !exact_channels && m->channel_map.channels != try_map.channels) {
 
4387
        char buf[PA_CHANNEL_MAP_SNPRINT_MAX];
 
4388
        pa_log_debug("Channel map for mapping '%s' permanently changed to '%s'", m->name,
 
4389
                     pa_channel_map_snprint(buf, sizeof(buf), &try_map));
 
4390
        m->channel_map = try_map;
 
4391
    }
 
4392
    return handle;
 
4393
}
 
4394
 
 
4395
static void paths_drop_unused(pa_hashmap* h, pa_hashmap *keep) {
 
4396
 
 
4397
    void* state = NULL;
 
4398
    const void* key;
 
4399
    pa_alsa_path* p;
 
4400
 
 
4401
    pa_assert(h);
 
4402
    pa_assert(keep);
 
4403
 
 
4404
    p = pa_hashmap_iterate(h, &state, &key);
 
4405
    while (p) {
 
4406
        if (pa_hashmap_get(keep, p) == NULL)
 
4407
            pa_hashmap_remove_and_free(h, key);
 
4408
        p = pa_hashmap_iterate(h, &state, &key);
 
4409
    }
 
4410
}
 
4411
 
 
4412
static int add_profiles_to_probe(
 
4413
        pa_alsa_profile **list,
 
4414
        pa_hashmap *profiles,
 
4415
        bool fallback_output,
 
4416
        bool fallback_input) {
 
4417
 
 
4418
    int i = 0;
 
4419
    void *state;
 
4420
    pa_alsa_profile *p;
 
4421
    PA_HASHMAP_FOREACH(p, profiles, state)
 
4422
        if (p->fallback_input == fallback_input && p->fallback_output == fallback_output) {
 
4423
            *list = p;
 
4424
            list++;
 
4425
            i++;
 
4426
        }
 
4427
    return i;
 
4428
}
 
4429
 
 
4430
void pa_alsa_profile_set_probe(
 
4431
        pa_alsa_profile_set *ps,
 
4432
        const char *dev_id,
 
4433
        const pa_sample_spec *ss,
 
4434
        unsigned default_n_fragments,
 
4435
        unsigned default_fragment_size_msec) {
 
4436
 
 
4437
    bool found_output = false, found_input = false;
 
4438
 
 
4439
    pa_alsa_profile *p, *last = NULL;
 
4440
    pa_alsa_profile **pp, **probe_order;
 
4441
    pa_alsa_mapping *m;
 
4442
    pa_hashmap *broken_inputs, *broken_outputs, *used_paths;
 
4443
 
 
4444
    pa_assert(ps);
 
4445
    pa_assert(dev_id);
 
4446
    pa_assert(ss);
 
4447
 
 
4448
    if (ps->probed)
 
4449
        return;
 
4450
 
 
4451
    broken_inputs = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
 
4452
    broken_outputs = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
 
4453
    used_paths = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
 
4454
    pp = probe_order = pa_xnew0(pa_alsa_profile *, pa_hashmap_size(ps->profiles) + 1);
 
4455
 
 
4456
    pp += add_profiles_to_probe(pp, ps->profiles, false, false);
 
4457
    pp += add_profiles_to_probe(pp, ps->profiles, false, true);
 
4458
    pp += add_profiles_to_probe(pp, ps->profiles, true, false);
 
4459
    pp += add_profiles_to_probe(pp, ps->profiles, true, true);
 
4460
 
 
4461
    for (pp = probe_order; *pp; pp++) {
 
4462
        uint32_t idx;
 
4463
        p = *pp;
 
4464
 
 
4465
        /* Skip if fallback and already found something */
 
4466
        if (found_input && p->fallback_input)
 
4467
            continue;
 
4468
        if (found_output && p->fallback_output)
 
4469
            continue;
 
4470
 
 
4471
        /* Skip if this is already marked that it is supported (i.e. from the config file) */
 
4472
        if (!p->supported) {
 
4473
 
 
4474
            profile_finalize_probing(last, p);
 
4475
            p->supported = true;
 
4476
 
 
4477
            if (p->output_mappings) {
 
4478
                PA_IDXSET_FOREACH(m, p->output_mappings, idx) {
 
4479
                    if (pa_hashmap_get(broken_outputs, m) == m) {
 
4480
                        pa_log_debug("Skipping profile %s - will not be able to open output:%s", p->name, m->name);
 
4481
                        p->supported = false;
 
4482
                        break;
 
4483
                    }
 
4484
                }
 
4485
            }
 
4486
 
 
4487
            if (p->input_mappings && p->supported) {
 
4488
                PA_IDXSET_FOREACH(m, p->input_mappings, idx) {
 
4489
                    if (pa_hashmap_get(broken_inputs, m) == m) {
 
4490
                        pa_log_debug("Skipping profile %s - will not be able to open input:%s", p->name, m->name);
 
4491
                        p->supported = false;
 
4492
                        break;
 
4493
                    }
 
4494
                }
 
4495
            }
 
4496
 
 
4497
            if (p->supported)
 
4498
                pa_log_debug("Looking at profile %s", p->name);
 
4499
 
 
4500
            /* Check if we can open all new ones */
 
4501
            if (p->output_mappings && p->supported)
 
4502
                PA_IDXSET_FOREACH(m, p->output_mappings, idx) {
 
4503
 
 
4504
                    if (m->output_pcm)
 
4505
                        continue;
 
4506
 
 
4507
                    pa_log_debug("Checking for playback on %s (%s)", m->description, m->name);
 
4508
                    if (!(m->output_pcm = mapping_open_pcm(m, ss, dev_id, m->exact_channels,
 
4509
                                                           SND_PCM_STREAM_PLAYBACK,
 
4510
                                                           default_n_fragments,
 
4511
                                                           default_fragment_size_msec))) {
 
4512
                        p->supported = false;
 
4513
                        if (pa_idxset_size(p->output_mappings) == 1 &&
 
4514
                            ((!p->input_mappings) || pa_idxset_size(p->input_mappings) == 0)) {
 
4515
                            pa_log_debug("Caching failure to open output:%s", m->name);
 
4516
                            pa_hashmap_put(broken_outputs, m, m);
 
4517
                        }
 
4518
                        break;
 
4519
                    }
 
4520
                }
 
4521
 
 
4522
            if (p->input_mappings && p->supported)
 
4523
                PA_IDXSET_FOREACH(m, p->input_mappings, idx) {
 
4524
 
 
4525
                    if (m->input_pcm)
 
4526
                        continue;
 
4527
 
 
4528
                    pa_log_debug("Checking for recording on %s (%s)", m->description, m->name);
 
4529
                    if (!(m->input_pcm = mapping_open_pcm(m, ss, dev_id, m->exact_channels,
 
4530
                                                          SND_PCM_STREAM_CAPTURE,
 
4531
                                                          default_n_fragments,
 
4532
                                                          default_fragment_size_msec))) {
 
4533
                        p->supported = false;
 
4534
                        if (pa_idxset_size(p->input_mappings) == 1 &&
 
4535
                            ((!p->output_mappings) || pa_idxset_size(p->output_mappings) == 0)) {
 
4536
                            pa_log_debug("Caching failure to open input:%s", m->name);
 
4537
                            pa_hashmap_put(broken_inputs, m, m);
 
4538
                        }
 
4539
                        break;
 
4540
                    }
 
4541
                }
 
4542
 
 
4543
            last = p;
 
4544
 
 
4545
            if (!p->supported)
 
4546
                continue;
 
4547
        }
 
4548
 
 
4549
        pa_log_debug("Profile %s supported.", p->name);
 
4550
 
 
4551
        if (p->output_mappings)
 
4552
            PA_IDXSET_FOREACH(m, p->output_mappings, idx)
 
4553
                if (m->output_pcm) {
 
4554
                    found_output |= !p->fallback_output;
 
4555
                    mapping_paths_probe(m, p, PA_ALSA_DIRECTION_OUTPUT, used_paths);
 
4556
                }
 
4557
 
 
4558
        if (p->input_mappings)
 
4559
            PA_IDXSET_FOREACH(m, p->input_mappings, idx)
 
4560
                if (m->input_pcm) {
 
4561
                    found_input |= !p->fallback_input;
 
4562
                    mapping_paths_probe(m, p, PA_ALSA_DIRECTION_INPUT, used_paths);
 
4563
                }
 
4564
    }
 
4565
 
 
4566
    /* Clean up */
 
4567
    profile_finalize_probing(last, NULL);
 
4568
 
 
4569
    pa_alsa_profile_set_drop_unsupported(ps);
 
4570
 
 
4571
    paths_drop_unused(ps->input_paths, used_paths);
 
4572
    paths_drop_unused(ps->output_paths, used_paths);
 
4573
    pa_hashmap_free(broken_inputs);
 
4574
    pa_hashmap_free(broken_outputs);
 
4575
    pa_hashmap_free(used_paths);
 
4576
    pa_xfree(probe_order);
 
4577
 
 
4578
    ps->probed = true;
 
4579
}
 
4580
 
 
4581
void pa_alsa_profile_set_dump(pa_alsa_profile_set *ps) {
 
4582
    pa_alsa_profile *p;
 
4583
    pa_alsa_mapping *m;
 
4584
    pa_alsa_decibel_fix *db_fix;
 
4585
    void *state;
 
4586
 
 
4587
    pa_assert(ps);
 
4588
 
 
4589
    pa_log_debug("Profile set %p, auto_profiles=%s, probed=%s, n_mappings=%u, n_profiles=%u, n_decibel_fixes=%u",
 
4590
                 (void*)
 
4591
                 ps,
 
4592
                 pa_yes_no(ps->auto_profiles),
 
4593
                 pa_yes_no(ps->probed),
 
4594
                 pa_hashmap_size(ps->mappings),
 
4595
                 pa_hashmap_size(ps->profiles),
 
4596
                 pa_hashmap_size(ps->decibel_fixes));
 
4597
 
 
4598
    PA_HASHMAP_FOREACH(m, ps->mappings, state)
 
4599
        pa_alsa_mapping_dump(m);
 
4600
 
 
4601
    PA_HASHMAP_FOREACH(p, ps->profiles, state)
 
4602
        pa_alsa_profile_dump(p);
 
4603
 
 
4604
    PA_HASHMAP_FOREACH(db_fix, ps->decibel_fixes, state)
 
4605
        pa_alsa_decibel_fix_dump(db_fix);
 
4606
}
 
4607
 
 
4608
void pa_alsa_profile_set_drop_unsupported(pa_alsa_profile_set *ps) {
 
4609
    pa_alsa_profile *p;
 
4610
    pa_alsa_mapping *m;
 
4611
    void *state;
 
4612
 
 
4613
    PA_HASHMAP_FOREACH(p, ps->profiles, state) {
 
4614
        if (!p->supported)
 
4615
            pa_hashmap_remove_and_free(ps->profiles, p->name);
 
4616
    }
 
4617
 
 
4618
    PA_HASHMAP_FOREACH(m, ps->mappings, state) {
 
4619
        if (m->supported <= 0)
 
4620
            pa_hashmap_remove_and_free(ps->mappings, m->name);
 
4621
    }
 
4622
}
 
4623
 
 
4624
static pa_device_port* device_port_alsa_init(pa_hashmap *ports, /* card ports */
 
4625
    const char* name,
 
4626
    const char* description,
 
4627
    pa_alsa_path *path,
 
4628
    pa_alsa_setting *setting,
 
4629
    pa_card_profile *cp,
 
4630
    pa_hashmap *extra, /* sink/source ports */
 
4631
    pa_core *core) {
 
4632
 
 
4633
    pa_device_port *p;
 
4634
 
 
4635
    pa_assert(path);
 
4636
 
 
4637
    p = pa_hashmap_get(ports, name);
 
4638
 
 
4639
    if (!p) {
 
4640
        pa_alsa_port_data *data;
 
4641
        pa_device_port_new_data port_data;
 
4642
 
 
4643
        pa_device_port_new_data_init(&port_data);
 
4644
        pa_device_port_new_data_set_name(&port_data, name);
 
4645
        pa_device_port_new_data_set_description(&port_data, description);
 
4646
        pa_device_port_new_data_set_direction(&port_data, path->direction == PA_ALSA_DIRECTION_OUTPUT ? PA_DIRECTION_OUTPUT : PA_DIRECTION_INPUT);
 
4647
 
 
4648
        p = pa_device_port_new(core, &port_data, sizeof(pa_alsa_port_data));
 
4649
        pa_device_port_new_data_done(&port_data);
 
4650
        pa_assert(p);
 
4651
        pa_hashmap_put(ports, p->name, p);
 
4652
        pa_proplist_update(p->proplist, PA_UPDATE_REPLACE, path->proplist);
 
4653
 
 
4654
        data = PA_DEVICE_PORT_DATA(p);
 
4655
        data->path = path;
 
4656
        data->setting = setting;
 
4657
        path->port = p;
 
4658
    }
 
4659
 
 
4660
    if (cp)
 
4661
        pa_hashmap_put(p->profiles, cp->name, cp);
 
4662
 
 
4663
    if (extra) {
 
4664
        pa_hashmap_put(extra, p->name, p);
 
4665
        pa_device_port_ref(p);
 
4666
    }
 
4667
 
 
4668
    return p;
 
4669
}
 
4670
 
 
4671
void pa_alsa_path_set_add_ports(
 
4672
        pa_alsa_path_set *ps,
 
4673
        pa_card_profile *cp,
 
4674
        pa_hashmap *ports, /* card ports */
 
4675
        pa_hashmap *extra, /* sink/source ports */
 
4676
        pa_core *core) {
 
4677
 
 
4678
    pa_alsa_path *path;
 
4679
    void *state;
 
4680
 
 
4681
    pa_assert(ports);
 
4682
 
 
4683
    if (!ps)
 
4684
        return;
 
4685
 
 
4686
    PA_HASHMAP_FOREACH(path, ps->paths, state) {
 
4687
        if (!path->settings || !path->settings->next) {
 
4688
            /* If there is no or just one setting we only need a
 
4689
             * single entry */
 
4690
            pa_device_port *port = device_port_alsa_init(ports, path->name,
 
4691
                path->description, path, path->settings, cp, extra, core);
 
4692
            port->priority = path->priority * 100;
 
4693
 
 
4694
        } else {
 
4695
            pa_alsa_setting *s;
 
4696
            PA_LLIST_FOREACH(s, path->settings) {
 
4697
                pa_device_port *port;
 
4698
                char *n, *d;
 
4699
 
 
4700
                n = pa_sprintf_malloc("%s;%s", path->name, s->name);
 
4701
 
 
4702
                if (s->description[0])
 
4703
                    d = pa_sprintf_malloc("%s / %s", path->description, s->description);
 
4704
                else
 
4705
                    d = pa_xstrdup(path->description);
 
4706
 
 
4707
                port = device_port_alsa_init(ports, n, d, path, s, cp, extra, core);
 
4708
                port->priority = path->priority * 100 + s->priority;
 
4709
 
 
4710
                pa_xfree(n);
 
4711
                pa_xfree(d);
 
4712
            }
 
4713
        }
 
4714
    }
 
4715
}
 
4716
 
 
4717
void pa_alsa_add_ports(void *sink_or_source_new_data, pa_alsa_path_set *ps, pa_card *card) {
 
4718
    pa_hashmap *ports;
 
4719
 
 
4720
    pa_assert(sink_or_source_new_data);
 
4721
    pa_assert(ps);
 
4722
 
 
4723
    if (ps->direction == PA_ALSA_DIRECTION_OUTPUT)
 
4724
        ports = ((pa_sink_new_data *) sink_or_source_new_data)->ports;
 
4725
    else
 
4726
        ports = ((pa_source_new_data *) sink_or_source_new_data)->ports;
 
4727
 
 
4728
    if (ps->paths && pa_hashmap_size(ps->paths) > 0) {
 
4729
        pa_assert(card);
 
4730
        pa_alsa_path_set_add_ports(ps, NULL, card->ports, ports, card->core);
 
4731
    }
 
4732
 
 
4733
    pa_log_debug("Added %u ports", pa_hashmap_size(ports));
 
4734
}