summaryrefslogtreecommitdiffstats
path: root/src/modules/alsa
diff options
context:
space:
mode:
Diffstat (limited to 'src/modules/alsa')
-rw-r--r--src/modules/alsa/alsa-sink.c90
-rw-r--r--src/modules/alsa/alsa-source.c90
-rw-r--r--src/modules/alsa/alsa-util.c261
-rw-r--r--src/modules/alsa/alsa-util.h5
-rw-r--r--src/modules/alsa/module-alsa-card.c20
-rw-r--r--src/modules/alsa/module-alsa-sink.c2
-rw-r--r--src/modules/alsa/module-alsa-source.c2
7 files changed, 378 insertions, 92 deletions
diff --git a/src/modules/alsa/alsa-sink.c b/src/modules/alsa/alsa-sink.c
index 41e8b477..59a5ca75 100644
--- a/src/modules/alsa/alsa-sink.c
+++ b/src/modules/alsa/alsa-sink.c
@@ -116,6 +116,8 @@ struct userdata {
pa_reserve_wrapper *reserve;
pa_hook_slot *reserve_slot;
+ pa_reserve_monitor_wrapper *monitor;
+ pa_hook_slot *monitor_slot;
};
static void userdata_free(struct userdata *u);
@@ -124,7 +126,7 @@ static pa_hook_result_t reserve_cb(pa_reserve_wrapper *r, void *forced, struct u
pa_assert(r);
pa_assert(u);
- if (pa_sink_suspend(u->sink, TRUE) < 0)
+ if (pa_sink_suspend(u->sink, TRUE, PA_SUSPEND_APPLICATION) < 0)
return PA_HOOK_CANCEL;
return PA_HOOK_OK;
@@ -185,6 +187,57 @@ static int reserve_init(struct userdata *u, const char *dname) {
return 0;
}
+static pa_hook_result_t monitor_cb(pa_reserve_monitor_wrapper *w, void* busy, struct userdata *u) {
+ pa_bool_t b;
+
+ pa_assert(w);
+ pa_assert(u);
+
+ b = PA_PTR_TO_UINT(busy) && !u->reserve;
+
+ pa_sink_suspend(u->sink, b, PA_SUSPEND_APPLICATION);
+ return PA_HOOK_OK;
+}
+
+static void monitor_done(struct userdata *u) {
+ pa_assert(u);
+
+ if (u->monitor_slot) {
+ pa_hook_slot_free(u->monitor_slot);
+ u->monitor_slot = NULL;
+ }
+
+ if (u->monitor) {
+ pa_reserve_monitor_wrapper_unref(u->monitor);
+ u->monitor = NULL;
+ }
+}
+
+static int reserve_monitor_init(struct userdata *u, const char *dname) {
+ char *rname;
+
+ pa_assert(u);
+ pa_assert(dname);
+
+ if (pa_in_system_mode())
+ return 0;
+
+ /* We are resuming, try to lock the device */
+ if (!(rname = pa_alsa_get_reserve_name(dname)))
+ return 0;
+
+ u->monitor = pa_reserve_monitor_wrapper_get(u->core, rname);
+ pa_xfree(rname);
+
+ if (!(u->monitor))
+ return -1;
+
+ pa_assert(!u->monitor_slot);
+ u->monitor_slot = pa_hook_connect(pa_reserve_monitor_wrapper_hook(u->monitor), PA_HOOK_NORMAL, (pa_hook_cb_t) monitor_cb, u);
+
+ return 0;
+}
+
static void fix_min_sleep_wakeup(struct userdata *u) {
size_t max_use, max_use_2;
@@ -473,7 +526,7 @@ static int mmap_write(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polle
u->since_start += frames * u->frame_size;
#ifdef DEBUG_TIMING
- pa_log_debug("Wrote %lu bytes", (unsigned long) (frames * u->frame_size));
+ pa_log_debug("Wrote %lu bytes (of possible %lu bytes)", (unsigned long) (frames * u->frame_size), (unsigned long) n_bytes);
#endif
if ((size_t) frames * u->frame_size >= n_bytes)
@@ -1146,7 +1199,7 @@ fail:
static void sink_get_mute_cb(pa_sink *s) {
struct userdata *u = s->userdata;
- int err, sw;
+ int err, sw = 0;
pa_assert(u);
pa_assert(u->mixer_elem);
@@ -1508,6 +1561,7 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca
size_t frame_size;
pa_bool_t use_mmap = TRUE, b, use_tsched = TRUE, d, ignore_dB = FALSE;
pa_sink_new_data data;
+ char *control_device = NULL;
pa_assert(m);
pa_assert(ma);
@@ -1579,9 +1633,14 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca
pa_rtclock_usec(),
TRUE);
- if (reserve_init(u, pa_modargs_get_value(
- ma, "device_id",
- pa_modargs_get_value(ma, "device", DEFAULT_DEVICE))) < 0)
+ dev_id = pa_modargs_get_value(
+ ma, "device_id",
+ pa_modargs_get_value(ma, "device", DEFAULT_DEVICE));
+
+ if (reserve_init(u, dev_id) < 0)
+ goto fail;
+
+ if (reserve_monitor_init(u, dev_id) < 0)
goto fail;
b = use_mmap;
@@ -1664,7 +1723,7 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca
/* ALSA might tweak the sample spec, so recalculate the frame size */
frame_size = pa_frame_size(&ss);
- pa_alsa_find_mixer_and_elem(u->pcm_handle, &u->mixer_handle, &u->mixer_elem, pa_modargs_get_value(ma, "control", NULL), profile);
+ pa_alsa_find_mixer_and_elem(u->pcm_handle, &control_device, &u->mixer_handle, &u->mixer_elem, pa_modargs_get_value(ma, "control", NULL), profile);
pa_sink_new_data_init(&data);
data.driver = driver;
@@ -1687,6 +1746,17 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca
pa_alsa_init_description(data.proplist);
+ if (control_device) {
+ pa_alsa_init_proplist_ctl(data.proplist, control_device);
+ pa_xfree(control_device);
+ }
+
+ if (pa_modargs_get_proplist(ma, "sink_properties", data.proplist, PA_UPDATE_REPLACE) < 0) {
+ pa_log("Invalid properties");
+ pa_sink_new_data_done(&data);
+ goto fail;
+ }
+
u->sink = pa_sink_new(m->core, &data, PA_SINK_HARDWARE|PA_SINK_LATENCY|(u->use_tsched ? PA_SINK_DYNAMIC_LATENCY : 0));
pa_sink_new_data_done(&data);
@@ -1730,7 +1800,7 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca
pa_log_info("Time scheduling watermark is %0.2fms",
(double) pa_bytes_to_usec(u->tsched_watermark, &ss) / PA_USEC_PER_MSEC);
} else
- u->sink->fixed_latency = pa_bytes_to_usec(u->hwbuf_size, &ss);
+ pa_sink_set_fixed_latency(u->sink, pa_bytes_to_usec(u->hwbuf_size, &ss));
reserve_update(u);
@@ -1770,7 +1840,8 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca
fail:
- userdata_free(u);
+ if (u)
+ userdata_free(u);
return NULL;
}
@@ -1815,6 +1886,7 @@ static void userdata_free(struct userdata *u) {
pa_smoother_free(u->smoother);
reserve_done(u);
+ monitor_done(u);
pa_xfree(u->device_name);
pa_xfree(u);
diff --git a/src/modules/alsa/alsa-source.c b/src/modules/alsa/alsa-source.c
index 843f70bb..c176309e 100644
--- a/src/modules/alsa/alsa-source.c
+++ b/src/modules/alsa/alsa-source.c
@@ -114,6 +114,8 @@ struct userdata {
pa_reserve_wrapper *reserve;
pa_hook_slot *reserve_slot;
+ pa_reserve_monitor_wrapper *monitor;
+ pa_hook_slot *monitor_slot;
};
static void userdata_free(struct userdata *u);
@@ -122,7 +124,7 @@ static pa_hook_result_t reserve_cb(pa_reserve_wrapper *r, void *forced, struct u
pa_assert(r);
pa_assert(u);
- if (pa_source_suspend(u->source, TRUE) < 0)
+ if (pa_source_suspend(u->source, TRUE, PA_SUSPEND_APPLICATION) < 0)
return PA_HOOK_CANCEL;
return PA_HOOK_OK;
@@ -183,6 +185,57 @@ static int reserve_init(struct userdata *u, const char *dname) {
return 0;
}
+static pa_hook_result_t monitor_cb(pa_reserve_monitor_wrapper *w, void* busy, struct userdata *u) {
+ pa_bool_t b;
+
+ pa_assert(w);
+ pa_assert(u);
+
+ b = PA_PTR_TO_UINT(busy) && !u->reserve;
+
+ pa_source_suspend(u->source, b, PA_SUSPEND_APPLICATION);
+ return PA_HOOK_OK;
+}
+
+static void monitor_done(struct userdata *u) {
+ pa_assert(u);
+
+ if (u->monitor_slot) {
+ pa_hook_slot_free(u->monitor_slot);
+ u->monitor_slot = NULL;
+ }
+
+ if (u->monitor) {
+ pa_reserve_monitor_wrapper_unref(u->monitor);
+ u->monitor = NULL;
+ }
+}
+
+static int reserve_monitor_init(struct userdata *u, const char *dname) {
+ char *rname;
+
+ pa_assert(u);
+ pa_assert(dname);
+
+ if (pa_in_system_mode())
+ return 0;
+
+ /* We are resuming, try to lock the device */
+ if (!(rname = pa_alsa_get_reserve_name(dname)))
+ return 0;
+
+ u->monitor = pa_reserve_monitor_wrapper_get(u->core, rname);
+ pa_xfree(rname);
+
+ if (!(u->monitor))
+ return -1;
+
+ pa_assert(!u->monitor_slot);
+ u->monitor_slot = pa_hook_connect(pa_reserve_monitor_wrapper_hook(u->monitor), PA_HOOK_NORMAL, (pa_hook_cb_t) monitor_cb, u);
+
+ return 0;
+}
+
static void fix_min_sleep_wakeup(struct userdata *u) {
size_t max_use, max_use_2;
pa_assert(u);
@@ -455,7 +508,7 @@ static int mmap_read(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polled
u->read_count += frames * u->frame_size;
#ifdef DEBUG_TIMING
- pa_log_debug("Read %lu bytes", (unsigned long) (frames * u->frame_size));
+ pa_log_debug("Read %lu bytes (of possible %lu bytes)", (unsigned long) (frames * u->frame_size), (unsigned long) n_bytes);
#endif
if ((size_t) frames * u->frame_size >= n_bytes)
@@ -1105,7 +1158,7 @@ fail:
static void source_get_mute_cb(pa_source *s) {
struct userdata *u = s->userdata;
- int err, sw;
+ int err, sw = 0;
pa_assert(u);
pa_assert(u->mixer_elem);
@@ -1366,6 +1419,7 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p
size_t frame_size;
pa_bool_t use_mmap = TRUE, b, use_tsched = TRUE, d, ignore_dB = FALSE;
pa_source_new_data data;
+ char *control_device = NULL;
pa_assert(m);
pa_assert(ma);
@@ -1437,9 +1491,14 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p
pa_rtclock_usec(),
FALSE);
- if (reserve_init(u, pa_modargs_get_value(
- ma, "device_id",
- pa_modargs_get_value(ma, "device", DEFAULT_DEVICE))) < 0)
+ dev_id = pa_modargs_get_value(
+ ma, "device_id",
+ pa_modargs_get_value(ma, "device", DEFAULT_DEVICE));
+
+ if (reserve_init(u, dev_id) < 0)
+ goto fail;
+
+ if (reserve_monitor_init(u, dev_id) < 0)
goto fail;
b = use_mmap;
@@ -1519,7 +1578,7 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p
/* ALSA might tweak the sample spec, so recalculate the frame size */
frame_size = pa_frame_size(&ss);
- pa_alsa_find_mixer_and_elem(u->pcm_handle, &u->mixer_handle, &u->mixer_elem, pa_modargs_get_value(ma, "control", NULL), profile);
+ pa_alsa_find_mixer_and_elem(u->pcm_handle, &control_device, &u->mixer_handle, &u->mixer_elem, pa_modargs_get_value(ma, "control", NULL), profile);
pa_source_new_data_init(&data);
data.driver = driver;
@@ -1542,6 +1601,17 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p
pa_alsa_init_description(data.proplist);
+ if (control_device) {
+ pa_alsa_init_proplist_ctl(data.proplist, control_device);
+ pa_xfree(control_device);
+ }
+
+ if (pa_modargs_get_proplist(ma, "source_properties", data.proplist, PA_UPDATE_REPLACE) < 0) {
+ pa_log("Invalid properties");
+ pa_source_new_data_done(&data);
+ goto fail;
+ }
+
u->source = pa_source_new(m->core, &data, PA_SOURCE_HARDWARE|PA_SOURCE_LATENCY|(u->use_tsched ? PA_SOURCE_DYNAMIC_LATENCY : 0));
pa_source_new_data_done(&data);
@@ -1582,7 +1652,7 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p
pa_log_info("Time scheduling watermark is %0.2fms",
(double) pa_bytes_to_usec(u->tsched_watermark, &ss) / PA_USEC_PER_MSEC);
} else
- u->source->fixed_latency = pa_bytes_to_usec(u->hwbuf_size, &ss);
+ pa_source_set_fixed_latency(u->source, pa_bytes_to_usec(u->hwbuf_size, &ss));
reserve_update(u);
@@ -1621,7 +1691,8 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p
fail:
- userdata_free(u);
+ if (u)
+ userdata_free(u);
return NULL;
}
@@ -1663,6 +1734,7 @@ static void userdata_free(struct userdata *u) {
pa_smoother_free(u->smoother);
reserve_done(u);
+ monitor_done(u);
pa_xfree(u->device_name);
pa_xfree(u);
diff --git a/src/modules/alsa/alsa-util.c b/src/modules/alsa/alsa-util.c
index 18d6880e..c03866cc 100644
--- a/src/modules/alsa/alsa-util.c
+++ b/src/modules/alsa/alsa-util.c
@@ -189,15 +189,6 @@ struct pa_alsa_fdlist *pa_alsa_fdlist_new(void) {
fdl = pa_xnew0(struct pa_alsa_fdlist, 1);
- fdl->num_fds = 0;
- fdl->fds = NULL;
- fdl->work_fds = NULL;
- fdl->mixer = NULL;
- fdl->m = NULL;
- fdl->defer = NULL;
- fdl->ios = NULL;
- fdl->polled = FALSE;
-
return fdl;
}
@@ -281,6 +272,11 @@ static int set_format(snd_pcm_t *pcm_handle, snd_pcm_hw_params_t *hwparams, pa_s
if ((ret = snd_pcm_hw_params_set_format(pcm_handle, hwparams, format_trans[*f])) >= 0)
return ret;
+ pa_log_debug("snd_pcm_hw_params_set_format(%s) failed: %s",
+ snd_pcm_format_description(format_trans[*f]),
+ pa_alsa_strerror(ret));
+
+
if (*f == PA_SAMPLE_FLOAT32BE)
*f = PA_SAMPLE_FLOAT32LE;
else if (*f == PA_SAMPLE_FLOAT32LE)
@@ -307,6 +303,10 @@ static int set_format(snd_pcm_t *pcm_handle, snd_pcm_hw_params_t *hwparams, pa_s
if ((ret = snd_pcm_hw_params_set_format(pcm_handle, hwparams, format_trans[*f])) >= 0)
return ret;
+ pa_log_debug("snd_pcm_hw_params_set_format(%s) failed: %s",
+ snd_pcm_format_description(format_trans[*f]),
+ pa_alsa_strerror(ret));
+
try_auto:
for (i = 0; try_order[i] != PA_SAMPLE_INVALID; i++) {
@@ -314,6 +314,10 @@ try_auto:
if ((ret = snd_pcm_hw_params_set_format(pcm_handle, hwparams, format_trans[*f])) >= 0)
return ret;
+
+ pa_log_debug("snd_pcm_hw_params_set_format(%s) failed: %s",
+ snd_pcm_format_description(format_trans[*f]),
+ pa_alsa_strerror(ret));
}
return -1;
@@ -334,7 +338,6 @@ int pa_alsa_set_hw_params(
int ret = -1;
snd_pcm_uframes_t _period_size = period_size ? *period_size : 0;
unsigned int _periods = periods ? *periods : 0;
- snd_pcm_uframes_t buffer_size;
unsigned int r = ss->rate;
unsigned int c = ss->channels;
pa_sample_format_t f = ss->format;
@@ -348,11 +351,15 @@ int pa_alsa_set_hw_params(
snd_pcm_hw_params_alloca(&hwparams);
- if ((ret = snd_pcm_hw_params_any(pcm_handle, hwparams)) < 0)
+ if ((ret = snd_pcm_hw_params_any(pcm_handle, hwparams)) < 0) {
+ pa_log_debug("snd_pcm_hw_params_any() failed: %s", pa_alsa_strerror(ret));
goto finish;
+ }
- if ((ret = snd_pcm_hw_params_set_rate_resample(pcm_handle, hwparams, 0)) < 0)
+ if ((ret = snd_pcm_hw_params_set_rate_resample(pcm_handle, hwparams, 0)) < 0) {
+ pa_log_debug("snd_pcm_hw_params_set_rate_resample() failed: %s", pa_alsa_strerror(ret));
goto finish;
+ }
if (_use_mmap) {
@@ -360,14 +367,18 @@ int pa_alsa_set_hw_params(
/* mmap() didn't work, fall back to interleaved */
- if ((ret = snd_pcm_hw_params_set_access(pcm_handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0)
+ if ((ret = snd_pcm_hw_params_set_access(pcm_handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) {
+ pa_log_debug("snd_pcm_hw_params_set_access() failed: %s", pa_alsa_strerror(ret));
goto finish;
+ }
_use_mmap = FALSE;
}
- } else if ((ret = snd_pcm_hw_params_set_access(pcm_handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0)
+ } else if ((ret = snd_pcm_hw_params_set_access(pcm_handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) {
+ pa_log_debug("snd_pcm_hw_params_set_access() failed: %s", pa_alsa_strerror(ret));
goto finish;
+ }
if (!_use_mmap)
_use_tsched = FALSE;
@@ -375,54 +386,70 @@ int pa_alsa_set_hw_params(
if ((ret = set_format(pcm_handle, hwparams, &f)) < 0)
goto finish;
- if ((ret = snd_pcm_hw_params_set_rate_near(pcm_handle, hwparams, &r, NULL)) < 0)
+ if ((ret = snd_pcm_hw_params_set_rate_near(pcm_handle, hwparams, &r, NULL)) < 0) {
+ pa_log_debug("snd_pcm_hw_params_set_rate_near() failed: %s", pa_alsa_strerror(ret));
goto finish;
+ }
if (require_exact_channel_number) {
- if ((ret = snd_pcm_hw_params_set_channels(pcm_handle, hwparams, c)) < 0)
+ if ((ret = snd_pcm_hw_params_set_channels(pcm_handle, hwparams, c)) < 0) {
+ pa_log_debug("snd_pcm_hw_params_set_channels() failed: %s", pa_alsa_strerror(ret));
goto finish;
+ }
} else {
- if ((ret = snd_pcm_hw_params_set_channels_near(pcm_handle, hwparams, &c)) < 0)
+ if ((ret = snd_pcm_hw_params_set_channels_near(pcm_handle, hwparams, &c)) < 0) {
+ pa_log_debug("snd_pcm_hw_params_set_channels_near() failed: %s", pa_alsa_strerror(ret));
goto finish;
+ }
}
- if ((ret = snd_pcm_hw_params_set_periods_integer(pcm_handle, hwparams)) < 0)
+ if ((ret = snd_pcm_hw_params_set_periods_integer(pcm_handle, hwparams)) < 0) {
+ pa_log_debug("snd_pcm_hw_params_set_periods_integer() failed: %s", pa_alsa_strerror(ret));
goto finish;
+ }
if (_period_size && tsched_size && _periods) {
+
/* Adjust the buffer sizes, if we didn't get the rate we were asking for */
_period_size = (snd_pcm_uframes_t) (((uint64_t) _period_size * r) / ss->rate);
tsched_size = (snd_pcm_uframes_t) (((uint64_t) tsched_size * r) / ss->rate);
if (_use_tsched) {
- _period_size = tsched_size;
- _periods = 1;
+ snd_pcm_uframes_t buffer_size;
pa_assert_se(snd_pcm_hw_params_get_buffer_size_max(hwparams, &buffer_size) == 0);
pa_log_debug("Maximum hw buffer size is %u ms", (unsigned) buffer_size * 1000 / r);
+
+ _period_size = tsched_size;
+ _periods = 1;
}
- buffer_size = _periods * _period_size;
+ if (_period_size > 0 && _periods > 0) {
+ snd_pcm_uframes_t buffer_size;
+
+ buffer_size = _periods * _period_size;
+
+ if ((ret = snd_pcm_hw_params_set_buffer_size_near(pcm_handle, hwparams, &buffer_size)) < 0)
+ pa_log_info("snd_pcm_hw_params_set_buffer_size_near() failed: %s", pa_alsa_strerror(ret));
+ }
if (_periods > 0) {
- /* First we pass 0 as direction to get exactly what we asked
- * for. That this is necessary is presumably a bug in ALSA */
+ /* First we pass 0 as direction to get exactly what we
+ * asked for. That this is necessary is presumably a bug
+ * in ALSA. All in all this is mostly a hint to ALSA, so
+ * we don't care if this fails. */
dir = 0;
- if ((ret = snd_pcm_hw_params_set_periods_near(pcm_handle, hwparams, &_periods, &dir)) < 0) {
+ if (snd_pcm_hw_params_set_periods_near(pcm_handle, hwparams, &_periods, &dir) < 0) {
dir = 1;
- if ((ret = snd_pcm_hw_params_set_periods_near(pcm_handle, hwparams, &_periods, &dir)) < 0) {
+ if (snd_pcm_hw_params_set_periods_near(pcm_handle, hwparams, &_periods, &dir) < 0) {
dir = -1;
if ((ret = snd_pcm_hw_params_set_periods_near(pcm_handle, hwparams, &_periods, &dir)) < 0)
- goto finish;
+ pa_log_info("snd_pcm_hw_params_set_periods_near() failed: %s", pa_alsa_strerror(ret));
}
}
}
-
- if (_period_size > 0)
- if ((ret = snd_pcm_hw_params_set_buffer_size_near(pcm_handle, hwparams, &buffer_size)) < 0)
- goto finish;
}
if ((ret = snd_pcm_hw_params(pcm_handle, hwparams)) < 0)
@@ -528,7 +555,7 @@ int pa_alsa_set_sw_params(snd_pcm_t *pcm, snd_pcm_uframes_t avail_min) {
static const struct pa_alsa_profile_info device_table[] = {
{{ 1, { PA_CHANNEL_POSITION_MONO }},
- "hw",
+ "hw", NULL,
N_("Analog Mono"),
"analog-mono",
1,
@@ -536,7 +563,7 @@ static const struct pa_alsa_profile_info device_table[] = {
"Capture", "Mic" },
{{ 2, { PA_CHANNEL_POSITION_LEFT, PA_CHANNEL_POSITION_RIGHT }},
- "front",
+ "front", "hw",
N_("Analog Stereo"),
"analog-stereo",
10,
@@ -544,7 +571,7 @@ static const struct pa_alsa_profile_info device_table[] = {
"Capture", "Mic" },
{{ 2, { PA_CHANNEL_POSITION_LEFT, PA_CHANNEL_POSITION_RIGHT }},
- "iec958",
+ "iec958", NULL,
N_("Digital Stereo (IEC958)"),
"iec958-stereo",
5,
@@ -552,7 +579,7 @@ static const struct pa_alsa_profile_info device_table[] = {
"IEC958 In", NULL },
{{ 2, { PA_CHANNEL_POSITION_LEFT, PA_CHANNEL_POSITION_RIGHT }},
- "hdmi",
+ "hdmi", NULL,
N_("Digital Stereo (HDMI)"),
"hdmi-stereo",
4,
@@ -561,7 +588,7 @@ static const struct pa_alsa_profile_info device_table[] = {
{{ 4, { PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT,
PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT }},
- "surround40",
+ "surround40", NULL,
N_("Analog Surround 4.0"),
"analog-surround-40",
7,
@@ -570,7 +597,7 @@ static const struct pa_alsa_profile_info device_table[] = {
{{ 4, { PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT,
PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT }},
- "a52",
+ "a52", NULL,
N_("Digital Surround 4.0 (IEC958/AC3)"),
"iec958-ac3-surround-40",
2,
@@ -580,7 +607,7 @@ static const struct pa_alsa_profile_info device_table[] = {
{{ 5, { PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT,
PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT,
PA_CHANNEL_POSITION_LFE }},
- "surround41",
+ "surround41", NULL,
N_("Analog Surround 4.1"),
"analog-surround-41",
7,
@@ -590,7 +617,7 @@ static const struct pa_alsa_profile_info device_table[] = {
{{ 5, { PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT,
PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT,
PA_CHANNEL_POSITION_CENTER }},
- "surround50",
+ "surround50", NULL,
N_("Analog Surround 5.0"),
"analog-surround-50",
7,
@@ -600,7 +627,7 @@ static const struct pa_alsa_profile_info device_table[] = {
{{ 6, { PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT,
PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT,
PA_CHANNEL_POSITION_CENTER, PA_CHANNEL_POSITION_LFE }},
- "surround51",
+ "surround51", NULL,
N_("Analog Surround 5.1"),
"analog-surround-51",
8,
@@ -610,7 +637,7 @@ static const struct pa_alsa_profile_info device_table[] = {
{{ 6, { PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT,
PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT,
PA_CHANNEL_POSITION_FRONT_CENTER, PA_CHANNEL_POSITION_LFE}},
- "a52",
+ "a52", NULL,
N_("Digital Surround 5.1 (IEC958/AC3)"),
"iec958-ac3-surround-51",
3,
@@ -621,16 +648,72 @@ static const struct pa_alsa_profile_info device_table[] = {
PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT,
PA_CHANNEL_POSITION_CENTER, PA_CHANNEL_POSITION_LFE,
PA_CHANNEL_POSITION_SIDE_LEFT, PA_CHANNEL_POSITION_SIDE_RIGHT }},
- "surround71",
+ "surround71", NULL,
N_("Analog Surround 7.1"),
"analog-surround-71",
7,
"Master", "PCM",
"Capture", "Mic" },
- {{ 0, { 0 }}, NULL, NULL, NULL, 0, NULL, NULL, NULL, NULL }
+ {{ 0, { 0 }}, NULL, NULL, NULL, NULL, 0, NULL, NULL, NULL, NULL }
};
+static snd_pcm_t *open_by_device_string_with_fallback(
+ const char *prefix,
+ const char *prefix_fallback,
+ const char *dev_id,
+ char **dev,
+ pa_sample_spec *ss,
+ pa_channel_map* map,
+ int mode,
+ uint32_t *nfrags,
+ snd_pcm_uframes_t *period_size,
+ snd_pcm_uframes_t tsched_size,
+ pa_bool_t *use_mmap,
+ pa_bool_t *use_tsched,
+ pa_bool_t require_exact_channel_number) {
+
+ snd_pcm_t *pcm_handle;
+ char *d;
+
+ d = pa_sprintf_malloc("%s:%s", prefix, dev_id);
+
+ pcm_handle = pa_alsa_open_by_device_string(
+ d,
+ dev,
+ ss,
+ map,
+ mode,
+ nfrags,
+ period_size,
+ tsched_size,
+ use_mmap,
+ use_tsched,
+ require_exact_channel_number);
+ pa_xfree(d);
+
+ if (!pcm_handle && prefix_fallback) {
+
+ d = pa_sprintf_malloc("%s:%s", prefix_fallback, dev_id);
+
+ pcm_handle = pa_alsa_open_by_device_string(
+ d,
+ dev,
+ ss,
+ map,
+ mode,
+ nfrags,
+ period_size,
+ tsched_size,
+ use_mmap,
+ use_tsched,
+ require_exact_channel_number);
+ pa_xfree(d);
+ }
+
+ return pcm_handle;
+}
+
snd_pcm_t *pa_alsa_open_by_device_id_auto(
const char *dev_id,
char **dev,
@@ -671,14 +754,14 @@ snd_pcm_t *pa_alsa_open_by_device_id_auto(
pa_log_debug("Checking for %s (%s)", device_table[i].name, device_table[i].alsa_name);
- d = pa_sprintf_malloc("%s:%s", device_table[i].alsa_name, dev_id);
-
try_ss.channels = device_table[i].map.channels;
try_ss.rate = ss->rate;
try_ss.format = ss->format;
- pcm_handle = pa_alsa_open_by_device_string(
- d,
+ pcm_handle = open_by_device_string_with_fallback(
+ device_table[i].alsa_name,
+ device_table[i].alsa_name_fallback,
+ dev_id,
dev,
&try_ss,
map,
@@ -690,8 +773,6 @@ snd_pcm_t *pa_alsa_open_by_device_id_auto(
use_tsched,
TRUE);
- pa_xfree(d);
-
if (pcm_handle) {
*ss = try_ss;
@@ -703,6 +784,7 @@ snd_pcm_t *pa_alsa_open_by_device_id_auto(
return pcm_handle;
}
+
}
if (direction > 0) {
@@ -775,7 +857,6 @@ snd_pcm_t *pa_alsa_open_by_device_id_profile(
pa_bool_t *use_tsched,
const pa_alsa_profile_info *profile) {
- char *d;
snd_pcm_t *pcm_handle;
pa_sample_spec try_ss;
@@ -787,14 +868,14 @@ snd_pcm_t *pa_alsa_open_by_device_id_profile(
pa_assert(period_size);
pa_assert(profile);
- d = pa_sprintf_malloc("%s:%s", profile->alsa_name, dev_id);
-
try_ss.channels = profile->map.channels;
try_ss.rate = ss->rate;
try_ss.format = ss->format;
- pcm_handle = pa_alsa_open_by_device_string(
- d,
+ pcm_handle = open_by_device_string_with_fallback(
+ profile->alsa_name,
+ profile->alsa_name_fallback,
+ dev_id,
dev,
&try_ss,
map,
@@ -806,8 +887,6 @@ snd_pcm_t *pa_alsa_open_by_device_id_profile(
use_tsched,
TRUE);
- pa_xfree(d);
-
if (!pcm_handle)
return NULL;
@@ -860,6 +939,8 @@ snd_pcm_t *pa_alsa_open_by_device_string(
goto fail;
}
+ pa_log_debug("Managed to open %s", d);
+
if ((err = pa_alsa_set_hw_params(pcm_handle, ss, nfrags, period_size, tsched_size, use_mmap, use_tsched, require_exact_channel_number)) < 0) {
if (!reformat) {
@@ -928,26 +1009,25 @@ int pa_alsa_probe_profiles(
snd_pcm_t *pcm_i = NULL;
if (i->alsa_name) {
- char *id;
pa_sample_spec try_ss;
pa_channel_map try_map;
pa_log_debug("Checking for playback on %s (%s)", i->name, i->alsa_name);
- id = pa_sprintf_malloc("%s:%s", i->alsa_name, dev_id);
try_ss = *ss;
try_ss.channels = i->map.channels;
try_map = i->map;
- pcm_i = pa_alsa_open_by_device_string(
- id, NULL,
+ pcm_i = open_by_device_string_with_fallback(
+ i->alsa_name,
+ i->alsa_name_fallback,
+ dev_id,
+ NULL,
&try_ss, &try_map,
SND_PCM_STREAM_PLAYBACK,
NULL, NULL, 0, NULL, NULL,
TRUE);
- pa_xfree(id);
-
if (!pcm_i)
continue;
}
@@ -956,26 +1036,25 @@ int pa_alsa_probe_profiles(
snd_pcm_t *pcm_j = NULL;
if (j->alsa_name) {
- char *jd;
pa_sample_spec try_ss;
pa_channel_map try_map;
pa_log_debug("Checking for capture on %s (%s)", j->name, j->alsa_name);
- jd = pa_sprintf_malloc("%s:%s", j->alsa_name, dev_id);
try_ss = *ss;
try_ss.channels = j->map.channels;
try_map = j->map;
- pcm_j = pa_alsa_open_by_device_string(
- jd, NULL,
+ pcm_j = open_by_device_string_with_fallback(
+ j->alsa_name,
+ j->alsa_name_fallback,
+ dev_id,
+ NULL,
&try_ss, &try_map,
SND_PCM_STREAM_CAPTURE,
NULL, NULL, 0, NULL, NULL,
TRUE);
- pa_xfree(jd);
-
if (!pcm_j)
continue;
}
@@ -1117,6 +1196,7 @@ success:
int pa_alsa_find_mixer_and_elem(
snd_pcm_t *pcm,
+ char **ctl_device,
snd_mixer_t **_m,
snd_mixer_elem_t **_e,
const char *control_name,
@@ -1144,9 +1224,13 @@ int pa_alsa_find_mixer_and_elem(
/* First, try by name */
if ((dev = snd_pcm_name(pcm)))
- if (pa_alsa_prepare_mixer(m, dev) >= 0)
+ if (pa_alsa_prepare_mixer(m, dev) >= 0) {
found = TRUE;
+ if (ctl_device)
+ *ctl_device = pa_xstrdup(dev);
+ }
+
/* Then, try by card index */
if (!found) {
snd_pcm_info_t* info;
@@ -1161,9 +1245,15 @@ int pa_alsa_find_mixer_and_elem(
md = pa_sprintf_malloc("hw:%i", card_idx);
if (!dev || !pa_streq(dev, md))
- if (pa_alsa_prepare_mixer(m, md) >= 0)
+ if (pa_alsa_prepare_mixer(m, md) >= 0) {
found = TRUE;
+ if (ctl_device) {
+ *ctl_device = md;
+ md = NULL;
+ }
+ }
+
pa_xfree(md);
}
}
@@ -1199,6 +1289,9 @@ int pa_alsa_find_mixer_and_elem(
}
if (!e) {
+ if (ctl_device)
+ pa_xfree(*ctl_device);
+
snd_mixer_close(m);
return -1;
}
@@ -1542,6 +1635,36 @@ void pa_alsa_init_proplist_pcm(pa_core *c, pa_proplist *p, snd_pcm_t *pcm, snd_m
pa_alsa_init_proplist_pcm_info(c, p, info);
}
+void pa_alsa_init_proplist_ctl(pa_proplist *p, const char *name) {
+ int err;
+ snd_ctl_t *ctl;
+ snd_ctl_card_info_t *info;
+ const char *t;
+
+ pa_assert(p);
+
+ snd_ctl_card_info_alloca(&info);
+
+ if ((err = snd_ctl_open(&ctl, name, 0)) < 0) {
+ pa_log_warn("Error opening low-level control device '%s'", name);
+ return;
+ }
+
+ if ((err = snd_ctl_card_info(ctl, info)) < 0) {
+ pa_log_warn("Control device %s card info: %s", name, snd_strerror(err));
+ snd_ctl_close(ctl);
+ return;
+ }
+
+ if ((t = snd_ctl_card_info_get_mixername(info)))
+ pa_proplist_sets(p, "alsa.mixer_name", t);
+
+ if ((t = snd_ctl_card_info_get_components(info)))
+ pa_proplist_sets(p, "alsa.components", t);
+
+ snd_ctl_close(ctl);
+}
+
int pa_alsa_recover_from_poll(snd_pcm_t *pcm, int revents) {
snd_pcm_state_t state;
int err;
diff --git a/src/modules/alsa/alsa-util.h b/src/modules/alsa/alsa-util.h
index 77ac8a7f..27f43712 100644
--- a/src/modules/alsa/alsa-util.h
+++ b/src/modules/alsa/alsa-util.h
@@ -56,6 +56,7 @@ int pa_alsa_set_sw_params(snd_pcm_t *pcm, snd_pcm_uframes_t avail_min);
typedef struct pa_alsa_profile_info {
pa_channel_map map;
const char *alsa_name;
+ const char *alsa_name_fallback;
const char *description; /* internationalized */
const char *name;
unsigned priority;
@@ -65,7 +66,9 @@ typedef struct pa_alsa_profile_info {
int pa_alsa_prepare_mixer(snd_mixer_t *mixer, const char *dev);
snd_mixer_elem_t *pa_alsa_find_elem(snd_mixer_t *mixer, const char *name, const char *fallback, pa_bool_t playback);
-int pa_alsa_find_mixer_and_elem(snd_pcm_t *pcm, snd_mixer_t **_m, snd_mixer_elem_t **_e, const char *control_name, const pa_alsa_profile_info*profile);
+int pa_alsa_find_mixer_and_elem(snd_pcm_t *pcm, char **ctl_device, snd_mixer_t **_m, snd_mixer_elem_t **_e, const char *control_name, const pa_alsa_profile_info*profile);
+
+void pa_alsa_init_proplist_ctl(pa_proplist *p, const char *name);
/* Picks a working profile based on the specified ss/map */
snd_pcm_t *pa_alsa_open_by_device_id_auto(
diff --git a/src/modules/alsa/module-alsa-card.c b/src/modules/alsa/module-alsa-card.c
index 51d466e1..ad52f5e3 100644
--- a/src/modules/alsa/module-alsa-card.c
+++ b/src/modules/alsa/module-alsa-card.c
@@ -43,9 +43,12 @@ PA_MODULE_VERSION(PACKAGE_VERSION);
PA_MODULE_LOAD_ONCE(FALSE);
PA_MODULE_USAGE(
"name=<name for the card/sink/source, to be prefixed> "
- "card_name=<name for card> "
- "sink_name=<name for sink> "
- "source_name=<name for source> "
+ "card_name=<name for the card> "
+ "card_properties=<properties for the card> "
+ "sink_name=<name for the sink> "
+ "sink_properties=<properties for the sink> "
+ "source_name=<name for the source> "
+ "source_properties=<properties for the source> "
"device_id=<ALSA card index> "
"format=<sample format> "
"rate=<sample rate> "
@@ -61,8 +64,11 @@ PA_MODULE_USAGE(
static const char* const valid_modargs[] = {
"name",
"card_name",
+ "card_properties",
"sink_name",
+ "sink_properties",
"source_name",
+ "source_properties",
"device_id",
"format",
"rate",
@@ -135,7 +141,7 @@ static void enumerate_cb(
bonus += 20000;
}
- pa_log_info("Found output profile '%s'", t);
+ pa_log_info("Found profile '%s'", t);
p = pa_card_profile_new(n, t, sizeof(struct profile_data));
@@ -340,6 +346,12 @@ int pa__init(pa_module *m) {
add_disabled_profile(data.profiles);
+ if (pa_modargs_get_proplist(ma, "card_properties", data.proplist, PA_UPDATE_REPLACE) < 0) {
+ pa_log("Invalid properties");
+ pa_card_new_data_done(&data);
+ goto fail;
+ }
+
u->card = pa_card_new(m->core, &data);
pa_card_new_data_done(&data);
diff --git a/src/modules/alsa/module-alsa-sink.c b/src/modules/alsa/module-alsa-sink.c
index 8e600ab8..058ea205 100644
--- a/src/modules/alsa/module-alsa-sink.c
+++ b/src/modules/alsa/module-alsa-sink.c
@@ -40,6 +40,7 @@ PA_MODULE_LOAD_ONCE(FALSE);
PA_MODULE_USAGE(
"name=<name of the sink, to be prefixed> "
"sink_name=<name for the sink> "
+ "sink_properities=<properties for the sink> "
"device=<ALSA device> "
"device_id=<ALSA card index> "
"format=<sample format> "
@@ -58,6 +59,7 @@ PA_MODULE_USAGE(
static const char* const valid_modargs[] = {
"name",
"sink_name",
+ "sink_properties",
"device",
"device_id",
"format",
diff --git a/src/modules/alsa/module-alsa-source.c b/src/modules/alsa/module-alsa-source.c
index e6b27b3d..3bd1b451 100644
--- a/src/modules/alsa/module-alsa-source.c
+++ b/src/modules/alsa/module-alsa-source.c
@@ -64,6 +64,7 @@ PA_MODULE_LOAD_ONCE(FALSE);
PA_MODULE_USAGE(
"name=<name for the source, to be prefixed> "
"source_name=<name for the source> "
+ "source_properties=<properties for the source> "
"device=<ALSA device> "
"device_id=<ALSA card index> "
"format=<sample format> "
@@ -82,6 +83,7 @@ PA_MODULE_USAGE(
static const char* const valid_modargs[] = {
"name",
"source_name",
+ "source_properties",
"device",
"device_id",
"format",