pipewire/spa/plugins/alsa/acp/acp.c

2044 lines
60 KiB
C
Raw Normal View History

/* ALSA Card Profile */
/* SPDX-FileCopyrightText: Copyright © 2020 Wim Taymans */
/* SPDX-License-Identifier: MIT */
#include "acp.h"
#include "alsa-mixer.h"
#include "alsa-ucm.h"
#include <spa/utils/string.h>
#include <spa/utils/json.h>
int _acp_log_level = 1;
acp_log_func _acp_log_func;
void *_acp_log_data;
struct spa_i18n *acp_i18n;
#define DEFAULT_RATE 48000
#define VOLUME_ACCURACY (PA_VOLUME_NORM/100) /* don't require volume adjustments to be perfectly correct. don't necessarily extend granularity in software unless the differences get greater than this level */
static const uint32_t channel_table[PA_CHANNEL_POSITION_MAX] = {
[PA_CHANNEL_POSITION_MONO] = ACP_CHANNEL_MONO,
[PA_CHANNEL_POSITION_FRONT_LEFT] = ACP_CHANNEL_FL,
[PA_CHANNEL_POSITION_FRONT_RIGHT] = ACP_CHANNEL_FR,
[PA_CHANNEL_POSITION_FRONT_CENTER] = ACP_CHANNEL_FC,
[PA_CHANNEL_POSITION_REAR_CENTER] = ACP_CHANNEL_RC,
[PA_CHANNEL_POSITION_REAR_LEFT] = ACP_CHANNEL_RL,
[PA_CHANNEL_POSITION_REAR_RIGHT] = ACP_CHANNEL_RR,
[PA_CHANNEL_POSITION_LFE] = ACP_CHANNEL_LFE,
[PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER] = ACP_CHANNEL_FLC,
[PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER] = ACP_CHANNEL_FRC,
[PA_CHANNEL_POSITION_SIDE_LEFT] = ACP_CHANNEL_SL,
[PA_CHANNEL_POSITION_SIDE_RIGHT] = ACP_CHANNEL_SR,
[PA_CHANNEL_POSITION_AUX0] = ACP_CHANNEL_START_Aux + 0,
[PA_CHANNEL_POSITION_AUX1] = ACP_CHANNEL_START_Aux + 1,
[PA_CHANNEL_POSITION_AUX2] = ACP_CHANNEL_START_Aux + 2,
[PA_CHANNEL_POSITION_AUX3] = ACP_CHANNEL_START_Aux + 3,
[PA_CHANNEL_POSITION_AUX4] = ACP_CHANNEL_START_Aux + 4,
[PA_CHANNEL_POSITION_AUX5] = ACP_CHANNEL_START_Aux + 5,
[PA_CHANNEL_POSITION_AUX6] = ACP_CHANNEL_START_Aux + 6,
[PA_CHANNEL_POSITION_AUX7] = ACP_CHANNEL_START_Aux + 7,
[PA_CHANNEL_POSITION_AUX8] = ACP_CHANNEL_START_Aux + 8,
[PA_CHANNEL_POSITION_AUX9] = ACP_CHANNEL_START_Aux + 9,
[PA_CHANNEL_POSITION_AUX10] = ACP_CHANNEL_START_Aux + 10,
[PA_CHANNEL_POSITION_AUX11] = ACP_CHANNEL_START_Aux + 11,
[PA_CHANNEL_POSITION_AUX12] = ACP_CHANNEL_START_Aux + 12,
[PA_CHANNEL_POSITION_AUX13] = ACP_CHANNEL_START_Aux + 13,
[PA_CHANNEL_POSITION_AUX14] = ACP_CHANNEL_START_Aux + 14,
[PA_CHANNEL_POSITION_AUX15] = ACP_CHANNEL_START_Aux + 15,
[PA_CHANNEL_POSITION_AUX16] = ACP_CHANNEL_START_Aux + 16,
[PA_CHANNEL_POSITION_AUX17] = ACP_CHANNEL_START_Aux + 17,
[PA_CHANNEL_POSITION_AUX18] = ACP_CHANNEL_START_Aux + 18,
[PA_CHANNEL_POSITION_AUX19] = ACP_CHANNEL_START_Aux + 19,
[PA_CHANNEL_POSITION_AUX20] = ACP_CHANNEL_START_Aux + 20,
[PA_CHANNEL_POSITION_AUX21] = ACP_CHANNEL_START_Aux + 21,
[PA_CHANNEL_POSITION_AUX22] = ACP_CHANNEL_START_Aux + 22,
[PA_CHANNEL_POSITION_AUX23] = ACP_CHANNEL_START_Aux + 23,
[PA_CHANNEL_POSITION_AUX24] = ACP_CHANNEL_START_Aux + 24,
[PA_CHANNEL_POSITION_AUX25] = ACP_CHANNEL_START_Aux + 25,
[PA_CHANNEL_POSITION_AUX26] = ACP_CHANNEL_START_Aux + 26,
[PA_CHANNEL_POSITION_AUX27] = ACP_CHANNEL_START_Aux + 27,
[PA_CHANNEL_POSITION_AUX28] = ACP_CHANNEL_START_Aux + 28,
[PA_CHANNEL_POSITION_AUX29] = ACP_CHANNEL_START_Aux + 29,
[PA_CHANNEL_POSITION_AUX30] = ACP_CHANNEL_START_Aux + 30,
[PA_CHANNEL_POSITION_AUX31] = ACP_CHANNEL_START_Aux + 31,
[PA_CHANNEL_POSITION_TOP_CENTER] = ACP_CHANNEL_TC,
[PA_CHANNEL_POSITION_TOP_FRONT_LEFT] = ACP_CHANNEL_TFL,
[PA_CHANNEL_POSITION_TOP_FRONT_RIGHT] = ACP_CHANNEL_TFR,
[PA_CHANNEL_POSITION_TOP_FRONT_CENTER] = ACP_CHANNEL_TFC,
[PA_CHANNEL_POSITION_TOP_REAR_LEFT] = ACP_CHANNEL_TRL,
[PA_CHANNEL_POSITION_TOP_REAR_RIGHT] = ACP_CHANNEL_TRR,
[PA_CHANNEL_POSITION_TOP_REAR_CENTER] = ACP_CHANNEL_TRC,
};
static const char *channel_names[] = {
[ACP_CHANNEL_UNKNOWN] = "UNK",
[ACP_CHANNEL_NA] = "NA",
[ACP_CHANNEL_MONO] = "MONO",
[ACP_CHANNEL_FL] = "FL",
[ACP_CHANNEL_FR] = "FR",
[ACP_CHANNEL_FC] = "FC",
[ACP_CHANNEL_LFE] = "LFE",
[ACP_CHANNEL_SL] = "SL",
[ACP_CHANNEL_SR] = "SR",
[ACP_CHANNEL_FLC] = "FLC",
[ACP_CHANNEL_FRC] = "FRC",
[ACP_CHANNEL_RC] = "RC",
[ACP_CHANNEL_RL] = "RL",
[ACP_CHANNEL_RR] = "RR",
[ACP_CHANNEL_TC] = "TC",
[ACP_CHANNEL_TFL] = "TFL",
[ACP_CHANNEL_TFC] = "TFC",
[ACP_CHANNEL_TFR] = "TFR",
[ACP_CHANNEL_TRL] = "TRL",
[ACP_CHANNEL_TRC] = "TRC",
[ACP_CHANNEL_TRR] = "TRR",
[ACP_CHANNEL_RLC] = "RLC",
[ACP_CHANNEL_RRC] = "RRC",
[ACP_CHANNEL_FLW] = "FLW",
[ACP_CHANNEL_FRW] = "FRW",
[ACP_CHANNEL_LFE2] = "LFE2",
[ACP_CHANNEL_FLH] = "FLH",
[ACP_CHANNEL_FCH] = "FCH",
[ACP_CHANNEL_FRH] = "FRH",
[ACP_CHANNEL_TFLC] = "TFLC",
[ACP_CHANNEL_TFRC] = "TFRC",
[ACP_CHANNEL_TSL] = "TSL",
[ACP_CHANNEL_TSR] = "TSR",
[ACP_CHANNEL_LLFE] = "LLFE",
[ACP_CHANNEL_RLFE] = "RLFE",
[ACP_CHANNEL_BC] = "BC",
[ACP_CHANNEL_BLC] = "BLC",
[ACP_CHANNEL_BRC] = "BRC",
};
2020-11-26 10:42:26 +01:00
#define ACP_N_ELEMENTS(arr) (sizeof(arr) / sizeof((arr)[0]))
static inline uint32_t channel_pa2acp(pa_channel_position_t channel)
{
2020-11-26 10:42:26 +01:00
if (channel < 0 || (size_t)channel >= ACP_N_ELEMENTS(channel_table))
return ACP_CHANNEL_UNKNOWN;
return channel_table[channel];
}
char *acp_channel_str(char *buf, size_t len, enum acp_channel ch)
{
if (ch >= ACP_CHANNEL_START_Aux && ch <= ACP_CHANNEL_LAST_Aux) {
snprintf(buf, len, "AUX%d", ch - ACP_CHANNEL_START_Aux);
} else if (ch >= ACP_CHANNEL_UNKNOWN && ch <= ACP_CHANNEL_BRC) {
snprintf(buf, len, "%s", channel_names[ch]);
} else {
snprintf(buf, len, "UNK");
}
return buf;
}
2020-10-01 13:12:19 +02:00
const char *acp_available_str(enum acp_available status)
{
switch (status) {
case ACP_AVAILABLE_UNKNOWN:
return "unknown";
case ACP_AVAILABLE_NO:
return "no";
case ACP_AVAILABLE_YES:
return "yes";
}
return "error";
}
const char *acp_direction_str(enum acp_direction direction)
{
switch (direction) {
case ACP_DIRECTION_CAPTURE:
return "capture";
case ACP_DIRECTION_PLAYBACK:
return "playback";
}
return "error";
}
2020-11-09 15:01:07 +01:00
static void port_free(void *data)
{
2020-11-09 15:01:07 +01:00
pa_device_port *dp = data;
pa_dynarray_clear(&dp->devices);
pa_dynarray_clear(&dp->prof);
pa_device_port_free(dp);
}
2020-11-09 15:01:07 +01:00
static void device_free(void *data)
{
2020-11-09 15:01:07 +01:00
pa_alsa_device *dev = data;
pa_dynarray_clear(&dev->port_array);
pa_proplist_free(dev->proplist);
pa_hashmap_free(dev->ports);
}
static inline void channelmap_to_acp(pa_channel_map *m, uint32_t *map)
{
uint32_t i, j;
for (i = 0; i < m->channels; i++) {
map[i] = channel_pa2acp(m->map[i]);
for (j = 0; j < i; j++) {
if (map[i] == map[j])
map[i] += 32;
}
}
}
static void init_device(pa_card *impl, pa_alsa_device *dev, pa_alsa_direction_t direction,
pa_alsa_mapping *m, uint32_t index)
{
char **d;
dev->card = impl;
dev->mapping = m;
dev->device.index = index;
dev->device.name = m->name;
dev->device.description = m->description;
dev->device.priority = m->priority;
dev->device.device_strings = (const char **)m->device_strings;
dev->device.format.format_mask = m->sample_spec.format;
dev->device.format.rate_mask = m->sample_spec.rate;
dev->device.format.channels = m->channel_map.channels;
pa_cvolume_reset(&dev->real_volume, dev->device.format.channels);
pa_cvolume_reset(&dev->soft_volume, dev->device.format.channels);
channelmap_to_acp(&m->channel_map, dev->device.format.map);
dev->direction = direction;
dev->proplist = pa_proplist_new();
pa_proplist_update(dev->proplist, PA_UPDATE_REPLACE, m->proplist);
if (direction == PA_ALSA_DIRECTION_OUTPUT) {
dev->mixer_path_set = m->output_path_set;
dev->pcm_handle = m->output_pcm;
dev->device.direction = ACP_DIRECTION_PLAYBACK;
pa_proplist_update(dev->proplist, PA_UPDATE_REPLACE, m->output_proplist);
} else {
dev->mixer_path_set = m->input_path_set;
dev->pcm_handle = m->input_pcm;
dev->device.direction = ACP_DIRECTION_CAPTURE;
pa_proplist_update(dev->proplist, PA_UPDATE_REPLACE, m->input_proplist);
}
pa_proplist_sets(dev->proplist, PA_PROP_DEVICE_PROFILE_NAME, m->name);
pa_proplist_sets(dev->proplist, PA_PROP_DEVICE_PROFILE_DESCRIPTION, m->description);
pa_proplist_setf(dev->proplist, "card.profile.device", "%u", index);
pa_proplist_as_dict(dev->proplist, &dev->device.props);
2020-11-09 15:01:07 +01:00
dev->ports = pa_hashmap_new(pa_idxset_string_hash_func,
pa_idxset_string_compare_func);
if (m->ucm_context.ucm) {
dev->ucm_context = &m->ucm_context;
2022-01-03 17:21:28 +01:00
if (impl->ucm.alib_prefix != NULL) {
for (d = m->device_strings; *d; d++) {
2022-01-03 17:21:28 +01:00
if (pa_startswith(*d, impl->ucm.alib_prefix)) {
size_t plen = strlen(impl->ucm.alib_prefix);
size_t len = strlen(*d);
memmove(*d, (*d) + plen, len - plen + 1);
dev->device.flags |= ACP_DEVICE_UCM_DEVICE;
}
}
}
}
for (d = m->device_strings; *d; d++) {
if (pa_startswith(*d, "iec958") ||
pa_startswith(*d, "hdmi"))
dev->device.flags |= ACP_DEVICE_IEC958;
}
pa_dynarray_init(&dev->port_array, NULL);
}
static int compare_profile(const void *a, const void *b)
{
const pa_hashmap_item *i1 = a;
const pa_hashmap_item *i2 = b;
const pa_alsa_profile *p1, *p2;
if (i1->key == NULL || i2->key == NULL)
return 0;
p1 = i1->value;
p2 = i2->value;
if (p1->profile.priority == 0 || p2->profile.priority == 0)
return 0;
return p2->profile.priority - p1->profile.priority;
}
2020-11-09 15:01:07 +01:00
static void profile_free(void *data)
{
pa_alsa_profile *ap = data;
pa_dynarray_clear(&ap->out.devices);
if (ap->profile.flags & ACP_PROFILE_OFF) {
free(ap->name);
free(ap->description);
free(ap);
}
}
static const char *find_best_verb(pa_card *impl)
{
const char *res = NULL;
unsigned prio = 0;
pa_alsa_ucm_verb *verb;
PA_LLIST_FOREACH(verb, impl->ucm.verbs) {
if (verb->priority >= prio)
res = pa_proplist_gets(verb->proplist, PA_ALSA_PROP_UCM_NAME);
}
return res;
}
static int add_pro_profile(pa_card *impl, uint32_t index)
{
snd_ctl_t *ctl_hndl;
int err, dev, count = 0, n_capture = 0, n_playback = 0;
pa_alsa_profile *ap;
pa_alsa_profile_set *ps = impl->profile_set;
pa_alsa_mapping *m;
char *device;
snd_pcm_info_t *pcminfo;
pa_sample_spec ss;
snd_pcm_uframes_t try_period_size, try_buffer_size;
uint32_t idx;
if (impl->use_ucm) {
const char *verb = find_best_verb(impl);
if (verb == NULL)
return -ENOTSUP;
if ((err = snd_use_case_set(impl->ucm.ucm_mgr, "_verb", verb)) < 0) {
pa_log_error("error setting verb: %s", snd_strerror(err));
return err;
}
}
ss.format = PA_SAMPLE_S32LE;
ss.rate = impl->rate;
ss.channels = impl->pro_channels;
ap = pa_xnew0(pa_alsa_profile, 1);
ap->profile_set = ps;
ap->profile.name = ap->name = pa_xstrdup("pro-audio");
ap->profile.description = ap->description = pa_xstrdup(_("Pro Audio"));
ap->profile.available = ACP_AVAILABLE_YES;
2023-01-03 13:13:03 +01:00
ap->profile.flags = ACP_PROFILE_PRO;
ap->output_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
ap->input_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
pa_hashmap_put(ps->profiles, ap->name, ap);
ap->output_name = pa_xstrdup("pro-output");
ap->input_name = pa_xstrdup("pro-input");
ap->priority = 1;
pa_assert_se(asprintf(&device, "hw:%d", index) >= 0);
if ((err = snd_ctl_open(&ctl_hndl, device, 0)) < 0) {
pa_log_error("can't open control for card %s: %s",
device, snd_strerror(err));
free(device);
return err;
}
free(device);
snd_pcm_info_alloca(&pcminfo);
dev = -1;
while (1) {
char desc[128], devstr[128], *name;
if ((err = snd_ctl_pcm_next_device(ctl_hndl, &dev)) < 0) {
pa_log_error("error iterating devices: %s", snd_strerror(err));
break;
}
if (dev < 0)
break;
snd_pcm_info_set_device(pcminfo, dev);
snd_pcm_info_set_subdevice(pcminfo, 0);
snprintf(devstr, sizeof(devstr), "hw:%d,%d", index, dev);
if (count++ == 0)
snprintf(desc, sizeof(desc), "Pro");
else
snprintf(desc, sizeof(desc), "Pro %d", dev);
snd_pcm_info_set_stream(pcminfo, SND_PCM_STREAM_PLAYBACK);
if ((err = snd_ctl_pcm_info(ctl_hndl, pcminfo)) < 0) {
if (err != -ENOENT)
pa_log_error("error pcm info: %s", snd_strerror(err));
}
if (err >= 0) {
pa_assert_se(asprintf(&name, "Mapping pro-output-%d", dev) >= 0);
m = pa_alsa_mapping_get(ps, name);
m->description = pa_xstrdup(desc);
m->device_strings = pa_split_spaces_strv(devstr);
try_period_size = 1024;
try_buffer_size = 1024 * 64;
m->sample_spec = ss;
if ((m->output_pcm = pa_alsa_open_by_template(m->device_strings,
devstr, NULL, &m->sample_spec,
&m->channel_map, SND_PCM_STREAM_PLAYBACK,
&try_period_size, &try_buffer_size,
Revert "alsa: UCM conflicting devices changes" If needs some more work This reverts commit aae770f18b7f6c94195fb3f2e2a61308029086af. This reverts commit 2216956e05689079db5ec05f793158e6479060da. This reverts commit 1d3c4501eff3392e0cd858d980c742c7f88c95e7. This reverts commit c7ca02460761273de0d1a8432c5defaafed43664. This reverts commit 8e4a5f5d5ed3d8b3081956f451c85c23aa74fc15. This reverts commit 8b20675ad12bc9daea84f6d4314ec12fccf5882c. This reverts commit ebe6f8b51a31ff7bd71d560991a12283f710b937. This reverts commit 4b97da1277b709e9749b568319f5eb403ff0f362. This reverts commit 7c46a656c734d4688cac229cd9d49af5ad0609fa. This reverts commit 96ed03e1fc5b1f0a287069c42031d7961239c896. This reverts commit 43770c533c71bfaf2c7e82d47d50bfd0533b53e0. This reverts commit ccb2fce05452185858d22de06d659452702f05d8. This reverts commit 93d0ddc8b327f9aaf11d71193f1d50d64bf1144b. This reverts commit 815e8c8fa09a88269b674786a152fe36849b933d. This reverts commit 97b99a0f7c2dd059c08c6ccd143cc1be06111ad1. This reverts commit 7e4d87402b17cd881e9140c37285b175c622ea24. This reverts commit 168b3ee22c1545b738634c4ae89ae4afbaf68cc4. This reverts commit 5128cdba319a2ced0f2e559ad594b8cbeb97a072. This reverts commit 31cde774c54931109da99bc452c858a208d94d6c. This reverts commit b02c8ba153f21459f1a120d4c1ca1bde6a98f4fb. This reverts commit 68ac72d09857901a33be22922669fc8674a4ab9c. This reverts commit 96cfc9bc1ac63414e7b3a6b928be5a29c03e1431. This reverts commit e2d642a20b1bc4e70f7273ddfc3da376a342e10f. This reverts commit 80fc80c34348bdc247bb1a12bcc174db772208a4. This reverts commit 3a68905c7c45677f64213ff563fd68bce6d14a9f. This reverts commit 7e64680a65e4dc2c755e79e55f43f7596b31e1d9. This reverts commit 8d1e38a2816e113121f2355a5827b0a681a3d41b. This reverts commit 591188195c60bb0bba4019d76547ece5386496ac. This reverts commit 973fe0cb79c9ef508778c7a2bb98bdfebeb70dfe. This reverts commit 4d08c9ad609d47c16d03ae3d5230c14d8d52f49e. This reverts commit 3dce92e58090de98e9f1470d9ba69da1f1b590db. This reverts commit 6b5e232c5c4a8e3d2d1c442c87ab03cc40350978. This reverts commit b1286c62c90167d6f6b6c17ad9bdfbb5d0254f60.
2023-04-12 12:30:56 +02:00
0, NULL, NULL, false))) {
pa_alsa_init_proplist_pcm(NULL, m->output_proplist, m->output_pcm);
pa_proplist_setf(m->output_proplist, "clock.name", "api.alsa.%u", index);
pa_proplist_setf(m->output_proplist, "device.profile.pro", "true");
pa_alsa_close(&m->output_pcm);
m->supported = true;
pa_channel_map_init_auto(&m->channel_map, m->sample_spec.channels, PA_CHANNEL_MAP_AUX);
n_playback++;
}
pa_idxset_put(ap->output_mappings, m, NULL);
free(name);
}
snd_pcm_info_set_stream(pcminfo, SND_PCM_STREAM_CAPTURE);
if ((err = snd_ctl_pcm_info(ctl_hndl, pcminfo)) < 0) {
if (err != -ENOENT)
pa_log_error("error pcm info: %s", snd_strerror(err));
}
if (err >= 0) {
pa_assert_se(asprintf(&name, "Mapping pro-input-%d", dev) >= 0);
m = pa_alsa_mapping_get(ps, name);
m->description = pa_xstrdup(desc);
m->device_strings = pa_split_spaces_strv(devstr);
try_period_size = 1024;
try_buffer_size = 1024 * 64;
m->sample_spec = ss;
if ((m->input_pcm = pa_alsa_open_by_template(m->device_strings,
devstr, NULL, &m->sample_spec,
&m->channel_map, SND_PCM_STREAM_CAPTURE,
&try_period_size, &try_buffer_size,
Revert "alsa: UCM conflicting devices changes" If needs some more work This reverts commit aae770f18b7f6c94195fb3f2e2a61308029086af. This reverts commit 2216956e05689079db5ec05f793158e6479060da. This reverts commit 1d3c4501eff3392e0cd858d980c742c7f88c95e7. This reverts commit c7ca02460761273de0d1a8432c5defaafed43664. This reverts commit 8e4a5f5d5ed3d8b3081956f451c85c23aa74fc15. This reverts commit 8b20675ad12bc9daea84f6d4314ec12fccf5882c. This reverts commit ebe6f8b51a31ff7bd71d560991a12283f710b937. This reverts commit 4b97da1277b709e9749b568319f5eb403ff0f362. This reverts commit 7c46a656c734d4688cac229cd9d49af5ad0609fa. This reverts commit 96ed03e1fc5b1f0a287069c42031d7961239c896. This reverts commit 43770c533c71bfaf2c7e82d47d50bfd0533b53e0. This reverts commit ccb2fce05452185858d22de06d659452702f05d8. This reverts commit 93d0ddc8b327f9aaf11d71193f1d50d64bf1144b. This reverts commit 815e8c8fa09a88269b674786a152fe36849b933d. This reverts commit 97b99a0f7c2dd059c08c6ccd143cc1be06111ad1. This reverts commit 7e4d87402b17cd881e9140c37285b175c622ea24. This reverts commit 168b3ee22c1545b738634c4ae89ae4afbaf68cc4. This reverts commit 5128cdba319a2ced0f2e559ad594b8cbeb97a072. This reverts commit 31cde774c54931109da99bc452c858a208d94d6c. This reverts commit b02c8ba153f21459f1a120d4c1ca1bde6a98f4fb. This reverts commit 68ac72d09857901a33be22922669fc8674a4ab9c. This reverts commit 96cfc9bc1ac63414e7b3a6b928be5a29c03e1431. This reverts commit e2d642a20b1bc4e70f7273ddfc3da376a342e10f. This reverts commit 80fc80c34348bdc247bb1a12bcc174db772208a4. This reverts commit 3a68905c7c45677f64213ff563fd68bce6d14a9f. This reverts commit 7e64680a65e4dc2c755e79e55f43f7596b31e1d9. This reverts commit 8d1e38a2816e113121f2355a5827b0a681a3d41b. This reverts commit 591188195c60bb0bba4019d76547ece5386496ac. This reverts commit 973fe0cb79c9ef508778c7a2bb98bdfebeb70dfe. This reverts commit 4d08c9ad609d47c16d03ae3d5230c14d8d52f49e. This reverts commit 3dce92e58090de98e9f1470d9ba69da1f1b590db. This reverts commit 6b5e232c5c4a8e3d2d1c442c87ab03cc40350978. This reverts commit b1286c62c90167d6f6b6c17ad9bdfbb5d0254f60.
2023-04-12 12:30:56 +02:00
0, NULL, NULL, false))) {
pa_alsa_init_proplist_pcm(NULL, m->input_proplist, m->input_pcm);
pa_proplist_setf(m->input_proplist, "clock.name", "api.alsa.%u", index);
pa_proplist_setf(m->input_proplist, "device.profile.pro", "true");
pa_alsa_close(&m->input_pcm);
m->supported = true;
pa_channel_map_init_auto(&m->channel_map, m->sample_spec.channels, PA_CHANNEL_MAP_AUX);
n_capture++;
}
pa_idxset_put(ap->input_mappings, m, NULL);
free(name);
}
}
snd_ctl_close(ctl_hndl);
if (n_capture == 1 && n_playback == 1) {
PA_IDXSET_FOREACH(m, ap->output_mappings, idx) {
pa_proplist_setf(m->output_proplist, "node.group", "pro-audio-%u", index);
pa_proplist_setf(m->output_proplist, "node.link-group", "pro-audio-%u", index);
pa_proplist_setf(m->output_proplist, "api.alsa.auto-link", "true");
pa_proplist_setf(m->output_proplist, "api.alsa.disable-tsched", "true");
}
PA_IDXSET_FOREACH(m, ap->input_mappings, idx) {
pa_proplist_setf(m->input_proplist, "node.group", "pro-audio-%u", index);
pa_proplist_setf(m->input_proplist, "node.link-group", "pro-audio-%u", index);
pa_proplist_setf(m->input_proplist, "api.alsa.auto-link", "true");
pa_proplist_setf(m->input_proplist, "api.alsa.disable-tsched", "true");
}
}
return 0;
}
static bool contains_string(const char *arr, const char *str)
{
struct spa_json it[2];
char v[256];
if (arr == NULL || str == NULL)
return false;
spa_json_init(&it[0], arr, strlen(arr));
if (spa_json_enter_array(&it[0], &it[1]) <= 0)
spa_json_init(&it[1], arr, strlen(arr));
while (spa_json_get_string(&it[1], v, sizeof(v)) > 0) {
if (spa_streq(v, str))
return true;
}
return false;
}
static void add_profiles(pa_card *impl)
{
pa_alsa_profile *ap;
void *state;
struct acp_card_profile *cp;
pa_device_port *dp;
pa_alsa_device *dev;
int n_profiles, n_ports, n_devices;
uint32_t idx;
const char *arr;
n_devices = 0;
2020-11-09 15:01:07 +01:00
pa_dynarray_init(&impl->out.devices, device_free);
ap = pa_xnew0(pa_alsa_profile, 1);
ap->profile.name = ap->name = pa_xstrdup("off");
ap->profile.description = ap->description = pa_xstrdup(_("Off"));
ap->profile.available = ACP_AVAILABLE_YES;
2020-09-28 11:32:36 +02:00
ap->profile.flags = ACP_PROFILE_OFF;
pa_hashmap_put(impl->profiles, ap->name, ap);
add_pro_profile(impl, impl->card.index);
PA_HASHMAP_FOREACH(ap, impl->profile_set->profiles, state) {
pa_alsa_mapping *m;
cp = &ap->profile;
cp->name = ap->name;
cp->description = ap->description;
cp->priority = ap->priority ? ap->priority : 1;
pa_dynarray_init(&ap->out.devices, NULL);
if (ap->output_mappings) {
PA_IDXSET_FOREACH(m, ap->output_mappings, idx) {
dev = &m->output;
if (dev->mapping == NULL) {
init_device(impl, dev, PA_ALSA_DIRECTION_OUTPUT, m, n_devices++);
pa_dynarray_append(&impl->out.devices, dev);
}
if (impl->use_ucm) {
Revert "alsa: UCM conflicting devices changes" If needs some more work This reverts commit aae770f18b7f6c94195fb3f2e2a61308029086af. This reverts commit 2216956e05689079db5ec05f793158e6479060da. This reverts commit 1d3c4501eff3392e0cd858d980c742c7f88c95e7. This reverts commit c7ca02460761273de0d1a8432c5defaafed43664. This reverts commit 8e4a5f5d5ed3d8b3081956f451c85c23aa74fc15. This reverts commit 8b20675ad12bc9daea84f6d4314ec12fccf5882c. This reverts commit ebe6f8b51a31ff7bd71d560991a12283f710b937. This reverts commit 4b97da1277b709e9749b568319f5eb403ff0f362. This reverts commit 7c46a656c734d4688cac229cd9d49af5ad0609fa. This reverts commit 96ed03e1fc5b1f0a287069c42031d7961239c896. This reverts commit 43770c533c71bfaf2c7e82d47d50bfd0533b53e0. This reverts commit ccb2fce05452185858d22de06d659452702f05d8. This reverts commit 93d0ddc8b327f9aaf11d71193f1d50d64bf1144b. This reverts commit 815e8c8fa09a88269b674786a152fe36849b933d. This reverts commit 97b99a0f7c2dd059c08c6ccd143cc1be06111ad1. This reverts commit 7e4d87402b17cd881e9140c37285b175c622ea24. This reverts commit 168b3ee22c1545b738634c4ae89ae4afbaf68cc4. This reverts commit 5128cdba319a2ced0f2e559ad594b8cbeb97a072. This reverts commit 31cde774c54931109da99bc452c858a208d94d6c. This reverts commit b02c8ba153f21459f1a120d4c1ca1bde6a98f4fb. This reverts commit 68ac72d09857901a33be22922669fc8674a4ab9c. This reverts commit 96cfc9bc1ac63414e7b3a6b928be5a29c03e1431. This reverts commit e2d642a20b1bc4e70f7273ddfc3da376a342e10f. This reverts commit 80fc80c34348bdc247bb1a12bcc174db772208a4. This reverts commit 3a68905c7c45677f64213ff563fd68bce6d14a9f. This reverts commit 7e64680a65e4dc2c755e79e55f43f7596b31e1d9. This reverts commit 8d1e38a2816e113121f2355a5827b0a681a3d41b. This reverts commit 591188195c60bb0bba4019d76547ece5386496ac. This reverts commit 973fe0cb79c9ef508778c7a2bb98bdfebeb70dfe. This reverts commit 4d08c9ad609d47c16d03ae3d5230c14d8d52f49e. This reverts commit 3dce92e58090de98e9f1470d9ba69da1f1b590db. This reverts commit 6b5e232c5c4a8e3d2d1c442c87ab03cc40350978. This reverts commit b1286c62c90167d6f6b6c17ad9bdfbb5d0254f60.
2023-04-12 12:30:56 +02:00
if (m->ucm_context.ucm_devices) {
pa_alsa_ucm_add_ports_combination(NULL, &m->ucm_context,
true, impl->ports, ap, NULL);
pa_alsa_ucm_add_ports(&dev->ports, m->proplist, &m->ucm_context,
true, impl, dev->pcm_handle, impl->profile_set->ignore_dB);
}
}
else
pa_alsa_path_set_add_ports(m->output_path_set, ap, impl->ports,
dev->ports, NULL);
pa_dynarray_append(&ap->out.devices, dev);
}
}
if (ap->input_mappings) {
PA_IDXSET_FOREACH(m, ap->input_mappings, idx) {
dev = &m->input;
if (dev->mapping == NULL) {
init_device(impl, dev, PA_ALSA_DIRECTION_INPUT, m, n_devices++);
pa_dynarray_append(&impl->out.devices, dev);
}
if (impl->use_ucm) {
Revert "alsa: UCM conflicting devices changes" If needs some more work This reverts commit aae770f18b7f6c94195fb3f2e2a61308029086af. This reverts commit 2216956e05689079db5ec05f793158e6479060da. This reverts commit 1d3c4501eff3392e0cd858d980c742c7f88c95e7. This reverts commit c7ca02460761273de0d1a8432c5defaafed43664. This reverts commit 8e4a5f5d5ed3d8b3081956f451c85c23aa74fc15. This reverts commit 8b20675ad12bc9daea84f6d4314ec12fccf5882c. This reverts commit ebe6f8b51a31ff7bd71d560991a12283f710b937. This reverts commit 4b97da1277b709e9749b568319f5eb403ff0f362. This reverts commit 7c46a656c734d4688cac229cd9d49af5ad0609fa. This reverts commit 96ed03e1fc5b1f0a287069c42031d7961239c896. This reverts commit 43770c533c71bfaf2c7e82d47d50bfd0533b53e0. This reverts commit ccb2fce05452185858d22de06d659452702f05d8. This reverts commit 93d0ddc8b327f9aaf11d71193f1d50d64bf1144b. This reverts commit 815e8c8fa09a88269b674786a152fe36849b933d. This reverts commit 97b99a0f7c2dd059c08c6ccd143cc1be06111ad1. This reverts commit 7e4d87402b17cd881e9140c37285b175c622ea24. This reverts commit 168b3ee22c1545b738634c4ae89ae4afbaf68cc4. This reverts commit 5128cdba319a2ced0f2e559ad594b8cbeb97a072. This reverts commit 31cde774c54931109da99bc452c858a208d94d6c. This reverts commit b02c8ba153f21459f1a120d4c1ca1bde6a98f4fb. This reverts commit 68ac72d09857901a33be22922669fc8674a4ab9c. This reverts commit 96cfc9bc1ac63414e7b3a6b928be5a29c03e1431. This reverts commit e2d642a20b1bc4e70f7273ddfc3da376a342e10f. This reverts commit 80fc80c34348bdc247bb1a12bcc174db772208a4. This reverts commit 3a68905c7c45677f64213ff563fd68bce6d14a9f. This reverts commit 7e64680a65e4dc2c755e79e55f43f7596b31e1d9. This reverts commit 8d1e38a2816e113121f2355a5827b0a681a3d41b. This reverts commit 591188195c60bb0bba4019d76547ece5386496ac. This reverts commit 973fe0cb79c9ef508778c7a2bb98bdfebeb70dfe. This reverts commit 4d08c9ad609d47c16d03ae3d5230c14d8d52f49e. This reverts commit 3dce92e58090de98e9f1470d9ba69da1f1b590db. This reverts commit 6b5e232c5c4a8e3d2d1c442c87ab03cc40350978. This reverts commit b1286c62c90167d6f6b6c17ad9bdfbb5d0254f60.
2023-04-12 12:30:56 +02:00
if (m->ucm_context.ucm_devices) {
pa_alsa_ucm_add_ports_combination(NULL, &m->ucm_context,
false, impl->ports, ap, NULL);
pa_alsa_ucm_add_ports(&dev->ports, m->proplist, &m->ucm_context,
false, impl, dev->pcm_handle, impl->profile_set->ignore_dB);
}
} else
pa_alsa_path_set_add_ports(m->input_path_set, ap, impl->ports,
dev->ports, NULL);
pa_dynarray_append(&ap->out.devices, dev);
}
}
cp->n_devices = pa_dynarray_size(&ap->out.devices);
cp->devices = ap->out.devices.array.data;
pa_hashmap_put(impl->profiles, ap->name, cp);
}
pa_dynarray_init(&impl->out.ports, NULL);
n_ports = 0;
PA_HASHMAP_FOREACH(dp, impl->ports, state) {
void *state2;
dp->card = impl;
dp->port.index = n_ports++;
dp->port.priority = dp->priority;
pa_dynarray_init(&dp->prof, NULL);
pa_dynarray_init(&dp->devices, NULL);
n_profiles = 0;
PA_HASHMAP_FOREACH(cp, dp->profiles, state2) {
pa_dynarray_append(&dp->prof, cp);
n_profiles++;
}
dp->port.n_profiles = n_profiles;
dp->port.profiles = dp->prof.array.data;
pa_proplist_setf(dp->proplist, "card.profile.port", "%u", dp->port.index);
pa_proplist_as_dict(dp->proplist, &dp->port.props);
pa_dynarray_append(&impl->out.ports, dp);
}
PA_DYNARRAY_FOREACH(dev, &impl->out.devices, idx) {
PA_HASHMAP_FOREACH(dp, dev->ports, state) {
pa_dynarray_append(&dev->port_array, dp);
pa_dynarray_append(&dp->devices, dev);
}
dev->device.ports = dev->port_array.array.data;
dev->device.n_ports = pa_dynarray_size(&dev->port_array);
}
arr = pa_proplist_gets(impl->proplist, "api.acp.hidden-ports");
PA_HASHMAP_FOREACH(dp, impl->ports, state) {
if (contains_string(arr, dp->name))
dp->port.flags |= ACP_PORT_HIDDEN;
dp->port.devices = dp->devices.array.data;
dp->port.n_devices = pa_dynarray_size(&dp->devices);
}
pa_hashmap_sort(impl->profiles, compare_profile);
n_profiles = 0;
pa_dynarray_init(&impl->out.profiles, NULL);
arr = pa_proplist_gets(impl->proplist, "api.acp.hidden-profiles");
PA_HASHMAP_FOREACH(cp, impl->profiles, state) {
if (contains_string(arr, cp->name))
cp->flags |= ACP_PROFILE_HIDDEN;
cp->index = n_profiles++;
pa_dynarray_append(&impl->out.profiles, cp);
}
}
static pa_available_t calc_port_state(pa_device_port *p, pa_card *impl)
{
void *state;
pa_alsa_jack *jack;
pa_available_t pa = PA_AVAILABLE_UNKNOWN;
pa_device_port *port;
PA_HASHMAP_FOREACH(jack, impl->jacks, state) {
pa_available_t cpa;
if (impl->use_ucm)
port = pa_hashmap_get(impl->ports, jack->name);
else {
if (jack->path)
port = jack->path->port;
else
continue;
}
if (p != port)
continue;
cpa = jack->plugged_in ? jack->state_plugged : jack->state_unplugged;
if (cpa == PA_AVAILABLE_NO) {
/* If a plugged-in jack causes the availability to go to NO, it
* should override all other availability information (like a
* blacklist) so set and bail */
if (jack->plugged_in) {
pa = cpa;
break;
}
2021-04-28 20:29:44 +02:00
/* If the current availability is unknown go the more precise no,
* but otherwise don't change state */
if (pa == PA_AVAILABLE_UNKNOWN)
pa = cpa;
} else if (cpa == PA_AVAILABLE_YES) {
/* Output is available through at least one jack, so go to that
* level of availability. We still need to continue iterating through
* the jacks in case a jack is plugged in that forces the state to no
*/
pa = cpa;
}
}
return pa;
}
static void profile_set_available(pa_card *impl, uint32_t index,
enum acp_available status, bool emit)
{
struct acp_card_profile *p = impl->card.profiles[index];
enum acp_available old = p->available;
if (old != status)
2020-10-01 13:26:06 +02:00
pa_log_info("Profile %s available %s -> %s", p->name,
acp_available_str(old), acp_available_str(status));
p->available = status;
if (emit && impl->events && impl->events->profile_available)
impl->events->profile_available(impl->user_data, index,
old, status);
}
struct temp_port_avail {
pa_device_port *port;
pa_available_t avail;
};
static int report_jack_state(snd_mixer_elem_t *melem, unsigned int mask)
{
pa_card *impl = snd_mixer_elem_get_callback_private(melem);
Revert "alsa: UCM conflicting devices changes" If needs some more work This reverts commit aae770f18b7f6c94195fb3f2e2a61308029086af. This reverts commit 2216956e05689079db5ec05f793158e6479060da. This reverts commit 1d3c4501eff3392e0cd858d980c742c7f88c95e7. This reverts commit c7ca02460761273de0d1a8432c5defaafed43664. This reverts commit 8e4a5f5d5ed3d8b3081956f451c85c23aa74fc15. This reverts commit 8b20675ad12bc9daea84f6d4314ec12fccf5882c. This reverts commit ebe6f8b51a31ff7bd71d560991a12283f710b937. This reverts commit 4b97da1277b709e9749b568319f5eb403ff0f362. This reverts commit 7c46a656c734d4688cac229cd9d49af5ad0609fa. This reverts commit 96ed03e1fc5b1f0a287069c42031d7961239c896. This reverts commit 43770c533c71bfaf2c7e82d47d50bfd0533b53e0. This reverts commit ccb2fce05452185858d22de06d659452702f05d8. This reverts commit 93d0ddc8b327f9aaf11d71193f1d50d64bf1144b. This reverts commit 815e8c8fa09a88269b674786a152fe36849b933d. This reverts commit 97b99a0f7c2dd059c08c6ccd143cc1be06111ad1. This reverts commit 7e4d87402b17cd881e9140c37285b175c622ea24. This reverts commit 168b3ee22c1545b738634c4ae89ae4afbaf68cc4. This reverts commit 5128cdba319a2ced0f2e559ad594b8cbeb97a072. This reverts commit 31cde774c54931109da99bc452c858a208d94d6c. This reverts commit b02c8ba153f21459f1a120d4c1ca1bde6a98f4fb. This reverts commit 68ac72d09857901a33be22922669fc8674a4ab9c. This reverts commit 96cfc9bc1ac63414e7b3a6b928be5a29c03e1431. This reverts commit e2d642a20b1bc4e70f7273ddfc3da376a342e10f. This reverts commit 80fc80c34348bdc247bb1a12bcc174db772208a4. This reverts commit 3a68905c7c45677f64213ff563fd68bce6d14a9f. This reverts commit 7e64680a65e4dc2c755e79e55f43f7596b31e1d9. This reverts commit 8d1e38a2816e113121f2355a5827b0a681a3d41b. This reverts commit 591188195c60bb0bba4019d76547ece5386496ac. This reverts commit 973fe0cb79c9ef508778c7a2bb98bdfebeb70dfe. This reverts commit 4d08c9ad609d47c16d03ae3d5230c14d8d52f49e. This reverts commit 3dce92e58090de98e9f1470d9ba69da1f1b590db. This reverts commit 6b5e232c5c4a8e3d2d1c442c87ab03cc40350978. This reverts commit b1286c62c90167d6f6b6c17ad9bdfbb5d0254f60.
2023-04-12 12:30:56 +02:00
snd_hctl_elem_t *elem = snd_mixer_elem_get_private(melem);
snd_ctl_elem_value_t *elem_value;
bool plugged_in, any_input_port_available;
void *state;
pa_alsa_jack *jack;
struct temp_port_avail *tp, *tports;
pa_alsa_profile *profile;
enum acp_available active_available = ACP_AVAILABLE_UNKNOWN;
size_t size;
#if 0
/* Changing the jack state may cause a port change, and a port change will
* make the sink or source change the mixer settings. If there are multiple
* users having pulseaudio running, the mixer changes done by inactive
* users may mess up the volume settings for the active users, because when
* the inactive users change the mixer settings, those changes are picked
* up by the active user's pulseaudio instance and the changes are
* interpreted as if the active user changed the settings manually e.g.
* with alsamixer. Even single-user systems suffer from this, because gdm
* runs its own pulseaudio instance.
*
* We rerun this function when being unsuspended to catch up on jack state
* changes */
if (u->card->suspend_cause & PA_SUSPEND_SESSION)
return 0;
#endif
if (mask == SND_CTL_EVENT_MASK_REMOVE)
return 0;
snd_ctl_elem_value_alloca(&elem_value);
if (snd_hctl_elem_read(elem, elem_value) < 0) {
pa_log_warn("Failed to read jack detection from '%s'", pa_strnull(snd_hctl_elem_get_name(elem)));
return 0;
}
plugged_in = !!snd_ctl_elem_value_get_boolean(elem_value, 0);
pa_log_debug("Jack '%s' is now %s", pa_strnull(snd_hctl_elem_get_name(elem)),
plugged_in ? "plugged in" : "unplugged");
size = sizeof(struct temp_port_avail) * (pa_hashmap_size(impl->jacks)+1);
tports = tp = alloca(size);
memset(tports, 0, size);
PA_HASHMAP_FOREACH(jack, impl->jacks, state)
if (jack->melem == melem) {
pa_alsa_jack_set_plugged_in(jack, plugged_in);
if (impl->use_ucm) {
/* When using UCM, pa_alsa_jack_set_plugged_in() maps the jack
* state to port availability. */
continue;
}
/* When not using UCM, we have to do the jack state -> port
* availability mapping ourselves. */
pa_assert_se(tp->port = jack->path->port);
tp->avail = calc_port_state(tp->port, impl);
tp++;
}
/* Report available ports before unavailable ones: in case port 1
* becomes available when port 2 becomes unavailable,
* this prevents an unnecessary switch port 1 -> port 3 -> port 2 */
for (tp = tports; tp->port; tp++)
if (tp->avail != PA_AVAILABLE_NO)
pa_device_port_set_available(tp->port, tp->avail);
for (tp = tports; tp->port; tp++)
if (tp->avail == PA_AVAILABLE_NO)
pa_device_port_set_available(tp->port, tp->avail);
for (tp = tports; tp->port; tp++) {
pa_alsa_port_data *data;
data = PA_DEVICE_PORT_DATA(tp->port);
if (!data->suspend_when_unavailable)
continue;
#if 0
pa_sink *sink;
uint32_t idx;
PA_IDXSET_FOREACH(sink, u->core->sinks, idx) {
if (sink->active_port == tp->port)
pa_sink_suspend(sink, tp->avail == PA_AVAILABLE_NO, PA_SUSPEND_UNAVAILABLE);
}
#endif
}
/* Update profile availabilities. Ideally we would mark all profiles
* unavailable that contain unavailable devices. We can't currently do that
* in all cases, because if there are multiple sinks in a profile, and the
* profile contains a mix of available and unavailable ports, we don't know
* how the ports are distributed between the different sinks. It's possible
* that some sinks contain only unavailable ports, in which case we should
* mark the profile as unavailable, but it's also possible that all sinks
* contain at least one available port, in which case we should mark the
* profile as available. Until the data structures are improved so that we
* can distinguish between these two cases, we mark the problematic cases
* as available (well, "unknown" to be precise, but there's little
* practical difference).
*
* When all output ports are unavailable, we know that all sinks are
* unavailable, and therefore the profile is marked unavailable as well.
* The same applies to input ports as well, of course.
*
* If there are no output ports at all, but the profile contains at least
* one sink, then the output is considered to be available. */
if (impl->card.active_profile_index != ACP_INVALID_INDEX)
active_available = impl->card.profiles[impl->card.active_profile_index]->available;
/* First round - detect, if we have any input port available.
If the hardware can report the state for all I/O jacks, only speakers
may be plugged in. */
any_input_port_available = false;
PA_HASHMAP_FOREACH(profile, impl->profiles, state) {
pa_device_port *port;
void *state2;
if (profile->profile.flags & ACP_PROFILE_OFF)
continue;
PA_HASHMAP_FOREACH(port, impl->ports, state2) {
if (!pa_hashmap_get(port->profiles, profile->profile.name))
continue;
if (port->port.direction == ACP_DIRECTION_CAPTURE &&
port->port.available != ACP_AVAILABLE_NO) {
any_input_port_available = true;
goto input_port_found;
}
}
}
input_port_found:
/* Second round */
PA_HASHMAP_FOREACH(profile, impl->profiles, state) {
pa_device_port *port;
void *state2;
bool has_input_port = false;
bool has_output_port = false;
bool found_available_input_port = false;
bool found_available_output_port = false;
enum acp_available available = ACP_AVAILABLE_UNKNOWN;
if (profile->profile.flags & ACP_PROFILE_OFF)
continue;
PA_HASHMAP_FOREACH(port, impl->ports, state2) {
if (!pa_hashmap_get(port->profiles, profile->profile.name))
continue;
if (port->port.direction == ACP_DIRECTION_CAPTURE) {
has_input_port = true;
if (port->port.available != ACP_AVAILABLE_NO)
found_available_input_port = true;
} else {
has_output_port = true;
if (port->port.available != ACP_AVAILABLE_NO)
found_available_output_port = true;
}
}
if ((has_input_port && !found_available_input_port) ||
(has_output_port && !found_available_output_port))
available = ACP_AVAILABLE_NO;
if (has_input_port && !has_output_port && found_available_input_port)
available = ACP_AVAILABLE_YES;
if (has_output_port && (!has_input_port || !any_input_port_available) && found_available_output_port)
available = ACP_AVAILABLE_YES;
if (has_output_port && has_input_port && found_available_output_port && found_available_input_port)
available = ACP_AVAILABLE_YES;
/* We want to update the active profile's status last, so logic that
* may change the active profile based on profile availability status
* has an updated view of all profiles' availabilities. */
if (profile->profile.index == impl->card.active_profile_index)
active_available = available;
else
profile_set_available(impl, profile->profile.index, available, false);
}
if (impl->card.active_profile_index != ACP_INVALID_INDEX)
profile_set_available(impl, impl->card.active_profile_index, active_available, true);
return 0;
}
static void init_jacks(pa_card *impl)
{
void *state;
pa_alsa_path* path;
pa_alsa_jack* jack;
2021-01-05 10:08:31 +01:00
char buf[64];
impl->jacks = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
if (impl->use_ucm) {
PA_LLIST_FOREACH(jack, impl->ucm.jacks)
if (jack->has_control)
pa_hashmap_put(impl->jacks, jack, jack);
} else {
/* See if we have any jacks */
if (impl->profile_set->output_paths)
PA_HASHMAP_FOREACH(path, impl->profile_set->output_paths, state)
PA_LLIST_FOREACH(jack, path->jacks)
if (jack->has_control)
pa_hashmap_put(impl->jacks, jack, jack);
if (impl->profile_set->input_paths)
PA_HASHMAP_FOREACH(path, impl->profile_set->input_paths, state)
PA_LLIST_FOREACH(jack, path->jacks)
if (jack->has_control)
pa_hashmap_put(impl->jacks, jack, jack);
}
pa_log_debug("Found %d jacks.", pa_hashmap_size(impl->jacks));
if (pa_hashmap_size(impl->jacks) == 0)
return;
PA_HASHMAP_FOREACH(jack, impl->jacks, state) {
if (!jack->mixer_device_name) {
jack->mixer_handle = pa_alsa_open_mixer(impl->ucm.mixers, impl->card.index, false);
if (!jack->mixer_handle) {
pa_log("Failed to open mixer for card %d for jack detection", impl->card.index);
continue;
}
} else {
jack->mixer_handle = pa_alsa_open_mixer_by_name(impl->ucm.mixers, jack->mixer_device_name, false);
if (!jack->mixer_handle) {
pa_log("Failed to open mixer '%s' for jack detection", jack->mixer_device_name);
continue;
}
}
pa_alsa_mixer_use_for_poll(impl->ucm.mixers, jack->mixer_handle);
2021-01-05 10:08:31 +01:00
jack->melem = pa_alsa_mixer_find_card(jack->mixer_handle, &jack->alsa_id, 0);
if (!jack->melem) {
2021-01-05 10:08:31 +01:00
pa_alsa_mixer_id_to_string(buf, sizeof(buf), &jack->alsa_id);
pa_log_warn("Jack '%s' seems to have disappeared.", buf);
pa_alsa_jack_set_has_control(jack, false);
continue;
}
snd_mixer_elem_set_callback(jack->melem, report_jack_state);
snd_mixer_elem_set_callback_private(jack->melem, impl);
report_jack_state(jack->melem, 0);
}
}
static pa_device_port* find_port_with_eld_device(pa_card *impl, int device)
{
void *state;
pa_device_port *p;
if (impl->use_ucm) {
PA_HASHMAP_FOREACH(p, impl->ports, state) {
pa_alsa_ucm_port_data *data = PA_DEVICE_PORT_DATA(p);
pa_assert(data->eld_mixer_device_name);
if (device == data->eld_device)
return p;
}
} else {
PA_HASHMAP_FOREACH(p, impl->ports, state) {
pa_alsa_port_data *data = PA_DEVICE_PORT_DATA(p);
pa_assert(data->path);
if (device == data->path->eld_device)
return p;
}
}
return NULL;
}
static int hdmi_eld_changed(snd_mixer_elem_t *melem, unsigned int mask)
{
pa_card *impl = snd_mixer_elem_get_callback_private(melem);
Revert "alsa: UCM conflicting devices changes" If needs some more work This reverts commit aae770f18b7f6c94195fb3f2e2a61308029086af. This reverts commit 2216956e05689079db5ec05f793158e6479060da. This reverts commit 1d3c4501eff3392e0cd858d980c742c7f88c95e7. This reverts commit c7ca02460761273de0d1a8432c5defaafed43664. This reverts commit 8e4a5f5d5ed3d8b3081956f451c85c23aa74fc15. This reverts commit 8b20675ad12bc9daea84f6d4314ec12fccf5882c. This reverts commit ebe6f8b51a31ff7bd71d560991a12283f710b937. This reverts commit 4b97da1277b709e9749b568319f5eb403ff0f362. This reverts commit 7c46a656c734d4688cac229cd9d49af5ad0609fa. This reverts commit 96ed03e1fc5b1f0a287069c42031d7961239c896. This reverts commit 43770c533c71bfaf2c7e82d47d50bfd0533b53e0. This reverts commit ccb2fce05452185858d22de06d659452702f05d8. This reverts commit 93d0ddc8b327f9aaf11d71193f1d50d64bf1144b. This reverts commit 815e8c8fa09a88269b674786a152fe36849b933d. This reverts commit 97b99a0f7c2dd059c08c6ccd143cc1be06111ad1. This reverts commit 7e4d87402b17cd881e9140c37285b175c622ea24. This reverts commit 168b3ee22c1545b738634c4ae89ae4afbaf68cc4. This reverts commit 5128cdba319a2ced0f2e559ad594b8cbeb97a072. This reverts commit 31cde774c54931109da99bc452c858a208d94d6c. This reverts commit b02c8ba153f21459f1a120d4c1ca1bde6a98f4fb. This reverts commit 68ac72d09857901a33be22922669fc8674a4ab9c. This reverts commit 96cfc9bc1ac63414e7b3a6b928be5a29c03e1431. This reverts commit e2d642a20b1bc4e70f7273ddfc3da376a342e10f. This reverts commit 80fc80c34348bdc247bb1a12bcc174db772208a4. This reverts commit 3a68905c7c45677f64213ff563fd68bce6d14a9f. This reverts commit 7e64680a65e4dc2c755e79e55f43f7596b31e1d9. This reverts commit 8d1e38a2816e113121f2355a5827b0a681a3d41b. This reverts commit 591188195c60bb0bba4019d76547ece5386496ac. This reverts commit 973fe0cb79c9ef508778c7a2bb98bdfebeb70dfe. This reverts commit 4d08c9ad609d47c16d03ae3d5230c14d8d52f49e. This reverts commit 3dce92e58090de98e9f1470d9ba69da1f1b590db. This reverts commit 6b5e232c5c4a8e3d2d1c442c87ab03cc40350978. This reverts commit b1286c62c90167d6f6b6c17ad9bdfbb5d0254f60.
2023-04-12 12:30:56 +02:00
snd_hctl_elem_t *elem = snd_mixer_elem_get_private(melem);
int device = snd_hctl_elem_get_device(elem);
const char *old_monitor_name;
pa_device_port *p;
pa_hdmi_eld eld;
bool changed = false;
if (mask == SND_CTL_EVENT_MASK_REMOVE)
return 0;
p = find_port_with_eld_device(impl, device);
if (p == NULL) {
pa_log_error("Invalid device changed in ALSA: %d", device);
return 0;
}
if (pa_alsa_get_hdmi_eld(elem, &eld) < 0)
memset(&eld, 0, sizeof(eld));
old_monitor_name = pa_proplist_gets(p->proplist, PA_PROP_DEVICE_PRODUCT_NAME);
if (eld.monitor_name[0] == '\0') {
changed |= old_monitor_name != NULL;
pa_proplist_unset(p->proplist, PA_PROP_DEVICE_PRODUCT_NAME);
} else {
changed |= (old_monitor_name == NULL) || (!spa_streq(old_monitor_name, eld.monitor_name));
pa_proplist_sets(p->proplist, PA_PROP_DEVICE_PRODUCT_NAME, eld.monitor_name);
}
pa_proplist_as_dict(p->proplist, &p->port.props);
if (changed && mask != 0 && impl->events && impl->events->props_changed)
2020-10-01 11:31:52 +02:00
impl->events->props_changed(impl->user_data);
return 0;
}
static void init_eld_ctls(pa_card *impl)
{
void *state;
pa_device_port *port;
/* The code in this function expects ports to have a pa_alsa_port_data
* struct as their data, but in UCM mode ports don't have any data. Hence,
* the ELD controls can't currently be used in UCM mode. */
PA_HASHMAP_FOREACH(port, impl->ports, state) {
snd_mixer_t *mixer_handle;
snd_mixer_elem_t* melem;
int device;
if (impl->use_ucm) {
pa_alsa_ucm_port_data *data = PA_DEVICE_PORT_DATA(port);
device = data->eld_device;
if (device < 0 || !data->eld_mixer_device_name)
continue;
mixer_handle = pa_alsa_open_mixer_by_name(impl->ucm.mixers, data->eld_mixer_device_name, true);
} else {
pa_alsa_port_data *data = PA_DEVICE_PORT_DATA(port);
pa_assert(data->path);
device = data->path->eld_device;
if (device < 0)
continue;
mixer_handle = pa_alsa_open_mixer(impl->ucm.mixers, impl->card.index, true);
}
if (!mixer_handle)
continue;
melem = pa_alsa_mixer_find_pcm(mixer_handle, "ELD", device);
if (melem) {
pa_alsa_mixer_use_for_poll(impl->ucm.mixers, mixer_handle);
snd_mixer_elem_set_callback(melem, hdmi_eld_changed);
snd_mixer_elem_set_callback_private(melem, impl);
hdmi_eld_changed(melem, 0);
pa_log_info("ELD device found for port %s (%d).", port->port.name, device);
}
else
pa_log_debug("No ELD device found for port %s (%d).", port->port.name, device);
}
}
uint32_t acp_card_find_best_profile_index(struct acp_card *card, const char *name)
{
uint32_t i;
uint32_t best, best2, off;
struct acp_card_profile **profiles = card->profiles;
best = best2 = ACP_INVALID_INDEX;
off = 0;
for (i = 0; i < card->n_profiles; i++) {
struct acp_card_profile *p = profiles[i];
if (SPA_FLAG_IS_SET(p->flags, ACP_PROFILE_HIDDEN))
continue;
if (name) {
if (spa_streq(name, p->name))
best = i;
2021-01-27 13:04:24 +01:00
} else if (p->flags & ACP_PROFILE_OFF) {
off = i;
} else if (p->available == ACP_AVAILABLE_YES) {
if (best == ACP_INVALID_INDEX || p->priority > profiles[best]->priority)
best = i;
} else if (p->available != ACP_AVAILABLE_NO) {
if (best2 == ACP_INVALID_INDEX || p->priority > profiles[best2]->priority)
best2 = i;
}
}
if (best == ACP_INVALID_INDEX)
best = best2;
if (best == ACP_INVALID_INDEX)
best = off;
return best;
}
static void find_mixer(pa_card *impl, pa_alsa_device *dev, const char *element, bool ignore_dB)
{
const char *mdev;
pa_alsa_mapping *mapping = dev->mapping;
if (!mapping && !element)
return;
if (!element && mapping && pa_alsa_path_set_is_empty(dev->mixer_path_set))
return;
mdev = pa_proplist_gets(mapping->proplist, "alsa.mixer_device");
if (mdev) {
dev->mixer_handle = pa_alsa_open_mixer_by_name(impl->ucm.mixers, mdev, true);
} else {
dev->mixer_handle = pa_alsa_open_mixer(impl->ucm.mixers, impl->card.index, true);
}
if (!dev->mixer_handle) {
pa_log_info("Failed to find a working mixer device.");
return;
}
if (element) {
if (!(dev->mixer_path = pa_alsa_path_synthesize(element, dev->direction)))
goto fail;
if (pa_alsa_path_probe(dev->mixer_path, NULL, dev->mixer_handle, ignore_dB) < 0)
goto fail;
pa_log_debug("Probed mixer path %s:", dev->mixer_path->name);
pa_alsa_path_dump(dev->mixer_path);
}
return;
fail:
if (dev->mixer_path) {
pa_alsa_path_free(dev->mixer_path);
dev->mixer_path = NULL;
}
dev->mixer_handle = NULL;
}
static int mixer_callback(snd_mixer_elem_t *elem, unsigned int mask)
{
pa_alsa_device *dev = snd_mixer_elem_get_callback_private(elem);
if (mask == SND_CTL_EVENT_MASK_REMOVE)
return 0;
pa_log_info("%p mixer changed %d", dev, mask);
if (mask & SND_CTL_EVENT_MASK_VALUE) {
if (dev->read_volume)
dev->read_volume(dev);
if (dev->read_mute)
dev->read_mute(dev);
}
return 0;
}
static int read_volume(pa_alsa_device *dev)
{
pa_card *impl = dev->card;
pa_cvolume r;
uint32_t i;
int res;
if (!dev->mixer_handle)
return 0;
if ((res = pa_alsa_path_get_volume(dev->mixer_path, dev->mixer_handle, &dev->mapping->channel_map, &r)) < 0)
return res;
/* Shift down by the base volume, so that 0dB becomes maximum volume */
pa_sw_cvolume_multiply_scalar(&r, &r, dev->base_volume);
if (pa_cvolume_equal(&dev->hardware_volume, &r))
return 0;
dev->real_volume = dev->hardware_volume = r;
pa_log_info("New hardware volume: min:%d max:%d",
pa_cvolume_min(&r), pa_cvolume_max(&r));
for (i = 0; i < r.channels; i++)
pa_log_debug(" %d: %d", i, r.values[i]);
pa_cvolume_reset(&dev->soft_volume, r.channels);
if (impl->events && impl->events->volume_changed)
impl->events->volume_changed(impl->user_data, &dev->device);
return 0;
}
static void set_volume(pa_alsa_device *dev, const pa_cvolume *v)
{
pa_cvolume r;
dev->real_volume = *v;
if (!dev->mixer_handle)
return;
/* Shift up by the base volume */
pa_sw_cvolume_divide_scalar(&r, &dev->real_volume, dev->base_volume);
if (pa_alsa_path_set_volume(dev->mixer_path, dev->mixer_handle, &dev->mapping->channel_map,
&r, false, true) < 0)
return;
/* Shift down by the base volume, so that 0dB becomes maximum volume */
pa_sw_cvolume_multiply_scalar(&r, &r, dev->base_volume);
dev->hardware_volume = r;
if (dev->mixer_path->has_dB) {
pa_cvolume new_soft_volume;
bool accurate_enough;
/* Match exactly what the user requested by software */
pa_sw_cvolume_divide(&new_soft_volume, &dev->real_volume, &dev->hardware_volume);
/* If the adjustment to do in software is only minimal we
* can skip it. That saves us CPU at the expense of a bit of
* accuracy */
accurate_enough =
(pa_cvolume_min(&new_soft_volume) >= (PA_VOLUME_NORM - VOLUME_ACCURACY)) &&
(pa_cvolume_max(&new_soft_volume) <= (PA_VOLUME_NORM + VOLUME_ACCURACY));
pa_log_debug("Requested volume: %d", pa_cvolume_max(&dev->real_volume));
pa_log_debug("Got hardware volume: %d", pa_cvolume_max(&dev->hardware_volume));
pa_log_debug("Calculated software volume: %d (accurate-enough=%s)",
pa_cvolume_max(&new_soft_volume),
pa_yes_no(accurate_enough));
if (accurate_enough)
pa_cvolume_reset(&new_soft_volume, new_soft_volume.channels);
dev->soft_volume = new_soft_volume;
} else {
pa_log_debug("Wrote hardware volume: %d", pa_cvolume_max(&r));
/* We can't match exactly what the user requested, hence let's
* at least tell the user about it */
dev->real_volume = r;
}
}
static int read_mute(pa_alsa_device *dev)
{
pa_card *impl = dev->card;
bool mute;
int res;
if (!dev->mixer_handle)
return 0;
if ((res = pa_alsa_path_get_mute(dev->mixer_path, dev->mixer_handle, &mute)) < 0)
return res;
if (mute == dev->muted)
return 0;
dev->muted = mute;
pa_log_info("New hardware muted: %d", mute);
if (impl->events && impl->events->mute_changed)
impl->events->mute_changed(impl->user_data, &dev->device);
return 0;
}
static void set_mute(pa_alsa_device *dev, bool mute)
{
dev->muted = mute;
if (!dev->mixer_handle)
return;
pa_alsa_path_set_mute(dev->mixer_path, dev->mixer_handle, mute);
}
static void mixer_volume_init(pa_card *impl, pa_alsa_device *dev)
{
pa_assert(dev);
if (impl->soft_mixer || !dev->mixer_path || !dev->mixer_path->has_volume) {
dev->read_volume = NULL;
dev->set_volume = NULL;
pa_log_info("Driver does not support hardware volume control, "
"falling back to software volume control.");
dev->base_volume = PA_VOLUME_NORM;
dev->n_volume_steps = PA_VOLUME_NORM+1;
dev->device.flags &= ~ACP_DEVICE_HW_VOLUME;
} else {
dev->read_volume = read_volume;
dev->set_volume = set_volume;
dev->device.flags |= ACP_DEVICE_HW_VOLUME;
#if 0
if (u->mixer_path->has_dB && u->deferred_volume) {
pa_sink_set_write_volume_callback(u->sink, sink_write_volume_cb);
pa_log_info("Successfully enabled deferred volume.");
} else
pa_sink_set_write_volume_callback(u->sink, NULL);
#endif
if (dev->mixer_path->has_dB) {
dev->decibel_volume = true;
pa_log_info("Hardware volume ranges from %0.2f dB to %0.2f dB.",
dev->mixer_path->min_dB, dev->mixer_path->max_dB);
dev->base_volume = pa_sw_volume_from_dB(-dev->mixer_path->max_dB);
dev->n_volume_steps = PA_VOLUME_NORM+1;
pa_log_info("Fixing base volume to %0.2f dB", pa_sw_volume_to_dB(dev->base_volume));
} else {
dev->decibel_volume = false;
pa_log_info("Hardware volume ranges from %li to %li.",
dev->mixer_path->min_volume, dev->mixer_path->max_volume);
dev->base_volume = PA_VOLUME_NORM;
dev->n_volume_steps = dev->mixer_path->max_volume - dev->mixer_path->min_volume + 1;
}
pa_log_info("Using hardware volume control. Hardware dB scale %s.",
dev->mixer_path->has_dB ? "supported" : "not supported");
}
2021-05-20 11:20:19 +10:00
dev->device.base_volume = pa_sw_volume_to_linear(dev->base_volume);
dev->device.volume_step = 1.0f / dev->n_volume_steps;
if (impl->soft_mixer || !dev->mixer_path || !dev->mixer_path->has_mute) {
dev->read_mute = NULL;
dev->set_mute = NULL;
pa_log_info("Driver does not support hardware mute control, falling back to software mute control.");
dev->device.flags &= ~ACP_DEVICE_HW_MUTE;
} else {
dev->read_mute = read_mute;
dev->set_mute = set_mute;
pa_log_info("Using hardware mute control.");
dev->device.flags |= ACP_DEVICE_HW_MUTE;
}
}
static int setup_mixer(pa_card *impl, pa_alsa_device *dev, bool ignore_dB)
{
int res;
bool need_mixer_callback = false;
/* This code is before the u->mixer_handle check, because if the UCM
* configuration doesn't specify volume or mute controls, u->mixer_handle
* will be NULL, but the UCM device enable sequence will still need to be
* executed. */
if (dev->active_port && dev->ucm_context) {
Revert "alsa: UCM conflicting devices changes" If needs some more work This reverts commit aae770f18b7f6c94195fb3f2e2a61308029086af. This reverts commit 2216956e05689079db5ec05f793158e6479060da. This reverts commit 1d3c4501eff3392e0cd858d980c742c7f88c95e7. This reverts commit c7ca02460761273de0d1a8432c5defaafed43664. This reverts commit 8e4a5f5d5ed3d8b3081956f451c85c23aa74fc15. This reverts commit 8b20675ad12bc9daea84f6d4314ec12fccf5882c. This reverts commit ebe6f8b51a31ff7bd71d560991a12283f710b937. This reverts commit 4b97da1277b709e9749b568319f5eb403ff0f362. This reverts commit 7c46a656c734d4688cac229cd9d49af5ad0609fa. This reverts commit 96ed03e1fc5b1f0a287069c42031d7961239c896. This reverts commit 43770c533c71bfaf2c7e82d47d50bfd0533b53e0. This reverts commit ccb2fce05452185858d22de06d659452702f05d8. This reverts commit 93d0ddc8b327f9aaf11d71193f1d50d64bf1144b. This reverts commit 815e8c8fa09a88269b674786a152fe36849b933d. This reverts commit 97b99a0f7c2dd059c08c6ccd143cc1be06111ad1. This reverts commit 7e4d87402b17cd881e9140c37285b175c622ea24. This reverts commit 168b3ee22c1545b738634c4ae89ae4afbaf68cc4. This reverts commit 5128cdba319a2ced0f2e559ad594b8cbeb97a072. This reverts commit 31cde774c54931109da99bc452c858a208d94d6c. This reverts commit b02c8ba153f21459f1a120d4c1ca1bde6a98f4fb. This reverts commit 68ac72d09857901a33be22922669fc8674a4ab9c. This reverts commit 96cfc9bc1ac63414e7b3a6b928be5a29c03e1431. This reverts commit e2d642a20b1bc4e70f7273ddfc3da376a342e10f. This reverts commit 80fc80c34348bdc247bb1a12bcc174db772208a4. This reverts commit 3a68905c7c45677f64213ff563fd68bce6d14a9f. This reverts commit 7e64680a65e4dc2c755e79e55f43f7596b31e1d9. This reverts commit 8d1e38a2816e113121f2355a5827b0a681a3d41b. This reverts commit 591188195c60bb0bba4019d76547ece5386496ac. This reverts commit 973fe0cb79c9ef508778c7a2bb98bdfebeb70dfe. This reverts commit 4d08c9ad609d47c16d03ae3d5230c14d8d52f49e. This reverts commit 3dce92e58090de98e9f1470d9ba69da1f1b590db. This reverts commit 6b5e232c5c4a8e3d2d1c442c87ab03cc40350978. This reverts commit b1286c62c90167d6f6b6c17ad9bdfbb5d0254f60.
2023-04-12 12:30:56 +02:00
if ((res = pa_alsa_ucm_set_port(dev->ucm_context, dev->active_port,
dev->direction == PA_ALSA_DIRECTION_OUTPUT)) < 0)
return res;
}
if (!dev->mixer_handle)
return 0;
if (dev->active_port) {
if (!impl->use_ucm) {
pa_alsa_port_data *data;
/* We have a list of supported paths, so let's activate the
* one that has been chosen as active */
data = PA_DEVICE_PORT_DATA(dev->active_port);
dev->mixer_path = data->path;
pa_alsa_path_select(data->path, data->setting, dev->mixer_handle, dev->muted);
} else {
pa_alsa_ucm_port_data *data;
data = PA_DEVICE_PORT_DATA(dev->active_port);
/* Now activate volume controls, if any */
if (data->path) {
dev->mixer_path = data->path;
pa_alsa_path_select(dev->mixer_path, NULL, dev->mixer_handle, dev->muted);
}
}
} else {
if (!dev->mixer_path && dev->mixer_path_set)
dev->mixer_path = pa_hashmap_first(dev->mixer_path_set->paths);
if (dev->mixer_path) {
/* Hmm, we have only a single path, then let's activate it */
pa_alsa_path_select(dev->mixer_path, dev->mixer_path->settings,
dev->mixer_handle, dev->muted);
} else
return 0;
}
mixer_volume_init(impl, dev);
/* Will we need to register callbacks? */
if (dev->mixer_path_set && dev->mixer_path_set->paths) {
pa_alsa_path *p;
void *state;
PA_HASHMAP_FOREACH(p, dev->mixer_path_set->paths, state) {
if (p->has_volume || p->has_mute)
need_mixer_callback = true;
}
}
else if (dev->mixer_path)
need_mixer_callback = dev->mixer_path->has_volume || dev->mixer_path->has_mute;
if (!impl->soft_mixer && need_mixer_callback) {
pa_alsa_mixer_use_for_poll(impl->ucm.mixers, dev->mixer_handle);
if (dev->mixer_path_set)
pa_alsa_path_set_set_callback(dev->mixer_path_set, dev->mixer_handle, mixer_callback, dev);
else
pa_alsa_path_set_callback(dev->mixer_path, dev->mixer_handle, mixer_callback, dev);
}
return 0;
}
static int device_disable(pa_card *impl, pa_alsa_mapping *mapping, pa_alsa_device *dev)
{
dev->device.flags &= ~ACP_DEVICE_ACTIVE;
if (dev->active_port) {
dev->active_port->port.flags &= ~ACP_PORT_ACTIVE;
dev->active_port = NULL;
}
return 0;
}
static int device_enable(pa_card *impl, pa_alsa_mapping *mapping, pa_alsa_device *dev)
{
const char *mod_name;
uint32_t i, port_index;
int res;
if (impl->use_ucm &&
(mod_name = pa_proplist_gets(mapping->proplist, PA_ALSA_PROP_UCM_MODIFIER))) {
if (snd_use_case_set(impl->ucm.ucm_mgr, "_enamod", mod_name) < 0)
pa_log("Failed to enable ucm modifier %s", mod_name);
else
pa_log_debug("Enabled ucm modifier %s", mod_name);
}
pa_log_info("Device: %s mapping '%s' (%s).", dev->device.description,
mapping->description, mapping->name);
dev->device.flags |= ACP_DEVICE_ACTIVE;
find_mixer(impl, dev, NULL, impl->ignore_dB);
/* Synchronize priority values, as it may have changed when setting the profile */
for (i = 0; i < impl->card.n_ports; i++) {
pa_device_port *p = (pa_device_port *)impl->card.ports[i];
p->port.priority = p->priority;
}
if (impl->auto_port)
port_index = acp_device_find_best_port_index(&dev->device, NULL);
else
port_index = ACP_INVALID_INDEX;
if (port_index == ACP_INVALID_INDEX)
dev->active_port = NULL;
else
dev->active_port = (pa_device_port*)impl->card.ports[port_index];
if (dev->active_port)
dev->active_port->port.flags |= ACP_PORT_ACTIVE;
if ((res = setup_mixer(impl, dev, impl->ignore_dB)) < 0)
return res;
if (dev->read_volume)
dev->read_volume(dev);
else {
pa_cvolume_reset(&dev->real_volume, dev->device.format.channels);
pa_cvolume_reset(&dev->soft_volume, dev->device.format.channels);
}
if (dev->read_mute)
dev->read_mute(dev);
else
dev->muted = false;
return 0;
}
int acp_card_set_profile(struct acp_card *card, uint32_t new_index, uint32_t flags)
{
pa_card *impl = (pa_card *)card;
pa_alsa_mapping *am;
uint32_t old_index = impl->card.active_profile_index;
struct acp_card_profile **profiles = card->profiles;
pa_alsa_profile *op, *np;
uint32_t idx;
2020-08-18 16:52:09 +02:00
int res;
if (new_index >= card->n_profiles)
return -EINVAL;
np = (pa_alsa_profile*)profiles[new_index];
if (SPA_FLAG_IS_SET(np->profile.flags, ACP_PROFILE_HIDDEN))
return -EINVAL;
op = old_index != ACP_INVALID_INDEX ? (pa_alsa_profile*)profiles[old_index] : NULL;
if (op == np)
return 0;
pa_log_info("activate profile: %s (%d)", np->profile.name, new_index);
if (op && op->output_mappings) {
PA_IDXSET_FOREACH(am, op->output_mappings, idx) {
if (np->output_mappings &&
pa_idxset_get_by_data(np->output_mappings, am, NULL))
continue;
device_disable(impl, am, &am->output);
}
}
if (op && op->input_mappings) {
PA_IDXSET_FOREACH(am, op->input_mappings, idx) {
if (np->input_mappings &&
pa_idxset_get_by_data(np->input_mappings, am, NULL))
continue;
device_disable(impl, am, &am->input);
}
}
/* if UCM is available for this card then update the verb */
if (impl->use_ucm && !(np->profile.flags & ACP_PROFILE_PRO)) {
2020-09-28 11:32:36 +02:00
if ((res = pa_alsa_ucm_set_profile(&impl->ucm, impl,
Revert "alsa: UCM conflicting devices changes" If needs some more work This reverts commit aae770f18b7f6c94195fb3f2e2a61308029086af. This reverts commit 2216956e05689079db5ec05f793158e6479060da. This reverts commit 1d3c4501eff3392e0cd858d980c742c7f88c95e7. This reverts commit c7ca02460761273de0d1a8432c5defaafed43664. This reverts commit 8e4a5f5d5ed3d8b3081956f451c85c23aa74fc15. This reverts commit 8b20675ad12bc9daea84f6d4314ec12fccf5882c. This reverts commit ebe6f8b51a31ff7bd71d560991a12283f710b937. This reverts commit 4b97da1277b709e9749b568319f5eb403ff0f362. This reverts commit 7c46a656c734d4688cac229cd9d49af5ad0609fa. This reverts commit 96ed03e1fc5b1f0a287069c42031d7961239c896. This reverts commit 43770c533c71bfaf2c7e82d47d50bfd0533b53e0. This reverts commit ccb2fce05452185858d22de06d659452702f05d8. This reverts commit 93d0ddc8b327f9aaf11d71193f1d50d64bf1144b. This reverts commit 815e8c8fa09a88269b674786a152fe36849b933d. This reverts commit 97b99a0f7c2dd059c08c6ccd143cc1be06111ad1. This reverts commit 7e4d87402b17cd881e9140c37285b175c622ea24. This reverts commit 168b3ee22c1545b738634c4ae89ae4afbaf68cc4. This reverts commit 5128cdba319a2ced0f2e559ad594b8cbeb97a072. This reverts commit 31cde774c54931109da99bc452c858a208d94d6c. This reverts commit b02c8ba153f21459f1a120d4c1ca1bde6a98f4fb. This reverts commit 68ac72d09857901a33be22922669fc8674a4ab9c. This reverts commit 96cfc9bc1ac63414e7b3a6b928be5a29c03e1431. This reverts commit e2d642a20b1bc4e70f7273ddfc3da376a342e10f. This reverts commit 80fc80c34348bdc247bb1a12bcc174db772208a4. This reverts commit 3a68905c7c45677f64213ff563fd68bce6d14a9f. This reverts commit 7e64680a65e4dc2c755e79e55f43f7596b31e1d9. This reverts commit 8d1e38a2816e113121f2355a5827b0a681a3d41b. This reverts commit 591188195c60bb0bba4019d76547ece5386496ac. This reverts commit 973fe0cb79c9ef508778c7a2bb98bdfebeb70dfe. This reverts commit 4d08c9ad609d47c16d03ae3d5230c14d8d52f49e. This reverts commit 3dce92e58090de98e9f1470d9ba69da1f1b590db. This reverts commit 6b5e232c5c4a8e3d2d1c442c87ab03cc40350978. This reverts commit b1286c62c90167d6f6b6c17ad9bdfbb5d0254f60.
2023-04-12 12:30:56 +02:00
np->profile.flags & ACP_PROFILE_OFF ? NULL : np->profile.name,
op ? op->profile.name : NULL)) < 0) {
2020-08-18 16:52:09 +02:00
return res;
}
}
if (np->output_mappings) {
PA_IDXSET_FOREACH(am, np->output_mappings, idx) {
if (impl->use_ucm) {
/* Update ports priorities */
Revert "alsa: UCM conflicting devices changes" If needs some more work This reverts commit aae770f18b7f6c94195fb3f2e2a61308029086af. This reverts commit 2216956e05689079db5ec05f793158e6479060da. This reverts commit 1d3c4501eff3392e0cd858d980c742c7f88c95e7. This reverts commit c7ca02460761273de0d1a8432c5defaafed43664. This reverts commit 8e4a5f5d5ed3d8b3081956f451c85c23aa74fc15. This reverts commit 8b20675ad12bc9daea84f6d4314ec12fccf5882c. This reverts commit ebe6f8b51a31ff7bd71d560991a12283f710b937. This reverts commit 4b97da1277b709e9749b568319f5eb403ff0f362. This reverts commit 7c46a656c734d4688cac229cd9d49af5ad0609fa. This reverts commit 96ed03e1fc5b1f0a287069c42031d7961239c896. This reverts commit 43770c533c71bfaf2c7e82d47d50bfd0533b53e0. This reverts commit ccb2fce05452185858d22de06d659452702f05d8. This reverts commit 93d0ddc8b327f9aaf11d71193f1d50d64bf1144b. This reverts commit 815e8c8fa09a88269b674786a152fe36849b933d. This reverts commit 97b99a0f7c2dd059c08c6ccd143cc1be06111ad1. This reverts commit 7e4d87402b17cd881e9140c37285b175c622ea24. This reverts commit 168b3ee22c1545b738634c4ae89ae4afbaf68cc4. This reverts commit 5128cdba319a2ced0f2e559ad594b8cbeb97a072. This reverts commit 31cde774c54931109da99bc452c858a208d94d6c. This reverts commit b02c8ba153f21459f1a120d4c1ca1bde6a98f4fb. This reverts commit 68ac72d09857901a33be22922669fc8674a4ab9c. This reverts commit 96cfc9bc1ac63414e7b3a6b928be5a29c03e1431. This reverts commit e2d642a20b1bc4e70f7273ddfc3da376a342e10f. This reverts commit 80fc80c34348bdc247bb1a12bcc174db772208a4. This reverts commit 3a68905c7c45677f64213ff563fd68bce6d14a9f. This reverts commit 7e64680a65e4dc2c755e79e55f43f7596b31e1d9. This reverts commit 8d1e38a2816e113121f2355a5827b0a681a3d41b. This reverts commit 591188195c60bb0bba4019d76547ece5386496ac. This reverts commit 973fe0cb79c9ef508778c7a2bb98bdfebeb70dfe. This reverts commit 4d08c9ad609d47c16d03ae3d5230c14d8d52f49e. This reverts commit 3dce92e58090de98e9f1470d9ba69da1f1b590db. This reverts commit 6b5e232c5c4a8e3d2d1c442c87ab03cc40350978. This reverts commit b1286c62c90167d6f6b6c17ad9bdfbb5d0254f60.
2023-04-12 12:30:56 +02:00
if (am->ucm_context.ucm_devices) {
pa_alsa_ucm_add_ports_combination(am->output.ports, &am->ucm_context,
true, impl->ports, np, NULL);
}
}
device_enable(impl, am, &am->output);
}
}
if (np->input_mappings) {
PA_IDXSET_FOREACH(am, np->input_mappings, idx) {
if (impl->use_ucm) {
/* Update ports priorities */
Revert "alsa: UCM conflicting devices changes" If needs some more work This reverts commit aae770f18b7f6c94195fb3f2e2a61308029086af. This reverts commit 2216956e05689079db5ec05f793158e6479060da. This reverts commit 1d3c4501eff3392e0cd858d980c742c7f88c95e7. This reverts commit c7ca02460761273de0d1a8432c5defaafed43664. This reverts commit 8e4a5f5d5ed3d8b3081956f451c85c23aa74fc15. This reverts commit 8b20675ad12bc9daea84f6d4314ec12fccf5882c. This reverts commit ebe6f8b51a31ff7bd71d560991a12283f710b937. This reverts commit 4b97da1277b709e9749b568319f5eb403ff0f362. This reverts commit 7c46a656c734d4688cac229cd9d49af5ad0609fa. This reverts commit 96ed03e1fc5b1f0a287069c42031d7961239c896. This reverts commit 43770c533c71bfaf2c7e82d47d50bfd0533b53e0. This reverts commit ccb2fce05452185858d22de06d659452702f05d8. This reverts commit 93d0ddc8b327f9aaf11d71193f1d50d64bf1144b. This reverts commit 815e8c8fa09a88269b674786a152fe36849b933d. This reverts commit 97b99a0f7c2dd059c08c6ccd143cc1be06111ad1. This reverts commit 7e4d87402b17cd881e9140c37285b175c622ea24. This reverts commit 168b3ee22c1545b738634c4ae89ae4afbaf68cc4. This reverts commit 5128cdba319a2ced0f2e559ad594b8cbeb97a072. This reverts commit 31cde774c54931109da99bc452c858a208d94d6c. This reverts commit b02c8ba153f21459f1a120d4c1ca1bde6a98f4fb. This reverts commit 68ac72d09857901a33be22922669fc8674a4ab9c. This reverts commit 96cfc9bc1ac63414e7b3a6b928be5a29c03e1431. This reverts commit e2d642a20b1bc4e70f7273ddfc3da376a342e10f. This reverts commit 80fc80c34348bdc247bb1a12bcc174db772208a4. This reverts commit 3a68905c7c45677f64213ff563fd68bce6d14a9f. This reverts commit 7e64680a65e4dc2c755e79e55f43f7596b31e1d9. This reverts commit 8d1e38a2816e113121f2355a5827b0a681a3d41b. This reverts commit 591188195c60bb0bba4019d76547ece5386496ac. This reverts commit 973fe0cb79c9ef508778c7a2bb98bdfebeb70dfe. This reverts commit 4d08c9ad609d47c16d03ae3d5230c14d8d52f49e. This reverts commit 3dce92e58090de98e9f1470d9ba69da1f1b590db. This reverts commit 6b5e232c5c4a8e3d2d1c442c87ab03cc40350978. This reverts commit b1286c62c90167d6f6b6c17ad9bdfbb5d0254f60.
2023-04-12 12:30:56 +02:00
if (am->ucm_context.ucm_devices) {
pa_alsa_ucm_add_ports_combination(am->input.ports, &am->ucm_context,
false, impl->ports, np, NULL);
}
}
device_enable(impl, am, &am->input);
}
}
if (op)
op->profile.flags &= ~(ACP_PROFILE_ACTIVE | ACP_PROFILE_SAVE);
np->profile.flags |= ACP_PROFILE_ACTIVE | flags;
impl->card.active_profile_index = new_index;
if (impl->events && impl->events->profile_changed)
impl->events->profile_changed(impl->user_data, old_index,
new_index);
return 0;
}
static void prune_singleton_availability_groups(pa_hashmap *ports) {
pa_device_port *p;
pa_hashmap *group_counts;
void *state, *count;
const char *group;
/* Collect groups and erase those that don't have more than 1 path */
group_counts = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
PA_HASHMAP_FOREACH(p, ports, state) {
if (p->availability_group) {
count = pa_hashmap_get(group_counts, p->availability_group);
pa_hashmap_remove(group_counts, p->availability_group);
pa_hashmap_put(group_counts, p->availability_group, PA_UINT_TO_PTR(PA_PTR_TO_UINT(count) + 1));
}
}
/* Now we have an availability_group -> count map, let's drop all groups
* that have only one member */
PA_HASHMAP_FOREACH_KV(group, count, group_counts, state) {
if (count == PA_UINT_TO_PTR(1))
pa_hashmap_remove(group_counts, group);
}
PA_HASHMAP_FOREACH(p, ports, state) {
if (p->availability_group && !pa_hashmap_get(group_counts, p->availability_group)) {
pa_log_debug("Pruned singleton availability group %s from port %s", p->availability_group, p->name);
pa_xfree(p->availability_group);
p->availability_group = NULL;
}
}
pa_hashmap_free(group_counts);
}
static const char *acp_dict_lookup(const struct acp_dict *dict, const char *key)
{
const struct acp_dict_item *it;
acp_dict_for_each(it, dict) {
if (spa_streq(key, it->key))
return it->value;
}
return NULL;
}
struct acp_card *acp_card_new(uint32_t index, const struct acp_dict *props)
{
pa_card *impl;
struct acp_card *card;
const char *s, *profile_set = NULL, *profile = NULL;
char device_id[16];
uint32_t profile_index;
2020-09-30 12:56:05 +02:00
int res;
impl = calloc(1, sizeof(*impl));
if (impl == NULL)
return NULL;
pa_alsa_refcnt_inc();
snprintf(device_id, sizeof(device_id), "%d", index);
impl->proplist = pa_proplist_new_dict(props);
card = &impl->card;
card->index = index;
card->active_profile_index = ACP_INVALID_INDEX;
impl->use_ucm = true;
impl->auto_profile = true;
impl->auto_port = true;
impl->ignore_dB = false;
impl->rate = DEFAULT_RATE;
impl->pro_channels = 64;
if (props) {
if ((s = acp_dict_lookup(props, "api.alsa.use-ucm")) != NULL)
impl->use_ucm = spa_atob(s);
if ((s = acp_dict_lookup(props, "api.alsa.soft-mixer")) != NULL)
impl->soft_mixer = spa_atob(s);
if ((s = acp_dict_lookup(props, "api.alsa.ignore-dB")) != NULL)
impl->ignore_dB = spa_atob(s);
if ((s = acp_dict_lookup(props, "device.profile-set")) != NULL)
profile_set = s;
if ((s = acp_dict_lookup(props, "device.profile")) != NULL)
profile = s;
if ((s = acp_dict_lookup(props, "api.acp.auto-profile")) != NULL)
impl->auto_profile = spa_atob(s);
if ((s = acp_dict_lookup(props, "api.acp.auto-port")) != NULL)
impl->auto_port = spa_atob(s);
if ((s = acp_dict_lookup(props, "api.acp.probe-rate")) != NULL)
impl->rate = atoi(s);
if ((s = acp_dict_lookup(props, "api.acp.pro-channels")) != NULL)
impl->pro_channels = atoi(s);
}
impl->ucm.default_sample_spec.format = PA_SAMPLE_S16NE;
impl->ucm.default_sample_spec.rate = impl->rate;
impl->ucm.default_sample_spec.channels = 2;
pa_channel_map_init_extend(&impl->ucm.default_channel_map,
impl->ucm.default_sample_spec.channels, PA_CHANNEL_MAP_ALSA);
impl->ucm.default_n_fragments = 4;
impl->ucm.default_fragment_size_msec = 25;
impl->ucm.mixers = pa_hashmap_new_full(pa_idxset_string_hash_func,
pa_idxset_string_compare_func,
pa_xfree, (pa_free_cb_t) pa_alsa_mixer_free);
impl->profiles = pa_hashmap_new_full(pa_idxset_string_hash_func,
pa_idxset_string_compare_func, NULL,
(pa_free_cb_t) profile_free);
impl->ports = pa_hashmap_new_full(pa_idxset_string_hash_func,
pa_idxset_string_compare_func, NULL,
(pa_free_cb_t) port_free);
snd_config_update_free_global();
2020-09-30 12:56:05 +02:00
res = impl->use_ucm ? pa_alsa_ucm_query_profiles(&impl->ucm, card->index) : -1;
if (res == -PA_ALSA_ERR_UCM_LINKED) {
res = -EEXIST;
2020-09-30 12:56:05 +02:00
goto error;
}
if (res == 0) {
pa_log_info("Found UCM profiles");
impl->profile_set = pa_alsa_ucm_add_profile_set(&impl->ucm, &impl->ucm.default_channel_map);
} else {
impl->use_ucm = false;
impl->profile_set = pa_alsa_profile_set_new(profile_set, &impl->ucm.default_channel_map);
}
2020-09-30 12:56:05 +02:00
if (impl->profile_set == NULL) {
res = -ENOTSUP;
goto error;
}
impl->profile_set->ignore_dB = impl->ignore_dB;
pa_alsa_profile_set_probe(impl->profile_set, impl->ucm.mixers,
device_id,
&impl->ucm.default_sample_spec,
impl->ucm.default_n_fragments,
impl->ucm.default_fragment_size_msec);
pa_alsa_init_proplist_card(NULL, impl->proplist, impl->card.index);
pa_proplist_sets(impl->proplist, PA_PROP_DEVICE_STRING, device_id);
pa_alsa_init_description(impl->proplist, NULL);
add_profiles(impl);
prune_singleton_availability_groups(impl->ports);
card->n_profiles = pa_dynarray_size(&impl->out.profiles);
card->profiles = impl->out.profiles.array.data;
card->n_ports = pa_dynarray_size(&impl->out.ports);
card->ports = impl->out.ports.array.data;
card->n_devices = pa_dynarray_size(&impl->out.devices);
card->devices = impl->out.devices.array.data;
pa_proplist_as_dict(impl->proplist, &card->props);
init_jacks(impl);
if (!impl->auto_profile && profile == NULL)
profile = "off";
profile_index = acp_card_find_best_profile_index(&impl->card, profile);
acp_card_set_profile(&impl->card, profile_index, 0);
init_eld_ctls(impl);
return &impl->card;
2020-09-30 12:56:05 +02:00
error:
pa_alsa_refcnt_dec();
free(impl);
errno = -res;
return NULL;
}
void acp_card_add_listener(struct acp_card *card,
const struct acp_card_events *events, void *user_data)
{
pa_card *impl = (pa_card *)card;
impl->events = events;
impl->user_data = user_data;
}
void acp_card_destroy(struct acp_card *card)
{
2020-09-30 12:56:05 +02:00
pa_card *impl = (pa_card *)card;
2020-11-09 15:01:07 +01:00
if (impl->profiles)
pa_hashmap_free(impl->profiles);
if (impl->ports)
pa_hashmap_free(impl->ports);
pa_dynarray_clear(&impl->out.devices);
pa_dynarray_clear(&impl->out.profiles);
pa_dynarray_clear(&impl->out.ports);
if (impl->ucm.mixers)
pa_hashmap_free(impl->ucm.mixers);
if (impl->jacks)
pa_hashmap_free(impl->jacks);
if (impl->profile_set)
pa_alsa_profile_set_free(impl->profile_set);
pa_alsa_ucm_free(&impl->ucm);
pa_proplist_free(impl->proplist);
pa_alsa_refcnt_dec();
2020-09-30 12:56:05 +02:00
free(impl);
}
int acp_card_poll_descriptors_count(struct acp_card *card)
{
pa_card *impl = (pa_card *)card;
void *state;
pa_alsa_mixer *pm;
int n, count = 0;
PA_HASHMAP_FOREACH(pm, impl->ucm.mixers, state) {
if (!pm->used_for_poll)
continue;
n = snd_mixer_poll_descriptors_count(pm->mixer_handle);
if (n < 0)
return n;
count += n;
}
return count;
}
int acp_card_poll_descriptors(struct acp_card *card, struct pollfd *pfds, unsigned int space)
{
pa_card *impl = (pa_card *)card;
void *state;
pa_alsa_mixer *pm;
int n, count = 0;
PA_HASHMAP_FOREACH(pm, impl->ucm.mixers, state) {
if (!pm->used_for_poll)
continue;
n = snd_mixer_poll_descriptors(pm->mixer_handle, pfds, space);
if (n < 0)
return n;
if (space >= (unsigned int) n) {
count += n;
space -= n;
pfds += n;
} else
space = 0;
}
return count;
}
int acp_card_poll_descriptors_revents(struct acp_card *card, struct pollfd *pfds,
unsigned int nfds, unsigned short *revents)
{
unsigned int idx;
unsigned short res;
if (nfds == 0)
return -EINVAL;
res = 0;
for (idx = 0; idx < nfds; idx++, pfds++)
res |= pfds->revents & (POLLIN|POLLERR|POLLNVAL);
*revents = res;
return 0;
}
int acp_card_handle_events(struct acp_card *card)
{
pa_card *impl = (pa_card *)card;
void *state;
pa_alsa_mixer *pm;
int n, count = 0;
PA_HASHMAP_FOREACH(pm, impl->ucm.mixers, state) {
if (!pm->used_for_poll)
continue;
n = snd_mixer_handle_events(pm->mixer_handle);
if (n < 0)
return n;
count += n;
}
return count;
}
static void sync_mixer(pa_alsa_device *d, pa_device_port *port)
{
pa_alsa_setting *setting = NULL;
if (!d->mixer_path)
return;
/* port may be NULL, because if we use a synthesized mixer path, then the
* sink has no ports. */
if (port && !d->ucm_context) {
pa_alsa_port_data *data;
data = PA_DEVICE_PORT_DATA(port);
setting = data->setting;
}
if (d->mixer_handle)
pa_alsa_path_select(d->mixer_path, setting, d->mixer_handle, d->muted);
if (d->set_mute)
d->set_mute(d, d->muted);
if (d->set_volume)
d->set_volume(d, &d->real_volume);
}
uint32_t acp_device_find_best_port_index(struct acp_device *dev, const char *name)
{
uint32_t i;
uint32_t best, best2, best3;
struct acp_port **ports = dev->ports;
best = best2 = best3 = ACP_INVALID_INDEX;
for (i = 0; i < dev->n_ports; i++) {
struct acp_port *p = ports[i];
if (SPA_FLAG_IS_SET(p->flags, ACP_PORT_HIDDEN))
continue;
if (name) {
if (spa_streq(name, p->name))
best = i;
2021-01-27 13:04:24 +01:00
} else if (p->available == ACP_AVAILABLE_YES) {
if (best == ACP_INVALID_INDEX || p->priority > ports[best]->priority)
best = i;
} else if (p->available != ACP_AVAILABLE_NO) {
if (best2 == ACP_INVALID_INDEX || p->priority > ports[best2]->priority)
best2 = i;
} else {
if (best3 == ACP_INVALID_INDEX || p->priority > ports[best3]->priority)
best3 = i;
}
}
if (best == ACP_INVALID_INDEX)
best = best2;
if (best == ACP_INVALID_INDEX)
best = best3;
if (best == ACP_INVALID_INDEX)
best = 0;
if (best < dev->n_ports)
return ports[best]->index;
else
return ACP_INVALID_INDEX;
}
int acp_device_set_port(struct acp_device *dev, uint32_t port_index, uint32_t flags)
{
pa_alsa_device *d = (pa_alsa_device*)dev;
pa_card *impl = d->card;
pa_device_port *p, *old = d->active_port;
int res;
if (port_index >= impl->card.n_ports)
return -EINVAL;
p = (pa_device_port*)impl->card.ports[port_index];
if (!pa_hashmap_get(d->ports, p->name))
return -EINVAL;
if (SPA_FLAG_IS_SET(p->port.flags, ACP_PORT_HIDDEN))
return -EINVAL;
p->port.flags = ACP_PORT_ACTIVE | flags;
if (p == old)
return 0;
if (old)
old->port.flags &= ~(ACP_PORT_ACTIVE | ACP_PORT_SAVE);
d->active_port = p;
if (impl->use_ucm) {
pa_alsa_ucm_port_data *data;
data = PA_DEVICE_PORT_DATA(p);
d->mixer_path = data->path;
mixer_volume_init(impl, d);
sync_mixer(d, p);
Revert "alsa: UCM conflicting devices changes" If needs some more work This reverts commit aae770f18b7f6c94195fb3f2e2a61308029086af. This reverts commit 2216956e05689079db5ec05f793158e6479060da. This reverts commit 1d3c4501eff3392e0cd858d980c742c7f88c95e7. This reverts commit c7ca02460761273de0d1a8432c5defaafed43664. This reverts commit 8e4a5f5d5ed3d8b3081956f451c85c23aa74fc15. This reverts commit 8b20675ad12bc9daea84f6d4314ec12fccf5882c. This reverts commit ebe6f8b51a31ff7bd71d560991a12283f710b937. This reverts commit 4b97da1277b709e9749b568319f5eb403ff0f362. This reverts commit 7c46a656c734d4688cac229cd9d49af5ad0609fa. This reverts commit 96ed03e1fc5b1f0a287069c42031d7961239c896. This reverts commit 43770c533c71bfaf2c7e82d47d50bfd0533b53e0. This reverts commit ccb2fce05452185858d22de06d659452702f05d8. This reverts commit 93d0ddc8b327f9aaf11d71193f1d50d64bf1144b. This reverts commit 815e8c8fa09a88269b674786a152fe36849b933d. This reverts commit 97b99a0f7c2dd059c08c6ccd143cc1be06111ad1. This reverts commit 7e4d87402b17cd881e9140c37285b175c622ea24. This reverts commit 168b3ee22c1545b738634c4ae89ae4afbaf68cc4. This reverts commit 5128cdba319a2ced0f2e559ad594b8cbeb97a072. This reverts commit 31cde774c54931109da99bc452c858a208d94d6c. This reverts commit b02c8ba153f21459f1a120d4c1ca1bde6a98f4fb. This reverts commit 68ac72d09857901a33be22922669fc8674a4ab9c. This reverts commit 96cfc9bc1ac63414e7b3a6b928be5a29c03e1431. This reverts commit e2d642a20b1bc4e70f7273ddfc3da376a342e10f. This reverts commit 80fc80c34348bdc247bb1a12bcc174db772208a4. This reverts commit 3a68905c7c45677f64213ff563fd68bce6d14a9f. This reverts commit 7e64680a65e4dc2c755e79e55f43f7596b31e1d9. This reverts commit 8d1e38a2816e113121f2355a5827b0a681a3d41b. This reverts commit 591188195c60bb0bba4019d76547ece5386496ac. This reverts commit 973fe0cb79c9ef508778c7a2bb98bdfebeb70dfe. This reverts commit 4d08c9ad609d47c16d03ae3d5230c14d8d52f49e. This reverts commit 3dce92e58090de98e9f1470d9ba69da1f1b590db. This reverts commit 6b5e232c5c4a8e3d2d1c442c87ab03cc40350978. This reverts commit b1286c62c90167d6f6b6c17ad9bdfbb5d0254f60.
2023-04-12 12:30:56 +02:00
res = pa_alsa_ucm_set_port(d->ucm_context, p,
dev->direction == ACP_DIRECTION_PLAYBACK);
} else {
pa_alsa_port_data *data;
data = PA_DEVICE_PORT_DATA(p);
d->mixer_path = data->path;
mixer_volume_init(impl, d);
sync_mixer(d, p);
res = 0;
#if 0
if (data->suspend_when_unavailable && p->available == PA_AVAILABLE_NO)
pa_sink_suspend(s, true, PA_SUSPEND_UNAVAILABLE);
else
pa_sink_suspend(s, false, PA_SUSPEND_UNAVAILABLE);
#endif
}
if (impl->events && impl->events->port_changed)
impl->events->port_changed(impl->user_data,
old ? old->port.index : 0, p->port.index);
return res;
}
int acp_device_set_volume(struct acp_device *dev, const float *volume, uint32_t n_volume)
{
pa_alsa_device *d = (pa_alsa_device*)dev;
pa_card *impl = d->card;
uint32_t i;
pa_cvolume v, old_volume;
if (n_volume == 0)
return -EINVAL;
old_volume = d->real_volume;
v.channels = d->mapping->channel_map.channels;
for (i = 0; i < v.channels; i++)
2021-05-20 11:20:19 +10:00
v.values[i] = pa_sw_volume_from_linear(volume[i % n_volume]);
pa_log_info("Set %s volume: min:%d max:%d",
d->set_volume ? "hardware" : "software",
pa_cvolume_min(&v), pa_cvolume_max(&v));
for (i = 0; i < v.channels; i++)
pa_log_debug(" %d: %d", i, v.values[i]);
if (d->set_volume) {
d->set_volume(d, &v);
} else {
d->real_volume = v;
d->soft_volume = v;
}
if (!pa_cvolume_equal(&d->real_volume, &old_volume))
if (impl->events && impl->events->volume_changed)
impl->events->volume_changed(impl->user_data, dev);
return 0;
}
static int get_volume(pa_cvolume *v, float *volume, uint32_t n_volume)
{
uint32_t i;
if (v->channels == 0)
return -EIO;
for (i = 0; i < n_volume; i++)
volume[i] = pa_sw_volume_to_linear(v->values[i % v->channels]);
return 0;
}
int acp_device_get_soft_volume(struct acp_device *dev, float *volume, uint32_t n_volume)
{
pa_alsa_device *d = (pa_alsa_device*)dev;
return get_volume(&d->soft_volume, volume, n_volume);
}
int acp_device_get_volume(struct acp_device *dev, float *volume, uint32_t n_volume)
{
pa_alsa_device *d = (pa_alsa_device*)dev;
return get_volume(&d->real_volume, volume, n_volume);
}
int acp_device_set_mute(struct acp_device *dev, bool mute)
{
pa_alsa_device *d = (pa_alsa_device*)dev;
pa_card *impl = d->card;
bool old_muted = d->muted;
if (old_muted == mute)
return 0;
pa_log_info("Set %s mute: %d", d->set_mute ? "hardware" : "software", mute);
if (d->set_mute) {
d->set_mute(d, mute);
} else {
d->muted = mute;
}
if (old_muted != mute)
if (impl->events && impl->events->mute_changed)
impl->events->mute_changed(impl->user_data, dev);
return 0;
}
int acp_device_get_mute(struct acp_device *dev, bool *mute)
{
pa_alsa_device *d = (pa_alsa_device*)dev;
*mute = d->muted;
return 0;
}
void acp_set_log_func(acp_log_func func, void *data)
{
_acp_log_func = func;
_acp_log_data = data;
}
void acp_set_log_level(int level)
{
_acp_log_level = level;
}