1#ifndef fooalsamixerhfoo
2#define fooalsamixerhfoo
3
4/***
5  This file is part of PulseAudio.
6
7  Copyright 2004-2006 Lennart Poettering
8  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
9
10  PulseAudio is free software; you can redistribute it and/or modify
11  it under the terms of the GNU Lesser General Public License as published
12  by the Free Software Foundation; either version 2.1 of the License,
13  or (at your option) any later version.
14
15  PulseAudio is distributed in the hope that it will be useful, but
16  WITHOUT ANY WARRANTY; without even the implied warranty of
17  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18  General Public License for more details.
19
20  You should have received a copy of the GNU Lesser General Public License
21  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
22***/
23
24#include <alsa/asoundlib.h>
25
26#include <pulse/sample.h>
27#include <pulse/mainloop-api.h>
28#include <pulse/channelmap.h>
29#include <pulse/volume.h>
30
31#include <pulsecore/llist.h>
32#include <pulsecore/rtpoll.h>
33
34typedef struct pa_alsa_fdlist pa_alsa_fdlist;
35typedef struct pa_alsa_mixer pa_alsa_mixer;
36typedef struct pa_alsa_mixer_pdata pa_alsa_mixer_pdata;
37typedef struct pa_alsa_setting pa_alsa_setting;
38typedef struct pa_alsa_mixer_id pa_alsa_mixer_id;
39typedef struct pa_alsa_option pa_alsa_option;
40typedef struct pa_alsa_element pa_alsa_element;
41typedef struct pa_alsa_jack pa_alsa_jack;
42typedef struct pa_alsa_path pa_alsa_path;
43typedef struct pa_alsa_path_set pa_alsa_path_set;
44typedef struct pa_alsa_mapping pa_alsa_mapping;
45typedef struct pa_alsa_profile pa_alsa_profile;
46typedef struct pa_alsa_decibel_fix pa_alsa_decibel_fix;
47typedef struct pa_alsa_profile_set pa_alsa_profile_set;
48typedef struct pa_alsa_port_data pa_alsa_port_data;
49
50#include "alsa-util.h"
51#include "alsa-ucm.h"
52
53#define POSITION_MASK_CHANNELS 8
54
55typedef enum pa_alsa_switch_use {
56    PA_ALSA_SWITCH_IGNORE,
57    PA_ALSA_SWITCH_MUTE,   /* make this switch follow mute status */
58    PA_ALSA_SWITCH_OFF,    /* set this switch to 'off' unconditionally */
59    PA_ALSA_SWITCH_ON,     /* set this switch to 'on' unconditionally */
60    PA_ALSA_SWITCH_SELECT  /* allow the user to select switch status through a setting */
61} pa_alsa_switch_use_t;
62
63typedef enum pa_alsa_volume_use {
64    PA_ALSA_VOLUME_IGNORE,
65    PA_ALSA_VOLUME_MERGE,   /* merge this volume slider into the global volume slider */
66    PA_ALSA_VOLUME_OFF,     /* set this volume to minimal unconditionally */
67    PA_ALSA_VOLUME_ZERO,    /* set this volume to 0dB unconditionally */
68    PA_ALSA_VOLUME_CONSTANT /* set this volume to a constant value unconditionally */
69} pa_alsa_volume_use_t;
70
71typedef enum pa_alsa_enumeration_use {
72    PA_ALSA_ENUMERATION_IGNORE,
73    PA_ALSA_ENUMERATION_SELECT
74} pa_alsa_enumeration_use_t;
75
76typedef enum pa_alsa_required {
77    PA_ALSA_REQUIRED_IGNORE,
78    PA_ALSA_REQUIRED_SWITCH,
79    PA_ALSA_REQUIRED_VOLUME,
80    PA_ALSA_REQUIRED_ENUMERATION,
81    PA_ALSA_REQUIRED_ANY
82} pa_alsa_required_t;
83
84typedef enum pa_alsa_direction {
85    PA_ALSA_DIRECTION_ANY,
86    PA_ALSA_DIRECTION_OUTPUT,
87    PA_ALSA_DIRECTION_INPUT
88} pa_alsa_direction_t;
89
90/* A setting combines a couple of options into a single entity that
91 * may be selected. Only one setting can be active at the same
92 * time. */
93struct pa_alsa_setting {
94    pa_alsa_path *path;
95    PA_LLIST_FIELDS(pa_alsa_setting);
96
97    pa_idxset *options;
98
99    char *name;
100    char *description;
101    unsigned priority;
102};
103
104/* An entry for one ALSA mixer */
105struct pa_alsa_mixer {
106    struct pa_alsa_mixer *alias;
107    snd_mixer_t *mixer_handle;
108    pa_alsa_fdlist *fdl;
109    bool used_for_probe_only:1;
110};
111
112/* ALSA mixer element identifier */
113struct pa_alsa_mixer_id {
114    char *name;
115    int index;
116};
117
118char *pa_alsa_mixer_id_to_string(char *dst, size_t dst_len, pa_alsa_mixer_id *id);
119
120/* An option belongs to an element and refers to one enumeration item
121 * of the element is an enumeration item, or a switch status if the
122 * element is a switch item. */
123struct pa_alsa_option {
124    pa_alsa_element *element;
125    PA_LLIST_FIELDS(pa_alsa_option);
126
127    char *alsa_name;
128    int alsa_idx;
129
130    char *name;
131    char *description;
132    unsigned priority;
133
134    pa_alsa_required_t required;
135    pa_alsa_required_t required_any;
136    pa_alsa_required_t required_absent;
137};
138
139/* An element wraps one specific ALSA element. A series of elements
140 * make up a path (see below). If the element is an enumeration or switch
141 * element it may include a list of options. */
142struct pa_alsa_element {
143    pa_alsa_path *path;
144    PA_LLIST_FIELDS(pa_alsa_element);
145
146    struct pa_alsa_mixer_id alsa_id;
147    pa_alsa_direction_t direction;
148
149    pa_alsa_switch_use_t switch_use;
150    pa_alsa_volume_use_t volume_use;
151    pa_alsa_enumeration_use_t enumeration_use;
152
153    pa_alsa_required_t required;
154    pa_alsa_required_t required_any;
155    pa_alsa_required_t required_absent;
156
157    long constant_volume;
158
159    unsigned int override_map;
160    bool direction_try_other:1;
161
162    bool has_dB:1;
163    long min_volume, max_volume;
164    long volume_limit; /* -1 for no configured limit */
165    double min_dB, max_dB;
166
167    pa_channel_position_mask_t masks[SND_MIXER_SCHN_LAST + 1][POSITION_MASK_CHANNELS];
168    unsigned n_channels;
169
170    pa_channel_position_mask_t merged_mask;
171
172    PA_LLIST_HEAD(pa_alsa_option, options);
173
174    pa_alsa_decibel_fix *db_fix;
175};
176
177struct pa_alsa_jack {
178    pa_alsa_path *path;
179    PA_LLIST_FIELDS(pa_alsa_jack);
180
181    snd_mixer_t *mixer_handle;
182    char *mixer_device_name;
183
184    struct pa_alsa_mixer_id alsa_id;
185    char *name; /* E g "Headphone" */
186    bool has_control; /* is the jack itself present? */
187    bool plugged_in; /* is this jack currently plugged in? */
188    snd_mixer_elem_t *melem; /* Jack detection handle */
189    pa_available_t state_unplugged, state_plugged;
190
191    pa_alsa_required_t required;
192    pa_alsa_required_t required_any;
193    pa_alsa_required_t required_absent;
194
195    pa_dynarray *ucm_devices; /* pa_alsa_ucm_device */
196    pa_dynarray *ucm_hw_mute_devices; /* pa_alsa_ucm_device */
197
198    bool append_pcm_to_name;
199};
200
201pa_alsa_jack *pa_alsa_jack_new(pa_alsa_path *path, const char *mixer_device_name, const char *name, int index);
202void pa_alsa_jack_free(pa_alsa_jack *jack);
203void pa_alsa_jack_set_has_control(pa_alsa_jack *jack, bool has_control);
204void pa_alsa_jack_set_plugged_in(pa_alsa_jack *jack, bool plugged_in);
205void pa_alsa_jack_add_ucm_device(pa_alsa_jack *jack, pa_alsa_ucm_device *device);
206void pa_alsa_jack_add_ucm_hw_mute_device(pa_alsa_jack *jack, pa_alsa_ucm_device *device);
207
208/* A path wraps a series of elements into a single entity which can be
209 * used to control it as if it had a single volume slider, a single
210 * mute switch and a single list of selectable options. */
211struct pa_alsa_path {
212    pa_alsa_direction_t direction;
213    pa_device_port* port;
214
215    char *name;
216    char *description_key;
217    char *description;
218    char *availability_group;
219    pa_device_port_type_t device_port_type;
220    unsigned priority;
221    bool autodetect_eld_device;
222    pa_alsa_mixer *eld_mixer_handle;
223    int eld_device;
224    pa_proplist *proplist;
225
226    bool probed:1;
227    bool supported:1;
228    bool has_mute:1;
229    bool has_volume:1;
230    bool has_dB:1;
231    bool mute_during_activation:1;
232    /* These two are used during probing only */
233    bool has_req_any:1;
234    bool req_any_present:1;
235
236    long min_volume, max_volume;
237    double min_dB, max_dB;
238
239    /* This is used during parsing only, as a shortcut so that we
240     * don't have to iterate the list all the time */
241    pa_alsa_element *last_element;
242    pa_alsa_option *last_option;
243    pa_alsa_setting *last_setting;
244    pa_alsa_jack *last_jack;
245
246    PA_LLIST_HEAD(pa_alsa_element, elements);
247    PA_LLIST_HEAD(pa_alsa_setting, settings);
248    PA_LLIST_HEAD(pa_alsa_jack, jacks);
249};
250
251/* A path set is simply a set of paths that are applicable to a
252 * device */
253struct pa_alsa_path_set {
254    pa_hashmap *paths;
255    pa_alsa_direction_t direction;
256};
257
258void pa_alsa_setting_dump(pa_alsa_setting *s);
259
260void pa_alsa_option_dump(pa_alsa_option *o);
261void pa_alsa_jack_dump(pa_alsa_jack *j);
262void pa_alsa_element_dump(pa_alsa_element *e);
263
264pa_alsa_path *pa_alsa_path_new(const char *paths_dir, const char *fname, pa_alsa_direction_t direction);
265pa_alsa_path *pa_alsa_path_synthesize(const char *element, pa_alsa_direction_t direction);
266pa_alsa_element *pa_alsa_element_get(pa_alsa_path *p, const char *section, bool prefixed);
267int pa_alsa_path_probe(pa_alsa_path *p, pa_alsa_mapping *mapping, snd_mixer_t *m, bool ignore_dB);
268void pa_alsa_path_dump(pa_alsa_path *p);
269int pa_alsa_path_get_volume(pa_alsa_path *p, snd_mixer_t *m, const pa_channel_map *cm, pa_cvolume *v);
270int pa_alsa_path_get_mute(pa_alsa_path *path, snd_mixer_t *m, bool *muted);
271int pa_alsa_path_set_volume(pa_alsa_path *path, snd_mixer_t *m, const pa_channel_map *cm, pa_cvolume *v, bool deferred_volume, bool write_to_hw);
272int pa_alsa_path_set_mute(pa_alsa_path *path, snd_mixer_t *m, bool muted);
273int pa_alsa_path_select(pa_alsa_path *p, pa_alsa_setting *s, snd_mixer_t *m, bool device_is_muted);
274void pa_alsa_path_set_callback(pa_alsa_path *p, snd_mixer_t *m, snd_mixer_elem_callback_t cb, void *userdata);
275void pa_alsa_path_free(pa_alsa_path *p);
276
277pa_alsa_path_set *pa_alsa_path_set_new(pa_alsa_mapping *m, pa_alsa_direction_t direction, const char *paths_dir);
278void pa_alsa_path_set_dump(pa_alsa_path_set *s);
279void pa_alsa_path_set_set_callback(pa_alsa_path_set *ps, snd_mixer_t *m, snd_mixer_elem_callback_t cb, void *userdata);
280void pa_alsa_path_set_free(pa_alsa_path_set *s);
281int pa_alsa_path_set_is_empty(pa_alsa_path_set *s);
282
283struct pa_alsa_mapping {
284    pa_alsa_profile_set *profile_set;
285
286    char *name;
287    char *description;
288    char *description_key;
289    unsigned priority;
290    pa_alsa_direction_t direction;
291    /* These are copied over to the resultant sink/source */
292    pa_proplist *proplist;
293
294    pa_sample_spec sample_spec;
295    pa_channel_map channel_map;
296
297    char **device_strings;
298
299    char **input_path_names;
300    char **output_path_names;
301    char **input_element; /* list of fallbacks */
302    char **output_element;
303    pa_alsa_path_set *input_path_set;
304    pa_alsa_path_set *output_path_set;
305
306    unsigned supported;
307    bool exact_channels:1;
308    bool fallback:1;
309
310    /* The "y" in "hw:x,y". This is set to -1 before the device index has been
311     * queried, or if the query failed. */
312    int hw_device_index;
313
314    /* Temporarily used during probing */
315    snd_pcm_t *input_pcm;
316    snd_pcm_t *output_pcm;
317
318    pa_sink *sink;
319    pa_source *source;
320
321    /* ucm device context*/
322    pa_alsa_ucm_mapping_context ucm_context;
323};
324
325struct pa_alsa_profile {
326    pa_alsa_profile_set *profile_set;
327
328    char *name;
329    char *description;
330    char *description_key;
331    unsigned priority;
332
333    char *input_name;
334    char *output_name;
335
336    bool supported:1;
337    bool fallback_input:1;
338    bool fallback_output:1;
339
340    char **input_mapping_names;
341    char **output_mapping_names;
342
343    pa_idxset *input_mappings;
344    pa_idxset *output_mappings;
345};
346
347struct pa_alsa_decibel_fix {
348    char *key;
349
350    pa_alsa_profile_set *profile_set;
351
352    char *name; /* Alsa volume element name. */
353    int index;  /* Alsa volume element index. */
354    long min_step;
355    long max_step;
356
357    /* An array that maps alsa volume element steps to decibels. The steps can
358     * be used as indices to this array, after subtracting min_step from the
359     * real value.
360     *
361     * The values are actually stored as integers representing millibels,
362     * because that's the format the alsa API uses. */
363    long *db_values;
364};
365
366struct pa_alsa_profile_set {
367    pa_hashmap *mappings;
368    pa_hashmap *profiles;
369    pa_hashmap *decibel_fixes;
370    pa_hashmap *input_paths;
371    pa_hashmap *output_paths;
372
373    bool auto_profiles;
374    bool ignore_dB:1;
375    bool probed:1;
376};
377
378void pa_alsa_mapping_dump(pa_alsa_mapping *m);
379void pa_alsa_profile_dump(pa_alsa_profile *p);
380void pa_alsa_decibel_fix_dump(pa_alsa_decibel_fix *db_fix);
381pa_alsa_mapping *pa_alsa_mapping_get(pa_alsa_profile_set *ps, const char *name);
382
383pa_alsa_profile_set* pa_alsa_profile_set_new(const char *fname, const pa_channel_map *bonus);
384void pa_alsa_profile_set_probe(pa_alsa_profile_set *ps, pa_hashmap *mixers, const char *dev_id, const pa_sample_spec *ss, unsigned default_n_fragments, unsigned default_fragment_size_msec);
385void pa_alsa_profile_set_free(pa_alsa_profile_set *s);
386void pa_alsa_profile_set_dump(pa_alsa_profile_set *s);
387void pa_alsa_profile_set_drop_unsupported(pa_alsa_profile_set *s);
388
389pa_alsa_fdlist *pa_alsa_fdlist_new(void);
390void pa_alsa_fdlist_free(pa_alsa_fdlist *fdl);
391int pa_alsa_fdlist_set_handle(pa_alsa_fdlist *fdl, snd_mixer_t *mixer_handle, snd_hctl_t *hctl_handle, pa_mainloop_api* m);
392
393/* Alternative for handling alsa mixer events in io-thread. */
394
395pa_alsa_mixer_pdata *pa_alsa_mixer_pdata_new(void);
396void pa_alsa_mixer_pdata_free(pa_alsa_mixer_pdata *pd);
397int pa_alsa_set_mixer_rtpoll(struct pa_alsa_mixer_pdata *pd, snd_mixer_t *mixer, pa_rtpoll *rtp);
398
399/* Data structure for inclusion in pa_device_port for alsa
400 * sinks/sources. This contains nothing that needs to be freed
401 * individually */
402struct pa_alsa_port_data {
403    pa_alsa_path *path;
404    pa_alsa_setting *setting;
405    bool suspend_when_unavailable;
406};
407
408void pa_alsa_add_ports(void *sink_or_source_new_data, pa_alsa_path_set *ps, pa_card *card);
409void pa_alsa_path_set_add_ports(pa_alsa_path_set *ps, pa_card_profile *cp, pa_hashmap *ports, pa_hashmap *extra, pa_core *core);
410
411#endif
412