Hello community, here is the log from the commit of package alsa for openSUSE:Factory checked in at 2016-06-12 18:51:37 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/alsa (Old) and /work/SRC/openSUSE:Factory/.alsa.new (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "alsa" Changes: -------- --- /work/SRC/openSUSE:Factory/alsa/alsa.changes 2016-05-19 12:03:36.000000000 +0200 +++ /work/SRC/openSUSE:Factory/.alsa.new/alsa.changes 2016-06-12 18:51:38.000000000 +0200 @@ -1,0 +2,15 @@ +Wed Jun 8 10:30:29 CEST 2016 - ti...@suse.de + +- Backport upstream fixes: fixing PCM dmix & co suspend/resume, + namehint parser fixes, stackable async handler: + 0007-namehint-Don-t-enumerate-as-duplex-if-only-a-single-.patch + 0008-pcm-Define-namehint-for-single-directional-PCM-types.patch + 0009-conf-Add-thread-safe-global-tree-reference.patch + 0010-pcm-Remove-resume-support-from-dmix-co.patch + 0011-pcm-Fix-secondary-retry-in-dsnoop-and-dshare.patch + 0012-pcm-dmix-resume-workaround-for-buggy-driver.patch + 0013-pcm-dmix-Prepare-slave-when-it-s-in-SETUP-too.patch + 0014-pcm-dmix-Return-error-when-slave-is-in-OPEN-or-DISCO.patch + 0015-async-Handle-previously-installed-signal-handler.patch + +------------------------------------------------------------------- New: ---- 0007-namehint-Don-t-enumerate-as-duplex-if-only-a-single-.patch 0008-pcm-Define-namehint-for-single-directional-PCM-types.patch 0009-conf-Add-thread-safe-global-tree-reference.patch 0010-pcm-Remove-resume-support-from-dmix-co.patch 0011-pcm-Fix-secondary-retry-in-dsnoop-and-dshare.patch 0012-pcm-dmix-resume-workaround-for-buggy-driver.patch 0013-pcm-dmix-Prepare-slave-when-it-s-in-SETUP-too.patch 0014-pcm-dmix-Return-error-when-slave-is-in-OPEN-or-DISCO.patch 0015-async-Handle-previously-installed-signal-handler.patch ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ alsa.spec ++++++ --- /var/tmp/diff_new_pack.3bnLQM/_old 2016-06-12 18:51:39.000000000 +0200 +++ /var/tmp/diff_new_pack.3bnLQM/_new 2016-06-12 18:51:39.000000000 +0200 @@ -55,6 +55,15 @@ Patch4: 0004-pcm-softvol-fix-conversion-of-TLVs-min_db-and-max_dB.patch Patch5: 0005-pcm-Fix-suspend-resume-regression-with-dmix-co.patch Patch6: 0006-pcm-dmix-Fix-doubly-resume-of-slave-PCM.patch +Patch7: 0007-namehint-Don-t-enumerate-as-duplex-if-only-a-single-.patch +Patch8: 0008-pcm-Define-namehint-for-single-directional-PCM-types.patch +Patch9: 0009-conf-Add-thread-safe-global-tree-reference.patch +Patch10: 0010-pcm-Remove-resume-support-from-dmix-co.patch +Patch11: 0011-pcm-Fix-secondary-retry-in-dsnoop-and-dshare.patch +Patch12: 0012-pcm-dmix-resume-workaround-for-buggy-driver.patch +Patch13: 0013-pcm-dmix-Prepare-slave-when-it-s-in-SETUP-too.patch +Patch14: 0014-pcm-dmix-Return-error-when-slave-is-in-OPEN-or-DISCO.patch +Patch15: 0015-async-Handle-previously-installed-signal-handler.patch # rest suse patches Patch99: alsa-lib-doxygen-avoid-crash-for-11.3.diff # suppress timestamp in documents @@ -131,6 +140,15 @@ %patch4 -p1 %patch5 -p1 %patch6 -p1 +%patch7 -p1 +%patch8 -p1 +%patch9 -p1 +%patch10 -p1 +%patch11 -p1 +%patch12 -p1 +%patch13 -p1 +%patch14 -p1 +%patch15 -p1 %if 0%{?suse_version} == 1130 %patch99 -p1 %endif ++++++ 0007-namehint-Don-t-enumerate-as-duplex-if-only-a-single-.patch ++++++ >From 8cdbdae73109c901aec4984f6ba65e5b25722f13 Mon Sep 17 00:00:00 2001 From: Takashi Iwai <ti...@suse.de> Date: Thu, 12 May 2016 16:30:44 +0200 Subject: [PATCH 07/15] namehint: Don't enumerate as duplex if only a single direction is defined When a hint description has only either device_input or device_output, we shouldn't handle it as a full duplex but rather a single direction. In that way, we can avoid to list up a playback stream like dmix or surround51 as a capture stream in the namehint. Reported-by: Trent Reed <treed0...@gmail.com> Signed-off-by: Takashi Iwai <ti...@suse.de> --- src/control/namehint.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/control/namehint.c b/src/control/namehint.c index 856957c76d74..ad8dda37a637 100644 --- a/src/control/namehint.c +++ b/src/control/namehint.c @@ -28,6 +28,7 @@ #include "local.h" #ifndef DOC_HIDDEN +#define DEV_SKIP 9999 /* some non-existing device number */ struct hint_list { char **list; unsigned int count; @@ -90,7 +91,7 @@ static int get_dev_name1(struct hint_list *list, char **res, int device, int stream) { *res = NULL; - if (device < 0) + if (device < 0 || device == DEV_SKIP) return 0; switch (list->iface) { #ifdef BUILD_HWDEP @@ -317,7 +318,9 @@ static int try_config(snd_config_t *config, err = -EINVAL; goto __cleanup; } - list->device_output = -1; + /* skip the counterpart if only a single direction is defined */ + if (list->device_output < 0) + list->device_output = DEV_SKIP; } if (snd_config_search(cfg, "device_output", &n) >= 0) { if (snd_config_get_integer(n, &list->device_output) < 0) { @@ -325,6 +328,9 @@ static int try_config(snd_config_t *config, err = -EINVAL; goto __cleanup; } + /* skip the counterpart if only a single direction is defined */ + if (list->device_input < 0) + list->device_input = DEV_SKIP; } } else if (level == 1 && !list->show_all) goto __skip_add; -- 2.8.3 ++++++ 0008-pcm-Define-namehint-for-single-directional-PCM-types.patch ++++++ >From 5fb3fe17249c3fffb8b8e15108ff72f27ba5e81c Mon Sep 17 00:00:00 2001 From: Takashi Iwai <ti...@suse.de> Date: Thu, 12 May 2016 16:33:19 +0200 Subject: [PATCH 08/15] pcm: Define namehint for single directional PCM types The PCM namehint for some PCM types like dmix, dsnoop and surround51 should be defined as single directional. Reported-by: Trent Reed <treed0...@gmail.com> Signed-off-by: Takashi Iwai <ti...@suse.de> --- src/conf/pcm/dmix.conf | 2 +- src/conf/pcm/dsnoop.conf | 2 +- src/conf/pcm/surround21.conf | 2 +- src/conf/pcm/surround40.conf | 2 +- src/conf/pcm/surround41.conf | 2 +- src/conf/pcm/surround50.conf | 2 +- src/conf/pcm/surround51.conf | 2 +- src/conf/pcm/surround71.conf | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/conf/pcm/dmix.conf b/src/conf/pcm/dmix.conf index e62cb295278c..7d0aa0158c42 100644 --- a/src/conf/pcm/dmix.conf +++ b/src/conf/pcm/dmix.conf @@ -110,6 +110,6 @@ pcm.!dmix { name defaults.namehint.extended } description "Direct sample mixing device" - device $DEV + device_output $DEV } } diff --git a/src/conf/pcm/dsnoop.conf b/src/conf/pcm/dsnoop.conf index 49cfca98c262..abbd44f7fd79 100644 --- a/src/conf/pcm/dsnoop.conf +++ b/src/conf/pcm/dsnoop.conf @@ -110,6 +110,6 @@ pcm.!dsnoop { name defaults.namehint.extended } description "Direct sample snooping device" - device $DEV + device_input $DEV } } diff --git a/src/conf/pcm/surround21.conf b/src/conf/pcm/surround21.conf index 7f4676b3f714..1cf1b7af8dc6 100644 --- a/src/conf/pcm/surround21.conf +++ b/src/conf/pcm/surround21.conf @@ -56,6 +56,6 @@ pcm.!surround21 { ttable.2.LFE 1 hint { description "2.1 Surround output to Front and Subwoofer speakers" - device $DEV + device_output $DEV } } diff --git a/src/conf/pcm/surround40.conf b/src/conf/pcm/surround40.conf index 361ccaa1e329..9788ad4884fd 100644 --- a/src/conf/pcm/surround40.conf +++ b/src/conf/pcm/surround40.conf @@ -54,6 +54,6 @@ pcm.!surround40 { } hint { description "4.0 Surround output to Front and Rear speakers" - device $DEV + device_output $DEV } } diff --git a/src/conf/pcm/surround41.conf b/src/conf/pcm/surround41.conf index 2f823815821a..7b4ef3beb43a 100644 --- a/src/conf/pcm/surround41.conf +++ b/src/conf/pcm/surround41.conf @@ -60,6 +60,6 @@ pcm.!surround41 { ttable.4.LFE 1 hint { description "4.1 Surround output to Front, Rear and Subwoofer speakers" - device $DEV + device_output $DEV } } diff --git a/src/conf/pcm/surround50.conf b/src/conf/pcm/surround50.conf index dc95c179da68..7d9a9e798fd8 100644 --- a/src/conf/pcm/surround50.conf +++ b/src/conf/pcm/surround50.conf @@ -60,6 +60,6 @@ pcm.!surround50 { ttable.4.FC 1 hint { description "5.0 Surround output to Front, Center and Rear speakers" - device $DEV + device_output $DEV } } diff --git a/src/conf/pcm/surround51.conf b/src/conf/pcm/surround51.conf index 3a7543f9fb8a..e67f007ef305 100644 --- a/src/conf/pcm/surround51.conf +++ b/src/conf/pcm/surround51.conf @@ -56,6 +56,6 @@ pcm.!surround51 { } hint { description "5.1 Surround output to Front, Center, Rear and Subwoofer speakers" - device $DEV + device_output $DEV } } diff --git a/src/conf/pcm/surround71.conf b/src/conf/pcm/surround71.conf index 076a97d73716..a26c3f36c437 100644 --- a/src/conf/pcm/surround71.conf +++ b/src/conf/pcm/surround71.conf @@ -58,6 +58,6 @@ pcm.!surround71 { } hint { description "7.1 Surround output to Front, Center, Side, Rear and Woofer speakers" - device $DEV + device_output $DEV } } -- 2.8.3 ++++++ 0009-conf-Add-thread-safe-global-tree-reference.patch ++++++ >From c9a0d7d601e8ab069f8745968c03c8470b24d20d Mon Sep 17 00:00:00 2001 From: Takashi Iwai <ti...@suse.de> Date: Tue, 17 May 2016 15:39:07 +0200 Subject: [PATCH 09/15] conf: Add thread-safe global tree reference Most of open functions in alsa-lib have the call pattern: snd_config_update(); return snd_xxx_open(x, snd_config, ...); This means that the toplevel config gets updated, and passed to a local open function. Although snd_config_update() itself has a pthread mutex to be thread safe, the whole procedure above isn't thread safe. Namely, the global snd_config tree may be deleted and recreated at any time while the open function is being processed. This may lead to a data corruption and crash of the program. For avoiding the corruption, this patch introduces a refcount to config tree object. A few new helper functions are introduced as well: - snd_config_update_ref() does update and take the refcount of the toplevel tree. The obtained config tree has to be freed via snd_config_unref() below. - snd_config_ref() and snd_config_unref() manage the refcount of the config object. The latter eventually deletes the object when all references are gone. Along with these additions, the caller of snd_config_update() and snd_config global tree in alsa-lib are replaced with the new helpers. Signed-off-by: Takashi Iwai <ti...@suse.de> --- include/conf.h | 4 +++ src/conf.c | 79 +++++++++++++++++++++++++++++++++++++++++++++++++ src/control/control.c | 8 +++-- src/hwdep/hwdep.c | 8 +++-- src/pcm/pcm.c | 8 +++-- src/rawmidi/rawmidi.c | 8 +++-- src/seq/seq.c | 8 +++-- src/timer/timer.c | 8 +++-- src/timer/timer_query.c | 8 +++-- 9 files changed, 125 insertions(+), 14 deletions(-) diff --git a/include/conf.h b/include/conf.h index 087c05dc6bcf..5d293d583fbb 100644 --- a/include/conf.h +++ b/include/conf.h @@ -94,6 +94,10 @@ int snd_config_update_r(snd_config_t **top, snd_config_update_t **update, const int snd_config_update_free(snd_config_update_t *update); int snd_config_update_free_global(void); +int snd_config_update_ref(snd_config_t **top); +void snd_config_ref(snd_config_t *top); +void snd_config_unref(snd_config_t *top); + int snd_config_search(snd_config_t *config, const char *key, snd_config_t **result); int snd_config_searchv(snd_config_t *config, diff --git a/src/conf.c b/src/conf.c index f8b7a6686529..a516611427ac 100644 --- a/src/conf.c +++ b/src/conf.c @@ -434,6 +434,7 @@ static pthread_once_t snd_config_update_mutex_once = PTHREAD_ONCE_INIT; struct _snd_config { char *id; snd_config_type_t type; + int refcount; /* default = 0 */ union { long integer; long long integer64; @@ -1825,6 +1826,10 @@ int snd_config_remove(snd_config_t *config) * If the node is a compound node, its descendants (the whole subtree) * are deleted recursively. * + * The function is supposed to be called only for locally copied config + * trees. For the global tree, take the reference via #snd_config_update_ref + * and free it via #snd_config_unref. + * * \par Conforming to: * LSB 3.2 * @@ -1833,6 +1838,10 @@ int snd_config_remove(snd_config_t *config) int snd_config_delete(snd_config_t *config) { assert(config); + if (config->refcount > 0) { + config->refcount--; + return 0; + } switch (config->type) { case SND_CONFIG_TYPE_COMPOUND: { @@ -3833,6 +3842,8 @@ int snd_config_update_r(snd_config_t **_top, snd_config_update_t **_update, cons * \warning Whenever #snd_config is updated, all string pointers and * configuration node handles previously obtained from it may become * invalid. + * For safer operations, use #snd_config_update_ref and release the config + * via #snd_config_unref. * * \par Errors: * Any errors encountered when parsing the input or returned by hooks or @@ -3851,6 +3862,74 @@ int snd_config_update(void) return err; } +/** + * \brief Updates #snd_config and takes its reference. + * \return 0 if #snd_config was up to date, 1 if #snd_config was + * updated, otherwise a negative error code. + * + * Unlike #snd_config_update, this function increases a reference counter + * so that the obtained tree won't be deleted until unreferenced by + * #snd_config_unref. + * + * This function is supposed to be thread-safe. + */ +int snd_config_update_ref(snd_config_t **top) +{ + int err; + + if (top) + *top = NULL; + snd_config_lock(); + err = snd_config_update_r(&snd_config, &snd_config_global_update, NULL); + if (err >= 0) { + if (snd_config) { + if (top) { + snd_config->refcount++; + *top = snd_config; + } + } else { + err = -ENODEV; + } + } + snd_config_unlock(); + return err; +} + +/** + * \brief Take the reference of the config tree. + * + * Increases a reference counter of the given config tree. + * + * This function is supposed to be thread-safe. + */ +void snd_config_ref(snd_config_t *cfg) +{ + snd_config_lock(); + if (cfg) + cfg->refcount++; + snd_config_unlock(); +} + +/** + * \brief Unreference the config tree. + * + * Decreases a reference counter of the given config tree, and eventually + * deletes the tree if all references are gone. This is the counterpart of + * #snd_config_unref. + * + * Also, the config taken via #snd_config_update_ref must be unreferenced + * by this function, too. + * + * This function is supposed to be thread-safe. + */ +void snd_config_unref(snd_config_t *cfg) +{ + snd_config_lock(); + if (cfg) + snd_config_delete(cfg); + snd_config_unlock(); +} + /** * \brief Frees a private update structure. * \param[in] update The private update structure to free. diff --git a/src/control/control.c b/src/control/control.c index 8a5d530f2674..ae7884313c63 100644 --- a/src/control/control.c +++ b/src/control/control.c @@ -968,12 +968,16 @@ static int snd_ctl_open_noupdate(snd_ctl_t **ctlp, snd_config_t *root, const cha */ int snd_ctl_open(snd_ctl_t **ctlp, const char *name, int mode) { + snd_config_t *top; int err; + assert(ctlp && name); - err = snd_config_update(); + err = snd_config_update_ref(&top); if (err < 0) return err; - return snd_ctl_open_noupdate(ctlp, snd_config, name, mode); + err = snd_ctl_open_noupdate(ctlp, top, name, mode); + snd_config_unref(top); + return err; } /** diff --git a/src/hwdep/hwdep.c b/src/hwdep/hwdep.c index 5dc791c99189..bac634bae14a 100644 --- a/src/hwdep/hwdep.c +++ b/src/hwdep/hwdep.c @@ -168,12 +168,16 @@ static int snd_hwdep_open_noupdate(snd_hwdep_t **hwdep, snd_config_t *root, cons */ int snd_hwdep_open(snd_hwdep_t **hwdep, const char *name, int mode) { + snd_config_t *top; int err; + assert(hwdep && name); - err = snd_config_update(); + err = snd_config_update_ref(&top); if (err < 0) return err; - return snd_hwdep_open_noupdate(hwdep, snd_config, name, mode); + err = snd_hwdep_open_noupdate(hwdep, top, name, mode); + snd_config_unref(top); + return err; } /** diff --git a/src/pcm/pcm.c b/src/pcm/pcm.c index 203e7a52491b..0d0d093deb49 100644 --- a/src/pcm/pcm.c +++ b/src/pcm/pcm.c @@ -2288,12 +2288,16 @@ static int snd_pcm_open_noupdate(snd_pcm_t **pcmp, snd_config_t *root, int snd_pcm_open(snd_pcm_t **pcmp, const char *name, snd_pcm_stream_t stream, int mode) { + snd_config_t *top; int err; + assert(pcmp && name); - err = snd_config_update(); + err = snd_config_update_ref(&top); if (err < 0) return err; - return snd_pcm_open_noupdate(pcmp, snd_config, name, stream, mode, 0); + err = snd_pcm_open_noupdate(pcmp, top, name, stream, mode, 0); + snd_config_unref(top); + return err; } /** diff --git a/src/rawmidi/rawmidi.c b/src/rawmidi/rawmidi.c index 0c89b8b984b9..4701b4375359 100644 --- a/src/rawmidi/rawmidi.c +++ b/src/rawmidi/rawmidi.c @@ -305,12 +305,16 @@ static int snd_rawmidi_open_noupdate(snd_rawmidi_t **inputp, snd_rawmidi_t **out int snd_rawmidi_open(snd_rawmidi_t **inputp, snd_rawmidi_t **outputp, const char *name, int mode) { + snd_config_t *top; int err; + assert((inputp || outputp) && name); - err = snd_config_update(); + err = snd_config_update_ref(&top); if (err < 0) return err; - return snd_rawmidi_open_noupdate(inputp, outputp, snd_config, name, mode); + err = snd_rawmidi_open_noupdate(inputp, outputp, top, name, mode); + snd_config_unref(top); + return err; } /** diff --git a/src/seq/seq.c b/src/seq/seq.c index 4405e68a7fe9..92798308a3b0 100644 --- a/src/seq/seq.c +++ b/src/seq/seq.c @@ -974,12 +974,16 @@ static int snd_seq_open_noupdate(snd_seq_t **seqp, snd_config_t *root, int snd_seq_open(snd_seq_t **seqp, const char *name, int streams, int mode) { + snd_config_t *top; int err; + assert(seqp && name); - err = snd_config_update(); + err = snd_config_update_ref(&top); if (err < 0) return err; - return snd_seq_open_noupdate(seqp, snd_config, name, streams, mode, 0); + err = snd_seq_open_noupdate(seqp, top, name, streams, mode, 0); + snd_config_unref(top); + return err; } /** diff --git a/src/timer/timer.c b/src/timer/timer.c index a25e4f797ce4..b25347117cba 100644 --- a/src/timer/timer.c +++ b/src/timer/timer.c @@ -201,12 +201,16 @@ static int snd_timer_open_noupdate(snd_timer_t **timer, snd_config_t *root, cons */ int snd_timer_open(snd_timer_t **timer, const char *name, int mode) { + snd_config_t *top; int err; + assert(timer && name); - err = snd_config_update(); + err = snd_config_update_ref(&top); if (err < 0) return err; - return snd_timer_open_noupdate(timer, snd_config, name, mode); + err = snd_timer_open_noupdate(timer, top, name, mode); + snd_config_unref(top); + return err; } /** diff --git a/src/timer/timer_query.c b/src/timer/timer_query.c index 93d2455d07fc..2072ceaea349 100644 --- a/src/timer/timer_query.c +++ b/src/timer/timer_query.c @@ -159,12 +159,16 @@ static int snd_timer_query_open_noupdate(snd_timer_query_t **timer, snd_config_t */ int snd_timer_query_open(snd_timer_query_t **timer, const char *name, int mode) { + snd_config_t *top; int err; + assert(timer && name); - err = snd_config_update(); + err = snd_config_update_ref(&top); if (err < 0) return err; - return snd_timer_query_open_noupdate(timer, snd_config, name, mode); + err = snd_timer_query_open_noupdate(timer, top, name, mode); + snd_config_unref(top); + return err; } /** -- 2.8.3 ++++++ 0010-pcm-Remove-resume-support-from-dmix-co.patch ++++++ >From d942498bfbd315c4c4559ccd573685e09aa03383 Mon Sep 17 00:00:00 2001 From: Takashi Iwai <ti...@suse.de> Date: Wed, 18 May 2016 10:38:27 +0200 Subject: [PATCH 10/15] pcm: Remove resume support from dmix & co PCM dmix and other plugins inherit the resume behavior from the slave PCM. However, the resume on dmix can't work reliably even if the slave PCM may do resume. The running state of each dmix stream is individual and may be PREPARED or RUN_PENDING while the slave PCM is already in RUNNING. And, when the slave PCM is resumed, the whole samples that have been already mapped are also played back, even if the corresponding dmix stream is still in SUSPENDED. Such inconsistencies can't be avoided as long as we manage each stream individually. That said, dmix & co can't provide the proper resume support "by design". For aligning with it, we should drop the whole resume code and clear the PCM SND_PCM_INFO_RESUME flag. Reported-by: Shengjiu Wang <shengjiu.w...@nxp.com> Signed-off-by: Takashi Iwai <ti...@suse.de> --- src/pcm/pcm_direct.c | 24 ++---------------------- 1 file changed, 2 insertions(+), 22 deletions(-) diff --git a/src/pcm/pcm_direct.c b/src/pcm/pcm_direct.c index ac082f1a73b2..53c49929cb1f 100644 --- a/src/pcm/pcm_direct.c +++ b/src/pcm/pcm_direct.c @@ -837,27 +837,7 @@ int snd_pcm_direct_prepare(snd_pcm_t *pcm) int snd_pcm_direct_resume(snd_pcm_t *pcm) { - snd_pcm_direct_t *dmix = pcm->private_data; - int err; - - snd_pcm_direct_semaphore_down(dmix, DIRECT_IPC_SEM_CLIENT); - /* resume only when the slave PCM is still in suspended state */ - if (snd_pcm_state(dmix->spcm) != SND_PCM_STATE_SUSPENDED) { - err = 0; - goto out; - } - - err = snd_pcm_resume(dmix->spcm); - if (err == -ENOSYS) { - /* FIXME: error handling? */ - snd_pcm_prepare(dmix->spcm); - snd_pcm_start(dmix->spcm); - err = 0; - } - out: - dmix->state = snd_pcm_state(dmix->spcm); - snd_pcm_direct_semaphore_up(dmix, DIRECT_IPC_SEM_CLIENT); - return err; + return -ENOSYS; } #define COPY_SLAVE(field) (dmix->shmptr->s.field = spcm->field) @@ -865,7 +845,7 @@ int snd_pcm_direct_resume(snd_pcm_t *pcm) /* copy the slave setting */ static void save_slave_setting(snd_pcm_direct_t *dmix, snd_pcm_t *spcm) { - spcm->info &= ~SND_PCM_INFO_PAUSE; + spcm->info &= ~(SND_PCM_INFO_PAUSE | SND_PCM_INFO_RESUME); COPY_SLAVE(access); COPY_SLAVE(format); -- 2.8.3 ++++++ 0011-pcm-Fix-secondary-retry-in-dsnoop-and-dshare.patch ++++++ >From 2fa36eb03c000560128f7abce701536546b4a618 Mon Sep 17 00:00:00 2001 From: Takashi Iwai <ti...@suse.de> Date: Sat, 28 May 2016 10:37:26 +0200 Subject: [PATCH 11/15] pcm: Fix secondary retry in dsnoop and dshare The commit [fdba9e1bad8f: pcm: Fallback open as the first instance for dmix & co] introduced a mechanism to retry the open of slave PCM for the secondary streams, but this also introduced a regression in dsnoop and dshare plugins: since the retry goto-tag was placed at a wrong position, it retries to re-fetch the shm unnecessarily and eventually leads to the fatal error. The bug can be easily reproduced by starting arecord and killing it via SIGKILL, then starting arecord again. The second arecord fails. The fix is obviously to move the wrong retry goto-tags to the right positions. Signed-off-by: Takashi Iwai <ti...@suse.de> --- src/pcm/pcm_dshare.c | 2 +- src/pcm/pcm_dsnoop.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pcm/pcm_dshare.c b/src/pcm/pcm_dshare.c index adb3587a2869..05854dedf259 100644 --- a/src/pcm/pcm_dshare.c +++ b/src/pcm/pcm_dshare.c @@ -690,7 +690,6 @@ int snd_pcm_dshare_open(snd_pcm_t **pcmp, const char *name, break; } - retry: first_instance = ret = snd_pcm_direct_shm_create_or_connect(dshare); if (ret < 0) { SNDERR("unable to create IPC shm instance"); @@ -705,6 +704,7 @@ int snd_pcm_dshare_open(snd_pcm_t **pcmp, const char *name, dshare->max_periods = opts->max_periods; dshare->sync_ptr = snd_pcm_dshare_sync_ptr; + retry: if (first_instance) { /* recursion is already checked in snd_pcm_direct_get_slave_ipc_offset() */ diff --git a/src/pcm/pcm_dsnoop.c b/src/pcm/pcm_dsnoop.c index 8ff0ba57cb14..2d45171dda01 100644 --- a/src/pcm/pcm_dsnoop.c +++ b/src/pcm/pcm_dsnoop.c @@ -583,7 +583,6 @@ int snd_pcm_dsnoop_open(snd_pcm_t **pcmp, const char *name, break; } - retry: first_instance = ret = snd_pcm_direct_shm_create_or_connect(dsnoop); if (ret < 0) { SNDERR("unable to create IPC shm instance"); @@ -598,6 +597,7 @@ int snd_pcm_dsnoop_open(snd_pcm_t **pcmp, const char *name, dsnoop->max_periods = opts->max_periods; dsnoop->sync_ptr = snd_pcm_dsnoop_sync_ptr; + retry: if (first_instance) { /* recursion is already checked in snd_pcm_direct_get_slave_ipc_offset() */ -- 2.8.3 ++++++ 0012-pcm-dmix-resume-workaround-for-buggy-driver.patch ++++++ >From 6d1d620eadf32c6d963468ce56ff52cc3a2f32e2 Mon Sep 17 00:00:00 2001 From: Takashi Iwai <ti...@suse.de> Date: Wed, 25 May 2016 15:03:51 +0200 Subject: [PATCH 12/15] pcm: dmix: resume workaround for buggy driver The previous commit removed the whole handling of resume in dmix, but this seems causing another regression; some buggy drivers assume that the device-resume needs to be triggered before transitioning to PREPARED state. As an ugly workaround, in this patch, when the slave PCM supports resume, snd_pcm_direct_resume() does resume of the slave PCM but immediately drop the stream after that. In that way, the device is brought to the sane active state, then the apps can prepare and restart the stream properly. Signed-off-by: Takashi Iwai <ti...@suse.de> --- src/pcm/pcm_direct.c | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/src/pcm/pcm_direct.c b/src/pcm/pcm_direct.c index 53c49929cb1f..343fd3c6da3c 100644 --- a/src/pcm/pcm_direct.c +++ b/src/pcm/pcm_direct.c @@ -837,6 +837,27 @@ int snd_pcm_direct_prepare(snd_pcm_t *pcm) int snd_pcm_direct_resume(snd_pcm_t *pcm) { + snd_pcm_direct_t *dmix = pcm->private_data; + snd_pcm_t *spcm = dmix->spcm; + + snd_pcm_direct_semaphore_down(dmix, DIRECT_IPC_SEM_CLIENT); + /* some buggy drivers require the device resumed before prepared; + * when a device has RESUME flag and is in SUSPENDED state, resume + * here but immediately drop to bring it to a sane active state. + */ + if ((spcm->info & SND_PCM_INFO_RESUME) && + snd_pcm_state(spcm) == SND_PCM_STATE_SUSPENDED) { + snd_pcm_resume(spcm); + snd_pcm_drop(spcm); + snd_pcm_direct_timer_stop(dmix); + snd_pcm_direct_clear_timer_queue(dmix); + snd_pcm_areas_silence(snd_pcm_mmap_areas(spcm), 0, + spcm->channels, spcm->buffer_size, + spcm->format); + snd_pcm_prepare(spcm); + snd_pcm_start(spcm); + } + snd_pcm_direct_semaphore_up(dmix, DIRECT_IPC_SEM_CLIENT); return -ENOSYS; } @@ -845,7 +866,7 @@ int snd_pcm_direct_resume(snd_pcm_t *pcm) /* copy the slave setting */ static void save_slave_setting(snd_pcm_direct_t *dmix, snd_pcm_t *spcm) { - spcm->info &= ~(SND_PCM_INFO_PAUSE | SND_PCM_INFO_RESUME); + spcm->info &= ~SND_PCM_INFO_PAUSE; COPY_SLAVE(access); COPY_SLAVE(format); @@ -874,6 +895,8 @@ static void save_slave_setting(snd_pcm_direct_t *dmix, snd_pcm_t *spcm) COPY_SLAVE(buffer_time); COPY_SLAVE(sample_bits); COPY_SLAVE(frame_bits); + + dmix->shmptr->s.info &= ~SND_PCM_INFO_RESUME; } #undef COPY_SLAVE -- 2.8.3 ++++++ 0013-pcm-dmix-Prepare-slave-when-it-s-in-SETUP-too.patch ++++++ >From 8feb96ed9b457c2aa62ddea2c48651475b7c3411 Mon Sep 17 00:00:00 2001 From: Takashi Iwai <ti...@suse.de> Date: Tue, 31 May 2016 12:46:03 +0200 Subject: [PATCH 13/15] pcm: dmix: Prepare slave when it's in SETUP, too SETUP is an unusual state, but it's still possible. Signed-off-by: Takashi Iwai <ti...@suse.de> --- src/pcm/pcm_direct.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/pcm/pcm_direct.c b/src/pcm/pcm_direct.c index 343fd3c6da3c..fbf9a592a4bc 100644 --- a/src/pcm/pcm_direct.c +++ b/src/pcm/pcm_direct.c @@ -819,6 +819,7 @@ int snd_pcm_direct_prepare(snd_pcm_t *pcm) int err; switch (snd_pcm_state(dmix->spcm)) { + case SND_PCM_STATE_SETUP: case SND_PCM_STATE_XRUN: case SND_PCM_STATE_SUSPENDED: case SND_PCM_STATE_DISCONNECTED: -- 2.8.3 ++++++ 0014-pcm-dmix-Return-error-when-slave-is-in-OPEN-or-DISCO.patch ++++++ >From 614ce73d3d6eba13946f863bec24981d355902e1 Mon Sep 17 00:00:00 2001 From: Takashi Iwai <ti...@suse.de> Date: Tue, 31 May 2016 12:48:40 +0200 Subject: [PATCH 14/15] pcm: dmix: Return error when slave is in OPEN or DISCONNECTED A slave PCM in OPEN or DISCONNECTED state can't be used properly at all, so the best option is to return -EBADFD error. Signed-off-by: Takashi Iwai <ti...@suse.de> --- src/pcm/pcm_direct.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/pcm/pcm_direct.c b/src/pcm/pcm_direct.c index fbf9a592a4bc..21f98e7a1779 100644 --- a/src/pcm/pcm_direct.c +++ b/src/pcm/pcm_direct.c @@ -822,12 +822,14 @@ int snd_pcm_direct_prepare(snd_pcm_t *pcm) case SND_PCM_STATE_SETUP: case SND_PCM_STATE_XRUN: case SND_PCM_STATE_SUSPENDED: - case SND_PCM_STATE_DISCONNECTED: err = snd_pcm_prepare(dmix->spcm); if (err < 0) return err; snd_pcm_start(dmix->spcm); break; + case SND_PCM_STATE_OPEN: + case SND_PCM_STATE_DISCONNECTED: + return -EBADFD; } snd_pcm_direct_check_interleave(dmix, pcm); dmix->state = SND_PCM_STATE_PREPARED; -- 2.8.3 ++++++ 0015-async-Handle-previously-installed-signal-handler.patch ++++++ >From d39e1879b9c72d51fe1ca4aeb5ba742e97b2175a Mon Sep 17 00:00:00 2001 From: Eliot Miranda <eliot.mira...@gmail.com> Date: Wed, 1 Jun 2016 08:16:31 +0200 Subject: [PATCH 15/15] async: Handle previously installed signal handler The issue is with the signal handler installed and deinstalled in alsa-lib async handler. This code makes no attempt to remember any previously installed signal handlers for SIGIO, if SIGIO is used. Consequently it does not call any previous handlers from its own handler once installed, and does not reinstall any previous handler when deinstalling its handler. Consequently, use of also-lib within applications that depend on SIGIO will break those applications, rendering them inoperative once alsa-lib is running because their signal handlers are no longer called. This patch does remember and restore any previous handler, and chains calls to the handler if it exists. Signed-off-by: Takashi Iwai <ti...@suse.de> --- src/async.c | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/src/async.c b/src/async.c index 98aec78e00a8..0e133c3a34cd 100644 --- a/src/async.c +++ b/src/async.c @@ -28,6 +28,9 @@ #include "control/control_local.h" #include <signal.h> +static struct sigaction previous_action; +#define MAX_SIG_FUNCTION_CODE 10 /* i.e. SIG_DFL SIG_IGN SIG_HOLD et al */ + #ifdef SND_ASYNC_RT_SIGNAL /** async signal number */ static int snd_async_signo; @@ -54,6 +57,9 @@ static void snd_async_handler(int signo ATTRIBUTE_UNUSED, siginfo_t *siginfo, vo int fd; struct list_head *i; //assert(siginfo->si_code == SI_SIGIO); + if (signo == SIGIO + && (unsigned long)(previous_action.sa_sigaction) > MAX_SIG_FUNCTION_CODE) + previous_action.sa_sigaction(signo, siginfo, context); fd = siginfo->si_fd; list_for_each(i, &snd_async_handlers) { snd_async_handler_t *h = list_entry(i, snd_async_handler_t, glist); @@ -114,7 +120,8 @@ int snd_async_add_handler(snd_async_handler_t **handler, int fd, act.sa_flags = SA_RESTART | SA_SIGINFO; act.sa_sigaction = snd_async_handler; sigemptyset(&act.sa_mask); - err = sigaction(snd_async_signo, &act, NULL); + assert(!previous_action.sa_sigaction); + err = sigaction(snd_async_signo, &act, &previous_action); if (err < 0) { SYSERR("sigaction"); return -errno; @@ -131,18 +138,17 @@ int snd_async_add_handler(snd_async_handler_t **handler, int fd, int snd_async_del_handler(snd_async_handler_t *handler) { int err = 0; + int was_empty = list_empty(&snd_async_handlers); assert(handler); list_del(&handler->glist); - if (list_empty(&snd_async_handlers)) { - struct sigaction act; - memset(&act, 0, sizeof(act)); - act.sa_flags = 0; - act.sa_handler = SIG_DFL; - err = sigaction(snd_async_signo, &act, NULL); + if (!was_empty + && list_empty(&snd_async_handlers)) { + err = sigaction(snd_async_signo, &previous_action, NULL); if (err < 0) { SYSERR("sigaction"); return -errno; } + memset(&previous_action, 0, sizeof(previous_action)); } if (handler->type == SND_ASYNC_HANDLER_GENERIC) goto _end; -- 2.8.3