From 93ca785e9b7e295deb3be7406b37de2c61ef2579 Mon Sep 17 00:00:00 2001 From: Thierry Bultel Date: Wed, 13 Feb 2019 11:05:20 +0100 Subject: alsa-utils-dump: added missing ending null char For some reason, snd_pcm_hw_params_dump does not add a null char at the end of the dumped string. This made weird chars appear in the console Change-Id: I6f2895e730e9bbd0f78139505f9b9e68dd164f2e Signed-off-by: Thierry Bultel --- plugins/alsa/alsa-utils-dump.c | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/alsa/alsa-utils-dump.c b/plugins/alsa/alsa-utils-dump.c index 79059ee..4bcc294 100644 --- a/plugins/alsa/alsa-utils-dump.c +++ b/plugins/alsa/alsa-utils-dump.c @@ -153,6 +153,7 @@ PUBLIC void AlsaDumpPcmParams(SoftMixerT *mixer, snd_pcm_hw_params_t *pcmHwParam snd_output_buffer_open(&output); snd_pcm_hw_params_dump(pcmHwParams, output); + snd_output_putc(output,'\0'); snd_output_buffer_string(output, &buffer); AFB_ApiDebug(mixer->api, "%s:\n--------------\n%s\n-------------",__func__, buffer); snd_output_close(output); -- cgit 1.2.3-korg From bb70b4863ce923b80bee0cbc48229db168be067c Mon Sep 17 00:00:00 2001 From: Thierry Bultel Date: Wed, 13 Feb 2019 11:19:25 +0100 Subject: alsa-api-pcm: added support of quirks Adds an optional array of quirks in the parameters of playback and captures. For now, the only known quirk is a needed workarround for writing sound output on the minnow board. Change-Id: I6c65d110a1f9333ccb77cd8f4eeb9c088d0d2eca Signed-off-by: Thierry Bultel --- plugins/alsa/alsa-api-pcm.c | 34 ++++++++++++++++++++++++++++++---- plugins/alsa/alsa-api-streams.c | 3 ++- plugins/alsa/alsa-api-zones.c | 2 ++ plugins/alsa/alsa-plug-route.c | 4 ++++ plugins/alsa/alsa-softmixer.h | 6 ++++++ 5 files changed, 44 insertions(+), 5 deletions(-) diff --git a/plugins/alsa/alsa-api-pcm.c b/plugins/alsa/alsa-api-pcm.c index ce74982..50246e1 100644 --- a/plugins/alsa/alsa-api-pcm.c +++ b/plugins/alsa/alsa-api-pcm.c @@ -536,7 +536,7 @@ fail: PUBLIC AlsaSndPcmT * ApiPcmAttachOne(SoftMixerT *mixer, const char *uid, snd_pcm_stream_t direction, json_object * argsJ) { - json_object *sourceJ = NULL, *paramsJ = NULL, *sinkJ = NULL, *targetJ = NULL; + json_object *sourceJ = NULL, *paramsJ = NULL, *sinkJ = NULL, *targetJ = NULL, *quirksJ = NULL; char *apiVerb = NULL, *apiInfo = NULL; apiVerbHandleT *handle = NULL; int error; @@ -554,7 +554,7 @@ PUBLIC AlsaSndPcmT * ApiPcmAttachOne(SoftMixerT *mixer, const char *uid, snd_pcm } CDS_INIT_LIST_HEAD(&pcm->sndcard->registryList); - error = wrap_json_unpack(argsJ, "{ss,s?s,s?s,s?s,s?i,s?i,s?o,s?o,s?o !}" + error = wrap_json_unpack(argsJ, "{ss,s?s,s?s,s?s,s?i,s?i,s?o,s?o,s?o,s?o !}" , "uid", &pcm->uid , "pcmplug_params", &pcm->sndcard->cid.pcmplug_params , "path", &pcm->sndcard->cid.devpath @@ -564,9 +564,10 @@ PUBLIC AlsaSndPcmT * ApiPcmAttachOne(SoftMixerT *mixer, const char *uid, snd_pcm , "sink", &sinkJ , "source", &sourceJ , "params", ¶msJ + , "quirks", &quirksJ ); if (error) { - AFB_ApiError(mixer->api, "%s: hal=%s missing 'uid|path|cardid|device|sink|source|params' error=%s args=%s", +(??) AFB_ApiError(mixer->api, "%s: hal=%s missing 'uid|path|cardid|device|sink|source|params|quirks' error=%s args=%s", __func__, uid, wrap_json_get_error_string(error), json_object_get_string(argsJ)); goto fail_pcm_sndcard; } @@ -579,8 +580,10 @@ PUBLIC AlsaSndPcmT * ApiPcmAttachOne(SoftMixerT *mixer, const char *uid, snd_pcm // try to open sound card control interface pcm->sndcard->ctl = AlsaByPathOpenCtl(mixer, pcm->uid, pcm->sndcard); if (!pcm->sndcard->ctl) { + AFB_ApiError(mixer->api, "%s: hal=%s Fail to open sndcard uid=%s devpath=%s cardid=%s", __func__, uid, pcm->uid, pcm->sndcard->cid.devpath, pcm->sndcard->cid.cardid); + goto fail_pcm_sndcard; } @@ -658,6 +661,29 @@ PUBLIC AlsaSndPcmT * ApiPcmAttachOne(SoftMixerT *mixer, const char *uid, snd_pcm pcm->sndcard->params->channels = pcm->nbChannels; + if (quirksJ) { + int nbQuirks = (int) json_object_array_length(quirksJ); + + for (int idx = 0; idx < nbQuirks; idx++) { + json_object * quirkJ = json_object_array_get_idx(quirksJ, idx); + + if (!json_object_is_type(quirkJ, json_type_string)) { + AFB_API_ERROR(mixer->api, "%s: hal=%s quirk must be of type string, arg=%s", + __func__, uid, json_object_get_string(quirkJ)); + goto fail_pcm_channels; + } + const char * quirk = json_object_get_string(quirkJ); + +#define QUIRK_CHECK(quirkS, quirk) \ + if (!strcmp(quirkS, #quirk)) { \ + pcm->quirks |= quirk; \ + AFB_API_INFO(mixer->api, "%s: PCM=%s has quirk %s",__func__, uid, #quirk);\ + } + + QUIRK_CHECK(quirk, QUIRK_BOGUS_POLL_REVENTS_DEMANGLING); + } + } + if (controlsJ) { json_object *volJ = NULL, *muteJ = NULL; error = wrap_json_unpack(controlsJ, "{s?o,s?o !}" @@ -741,7 +767,7 @@ PUBLIC AlsaSndPcmT * ApiPcmAttachOne(SoftMixerT *mixer, const char *uid, snd_pcm goto fail_pcm_uid; } } - +done: return pcm; fail_pcm_uid: diff --git a/plugins/alsa/alsa-api-streams.c b/plugins/alsa/alsa-api-streams.c index f7149cc..7241f41 100644 --- a/plugins/alsa/alsa-api-streams.c +++ b/plugins/alsa/alsa-api-streams.c @@ -445,8 +445,9 @@ STATIC int CreateOneStream(SoftMixerT *mixer, const char * uid, AlsaStreamAudioT } streamPcm->isPcmPlug = zone->isPcmPlug; + streamPcm->quirks = zone->quirks; - AFB_ApiDebug(mixer->api, "%s: Opening PCM PLAYBACK name %s", __func__, playbackName); + AFB_ApiDebug(mixer->api, "%s: Opening PCM PLAYBACK name %s (quirks=0x%x)", __func__, playbackName, streamPcm->quirks); // everything is now ready to open playback pcm in BLOCKING mode this time error = snd_pcm_open(&streamPcm->handle, playbackName, SND_PCM_STREAM_PLAYBACK, 0 /* will block*/ ); diff --git a/plugins/alsa/alsa-api-zones.c b/plugins/alsa/alsa-api-zones.c index bf3bece..03a2264 100644 --- a/plugins/alsa/alsa-api-zones.c +++ b/plugins/alsa/alsa-api-zones.c @@ -81,6 +81,7 @@ fail: return NULL; } + STATIC AlsaSndZoneT *AttachOneZone(SoftMixerT *mixer, const char *uid, json_object *zoneJ) { AFB_ApiDebug(mixer->api, "%s uid %s", __func__, uid); @@ -241,6 +242,7 @@ PUBLIC AlsaSndZoneT * zoneCreate(SoftMixerT* mixer, const char * uid, json_objec } zone->isPcmPlug = routeConfig->isPcmPlug; + zone->quirks = routeConfig->quirks; fail: return zone; diff --git a/plugins/alsa/alsa-plug-route.c b/plugins/alsa/alsa-plug-route.c index a192e03..d3f22a7 100644 --- a/plugins/alsa/alsa-plug-route.c +++ b/plugins/alsa/alsa-plug-route.c @@ -31,6 +31,7 @@ typedef struct { int ccount; int port; bool isPcmPlug; + unsigned int quirks; } ChannelCardPortT; STATIC int CardChannelByUid(SoftMixerT *mixer, const char *uid, ChannelCardPortT *response) { @@ -59,6 +60,7 @@ STATIC int CardChannelByUid(SoftMixerT *mixer, const char *uid, ChannelCardPortT response->cardidx = pcm->sndcard->cid.cardidx; response->pcmplug_params = pcm->sndcard->cid.pcmplug_params; response->isPcmPlug= pcm->isPcmPlug; + response->quirks = pcm->quirks; found = true; break; } @@ -151,6 +153,8 @@ PUBLIC AlsaPcmCtlT* AlsaCreateRoute(SoftMixerT *mixer, AlsaSndZoneT *zone, int o pcmRoute->isPcmPlug = true; } + pcmRoute->quirks = slave.quirks; + // temporary store to enable multiple channel to route to the same port snd_config_t **cports = alloca(zone->nbSinks * sizeof (void*)); diff --git a/plugins/alsa/alsa-softmixer.h b/plugins/alsa/alsa-softmixer.h index 2e7874d..c73dd0e 100644 --- a/plugins/alsa/alsa-softmixer.h +++ b/plugins/alsa/alsa-softmixer.h @@ -128,6 +128,7 @@ typedef struct { bool closeAtDeletion; // intermediate pcms in the pcm chain must not be closed, else it make libasound abort() bool isPcmPlug; + unsigned int quirks; } AlsaPcmCtlT; typedef struct { @@ -224,8 +225,12 @@ typedef struct { struct cds_list_head list; snd_config_t * routeConfig; bool isPcmPlug; + unsigned int quirks; } AlsaSndZoneT; +/* This is a list of known sound card (hardware or driver) specific bugs */ +/* - do not trust the result of snd_pcm_poll_descriptors_revents */ +#define QUIRK_BOGUS_POLL_REVENTS_DEMANGLING (1<<0) typedef struct { const char *uid; const char *verb; @@ -238,6 +243,7 @@ typedef struct { struct cds_list_head list; bool isPcmPlug; void * apiVerbHandle; + unsigned int quirks; } AlsaSndPcmT; typedef struct { -- cgit 1.2.3-korg From 93bf6e6901744f4bee2b673361ae81a97d3dd340 Mon Sep 17 00:00:00 2001 From: Thierry Bultel Date: Wed, 13 Feb 2019 11:30:53 +0100 Subject: alsa-transaction: simplify the cleanup This simplifies the invocation of cleanup, by only using AlsaMixerTransactionDelete that performs the 3 actions of cleanup, removal from list, and memory freeing. Fixes a bug at MixerExit, because the first transaction was not removed from the list and led to a double free error. Also added a boolean parameter to AlsaMixerTransactionObjectDelete (was AlsaMixerTransactionObjectForget before), that decides wether or not the found object must be destroyed with its destructor (for most of the cases) or simply freed in memory (which is needed for loop device). Change-Id: I2eacbf80a22e3d556dc432d393a1807fcd7c47fb Signed-off-by: Thierry Bultel --- plugins/alsa/alsa-api-loop.c | 2 +- plugins/alsa/alsa-api-mixer.c | 39 ++++++++++++++++----------------------- plugins/alsa/alsa-transaction.c | 30 ++++++++++++++++++------------ plugins/alsa/alsa-transaction.h | 4 +--- 4 files changed, 36 insertions(+), 39 deletions(-) diff --git a/plugins/alsa/alsa-api-loop.c b/plugins/alsa/alsa-api-loop.c index 7e852a8..bc3c62a 100644 --- a/plugins/alsa/alsa-api-loop.c +++ b/plugins/alsa/alsa-api-loop.c @@ -117,7 +117,7 @@ STATIC int CheckOneSubdev(SoftMixerT *mixer, AlsaSndLoopT *loop, AlsaLoopSubdevT pcmCtl->closeAtDeletion = true; // free PCM as we only open loop to assert it's a valid capture device - AlsaMixerTransactionObjectForget(mixer->transaction, pcmCtl); + AlsaMixerTransactionObjectDelete(mixer->transaction, pcmCtl, false); AlsaPcmCtlDelete(mixer, pcmCtl); return 0; diff --git a/plugins/alsa/alsa-api-mixer.c b/plugins/alsa/alsa-api-mixer.c index 328f02e..c000ea8 100644 --- a/plugins/alsa/alsa-api-mixer.c +++ b/plugins/alsa/alsa-api-mixer.c @@ -32,7 +32,7 @@ static json_object *LoopsJ = NULL; static void MixerExit() { SoftMixerT *mixer, *tmp; - printf("%s !\n", __func__); + printf("-------------------------- %s ------------------------!\n", __func__); cds_list_for_each_entry_safe(mixer, tmp, &mixerList, list) { // remove this mixer from the global mixer list @@ -41,13 +41,12 @@ static void MixerExit() { AlsaMixerTransaction * transaction, * tmp_trans; cds_list_for_each_entry_safe(transaction, tmp_trans, &mixer->transactionList, transaction_node) { - cds_list_del(&transaction->transaction_node); - AlsaMixerTransactionDoCleanup(transaction); AlsaMixerTransactionDelete(transaction); } + AFB_API_INFO(mixer->api, "Mixer %s terminated", mixer->uid); MixerDelete(mixer); } - printf("%s DONE ! Bye !\n", __func__); + printf("------------------------- %s DONE ! Bye ! --------------\n", __func__); } CTLP_ONLOAD(plugin, callbacks){ @@ -582,7 +581,7 @@ STATIC void MixerAttachVerb(AFB_ReqT request) { if (playbacksJ) { error = ApiSinkAttach(mixer, request, uid, playbacksJ); - if (error) goto fail_loop; + if (error) goto fail; json_object *resultJ = MixerInfoPcms(mixer, playbacksJ, SND_PCM_STREAM_PLAYBACK, 0); json_object_object_add(responseJ, "playbacks", resultJ); @@ -594,7 +593,7 @@ STATIC void MixerAttachVerb(AFB_ReqT request) { error = ApiSourceAttach(mixer, request, uid, capturesJ); if (error) { AFB_ApiError(mixer->api,"%s: source attach failed", __func__); - goto fail_sink; + goto fail; } json_object *resultJ = MixerInfoPcms(mixer, capturesJ, SND_PCM_STREAM_CAPTURE, 0); @@ -606,7 +605,7 @@ STATIC void MixerAttachVerb(AFB_ReqT request) { if (zonesJ) { error = ApiZoneAttach(mixer, request, uid, zonesJ); if (error) - goto fail_source; + goto fail; json_object *resultJ = MixerInfoZones(mixer, zonesJ, 0); json_object_object_add(responseJ, "zone", resultJ); @@ -627,7 +626,7 @@ STATIC void MixerAttachVerb(AFB_ReqT request) { if (rampsJ) { error = ApiRampAttach(mixer, request, uid, rampsJ); if (error) - goto fail_zone; + goto fail; json_object *resultJ = MixerInfoRamps(mixer, rampsJ, 0); json_object_object_add(responseJ, "ramps", resultJ); @@ -638,7 +637,7 @@ STATIC void MixerAttachVerb(AFB_ReqT request) { if (streamsJ) { error = ApiStreamAttach(mixer, request, uid, prefix, streamsJ); if (error) - goto fail_ramp; + goto fail; json_object *resultJ = MixerInfoStreams(mixer, streamsJ, 0); json_object_object_add(responseJ, "streams", resultJ); @@ -648,7 +647,7 @@ STATIC void MixerAttachVerb(AFB_ReqT request) { if (error) { AFB_ApiError(mixer->api, "%s mixer=%s verb=%s fail to register post attach Verb ", __func__, mixer->uid, uid); - goto fail_stream; + goto fail; } AFB_ApiNotice(mixer->api, "%s responseJ=%s", __func__, json_object_get_string(responseJ)); @@ -657,22 +656,12 @@ STATIC void MixerAttachVerb(AFB_ReqT request) { AFB_ApiInfo(mixer->api,"%s DONE", __func__); return; -fail_stream: - // TODO remove created streams -fail_ramp: - // TODO remove created ramps -fail_zone: - // TODO remove created zone -fail_loop: - // TODO remove created loops -fail_source: - // TODO remove created sources -fail_sink: - // TODO remove created sinks fail: if (mixer->transaction) - free(mixer->transaction); + AlsaMixerTransactionDelete(mixer->transaction); + + mixer->transaction = NULL; AFB_ApiError(mixer->api,"%s FAILED", __func__); return; @@ -771,6 +760,10 @@ CTLP_CAPI(MixerAttach, source, argsJ, responseJ) { return 0; OnErrorExit: + if (mixer->transaction) + AlsaMixerTransactionDelete(mixer->transaction); + + mixer->transaction = NULL; return -1; } diff --git a/plugins/alsa/alsa-transaction.c b/plugins/alsa/alsa-transaction.c index dcfd028..a630bdc 100644 --- a/plugins/alsa/alsa-transaction.c +++ b/plugins/alsa/alsa-transaction.c @@ -6,16 +6,19 @@ #include "wrap-json.h" -void AlsaMixerTransactionDelete(AlsaMixerTransaction * transaction) { +static void AlsaMixerTransactionFree(AlsaMixerTransaction * transaction) { free((char*)transaction->uid); free(transaction); } AlsaMixerTransaction * AlsaMixerTransactionNew(struct SoftMixerT_ * mixer, const char * uid) { + AlsaMixerTransaction * transaction = (AlsaMixerTransaction *) malloc(sizeof(AlsaMixerTransaction)); if (transaction == NULL) goto fail; + AFB_API_INFO(mixer->api, "-------- NEW TRANSACTION %s --------", uid); + CDS_INIT_LIST_HEAD(&transaction->item_list); transaction->uid = strdup(uid); if (transaction->uid == NULL) { @@ -33,14 +36,15 @@ fail: return NULL; } -void AlsaMixerTransactionDataListDestroy(AlsaMixerTransaction* list) { - free(list); -} -void AlsaMixerTransactionObjectForget(AlsaMixerTransaction* list, void * object) { +void AlsaMixerTransactionObjectDelete(AlsaMixerTransaction* transaction, void * object, bool destructor) { AlsaMixerTransactionDataItem *item, *tmp; - cds_list_for_each_entry_safe(item, tmp, &list->item_list, list_entry) + cds_list_for_each_entry_safe(item, tmp, &transaction->item_list, list_entry) if (item->object == object) { + + if (destructor && item->destructor) + item->destructor(transaction->mixer, item->object); + cds_list_del(&item->list_entry); free(item); } @@ -88,7 +92,7 @@ fail: } -void AlsaMixerTransactionDoCleanup(AlsaMixerTransaction* transaction) { +static void AlsaMixerTransactionDoCleanup(AlsaMixerTransaction* transaction) { AlsaMixerTransactionDataItem * item, *sav; AFB_ApiInfo(transaction->mixer->api, "%s for transaction %s", __func__, transaction->uid); @@ -127,11 +131,6 @@ void AlsaMixerTransactionVerbCB(AFB_ReqT request) { } if (strcmp(action, "remove") == 0) { - AlsaMixerTransactionDoCleanup(transaction); - - // remove this transaction for the list of mixer - cds_list_del(&transaction->transaction_node); - error = afb_api_del_verb(transaction->mixer->api, transaction->uid, (void**)NULL); if (error) { AFB_ReqFail(request, "verb deletion" , "verb was not removed"); @@ -154,3 +153,10 @@ fail: return; } + +void AlsaMixerTransactionDelete(AlsaMixerTransaction * transaction) { + AlsaMixerTransactionDoCleanup(transaction); + // remove this transaction for the list of mixer + cds_list_del(&transaction->transaction_node); + AlsaMixerTransactionFree(transaction); +} diff --git a/plugins/alsa/alsa-transaction.h b/plugins/alsa/alsa-transaction.h index ba45ae7..9080518 100644 --- a/plugins/alsa/alsa-transaction.h +++ b/plugins/alsa/alsa-transaction.h @@ -32,9 +32,7 @@ extern void AlsaMixerTransactionDataListDestroy(AlsaMixerTransaction*); extern bool AlsaMixerTransactionObjectAdd(AlsaMixerTransaction*, void * object, AlsaTransactionDestructor destructor); extern bool AlsaMixerTransactionObjectAddTail(AlsaMixerTransaction* list, void* object, AlsaTransactionDestructor destructor); -extern void AlsaMixerTransactionObjectForget(AlsaMixerTransaction* list, void * object); - -extern void AlsaMixerTransactionDoCleanup(AlsaMixerTransaction*); +extern void AlsaMixerTransactionObjectDelete(AlsaMixerTransaction* list, void * object, bool destructor); extern bool AlsaMixerTransactionVerbCreate(AlsaMixerTransaction*); -- cgit 1.2.3-korg From eb45566268d0bbb7df9e30f733a95d79275eb3a7 Mon Sep 17 00:00:00 2001 From: Thierry Bultel Date: Wed, 13 Feb 2019 11:44:02 +0100 Subject: alsa-api-pcm: added an 'optional' parameter This adds a boolean 'optional' parameter for both playback and capture devices. When the device is not detected, the stream(s) that use is are not created, without leading to the exit of the softmixer. Change-Id: I3effbd61bfe1d1d4a7cde573354b9db791f759cc Signed-off-by: Thierry Bultel --- plugins/alsa/alsa-api-pcm.c | 20 +++++++++++++++++--- plugins/alsa/alsa-api-streams.c | 21 +++++++++++++++++++-- plugins/alsa/alsa-softmixer.h | 5 +++++ 3 files changed, 41 insertions(+), 5 deletions(-) diff --git a/plugins/alsa/alsa-api-pcm.c b/plugins/alsa/alsa-api-pcm.c index 50246e1..0421119 100644 --- a/plugins/alsa/alsa-api-pcm.c +++ b/plugins/alsa/alsa-api-pcm.c @@ -392,6 +392,9 @@ PUBLIC void ApiPcmParamsShow(SoftMixerT * mixer, const char *msg, const AlsaPcmH PUBLIC AlsaPcmHwInfoT * ApiPcmSetParams(SoftMixerT *mixer, const char *uid, json_object * paramsJ) { const char *format = NULL, *access = NULL; + + AFB_API_NOTICE(mixer->api, "%s for %s: params are %s", __func__, uid, json_object_get_string(paramsJ)); + AlsaPcmHwInfoT *params = calloc(1, sizeof (AlsaPcmHwInfoT)); if (params == NULL) { SOFTMIXER_NOMEM(mixer->api); @@ -502,7 +505,9 @@ PUBLIC void ApiPcmDelete(SoftMixerT * mixer, AlsaSndPcmT * pcm) { pcmChannelsDestroy(mixer, pcm); - snd_ctl_close(card->ctl); + if (card->ctl) + snd_ctl_close(card->ctl); + free(card); free ((char*)pcm->mute.name); free ((char*)pcm->volume.name); @@ -554,7 +559,7 @@ PUBLIC AlsaSndPcmT * ApiPcmAttachOne(SoftMixerT *mixer, const char *uid, snd_pcm } CDS_INIT_LIST_HEAD(&pcm->sndcard->registryList); - error = wrap_json_unpack(argsJ, "{ss,s?s,s?s,s?s,s?i,s?i,s?o,s?o,s?o,s?o !}" + error = wrap_json_unpack(argsJ, "{ss,s?s,s?s,s?s,s?i,s?i,s?o,s?o,s?o,s?o,s?b !}" , "uid", &pcm->uid , "pcmplug_params", &pcm->sndcard->cid.pcmplug_params , "path", &pcm->sndcard->cid.devpath @@ -565,9 +570,10 @@ PUBLIC AlsaSndPcmT * ApiPcmAttachOne(SoftMixerT *mixer, const char *uid, snd_pcm , "source", &sourceJ , "params", ¶msJ , "quirks", &quirksJ + , "optional", &pcm->optional ); if (error) { -(??) AFB_ApiError(mixer->api, "%s: hal=%s missing 'uid|path|cardid|device|sink|source|params|quirks' error=%s args=%s", + AFB_ApiError(mixer->api, "%s: hal=%s missing 'uid|path|cardid|device|sink|source|params|quirks|optional' error=%s args=%s", __func__, uid, wrap_json_get_error_string(error), json_object_get_string(argsJ)); goto fail_pcm_sndcard; } @@ -577,9 +583,17 @@ PUBLIC AlsaSndPcmT * ApiPcmAttachOne(SoftMixerT *mixer, const char *uid, snd_pcm else pcm->isPcmPlug = false; + pcm->sndcard->optional = pcm->optional; + // try to open sound card control interface pcm->sndcard->ctl = AlsaByPathOpenCtl(mixer, pcm->uid, pcm->sndcard); if (!pcm->sndcard->ctl) { + if (pcm->optional) { + + AFB_API_INFO(mixer->api, "%s: hal=%s Fail to open OPTIONAL sndcard uid=%s devpath=%s cardid=%s", + __func__, uid, pcm->uid, pcm->sndcard->cid.devpath, pcm->sndcard->cid.cardid); + goto done; + } AFB_ApiError(mixer->api, "%s: hal=%s Fail to open sndcard uid=%s devpath=%s cardid=%s", __func__, uid, pcm->uid, pcm->sndcard->cid.devpath, pcm->sndcard->cid.cardid); diff --git a/plugins/alsa/alsa-api-streams.c b/plugins/alsa/alsa-api-streams.c index 7241f41..43c14da 100644 --- a/plugins/alsa/alsa-api-streams.c +++ b/plugins/alsa/alsa-api-streams.c @@ -284,6 +284,14 @@ STATIC int CreateOneStream(SoftMixerT *mixer, const char * uid, AlsaStreamAudioT } } + stream->optional = captureCard->optional; + + if (!captureCard->ctl && captureCard->optional) { + stream->noHwDetected = true; + AFB_API_INFO(mixer->api,"%s: no detected capture device", __func__); + goto done; + } + // check PCM is valid and get its full name AlsaPcmCtlT *capturePcm = AlsaByPathOpenPcmCtl(mixer, captureDev, SND_PCM_STREAM_CAPTURE); if (!capturePcm) { @@ -532,7 +540,7 @@ STATIC int CreateOneStream(SoftMixerT *mixer, const char * uid, AlsaStreamAudioT AFB_ApiNotice(mixer->api, "%s: mixer=%s stream=%s CREATED", __func__, mixer->uid, stream->uid); - +done: return 0; OnErrorExit: @@ -668,6 +676,9 @@ static void streamDestroy(SoftMixerT * mixer, void * arg) { int error = 0; AFB_ApiDebug(mixer->api, "%s... %s", __func__, stream->uid); + if (stream->noHwDetected) + goto freemem; + error = afb_api_del_verb(mixer->api, stream->uid, (void**)stream->verbApiHandle); if (error) { AFB_ApiDebug(mixer->api, "%s: failed to remove verb %s", __func__, stream->uid); @@ -675,6 +686,7 @@ static void streamDestroy(SoftMixerT * mixer, void * arg) { AlsaPcmCopyStop(mixer, stream->copy); +freemem: if (stream->softvolConfig) { AFB_ApiDebug(mixer->api, "%s... %s delete softvol config", __func__, stream->uid); snd_config_delete(stream->softvolConfig); @@ -744,6 +756,8 @@ PUBLIC int ApiStreamAttach(SoftMixerT *mixer, AFB_ReqT request, const char * uid AFB_ReqFailF(request, "bad-stream", "mixer=%s invalid stream= %s", mixer->uid, json_object_get_string(argsJ)); goto fail; } + if (newStream->noHwDetected) + AlsaMixerTransactionObjectDelete(mixer->transaction, newStream, true); break; @@ -766,7 +780,10 @@ PUBLIC int ApiStreamAttach(SoftMixerT *mixer, AFB_ReqT request, const char * uid "%s: mixer=%s invalid stream= %s", __func__, mixer->uid, json_object_get_string(streamJ)); goto fail; - } + } + if (newStream->noHwDetected) + AlsaMixerTransactionObjectDelete(mixer->transaction, newStream, true); + } break; default: diff --git a/plugins/alsa/alsa-softmixer.h b/plugins/alsa/alsa-softmixer.h index c73dd0e..d895cef 100644 --- a/plugins/alsa/alsa-softmixer.h +++ b/plugins/alsa/alsa-softmixer.h @@ -211,6 +211,7 @@ typedef struct AlsaSndCtlT_ { long nbRegistry; struct cds_list_head registryList; struct SubscribeHandleT_ * eventSubscribeHandle; + bool optional; } AlsaSndCtlT; @@ -244,6 +245,7 @@ typedef struct { bool isPcmPlug; void * apiVerbHandle; unsigned int quirks; + bool optional; } AlsaSndPcmT; typedef struct { @@ -283,9 +285,12 @@ typedef struct AlsaStreamAudioT_ { AlsaPcmCopyHandleT *copy; struct cds_list_head list; /* link to the global list*/ AlsaPcmCtlT * softvol; + snd_config_t * softvolConfig; snd_config_t * rateConfig; void * verbApiHandle; + bool optional; + bool noHwDetected; } AlsaStreamAudioT; typedef struct SoftMixerT_{ -- cgit 1.2.3-korg From e355716f6bac57615195333559a746e283e22060 Mon Sep 17 00:00:00 2001 From: Thierry Bultel Date: Wed, 13 Feb 2019 11:59:45 +0100 Subject: pcm plugs: rework the alsa config cleanup The alsa config (for softvol, routes, rate conversion ...) was cleaned up by the owner stream or zone, at their deletion time. This was a mistake, because when the stream or zone creation was not complete, the cleanup of the configuration was not done. Instead of doing that, the config is attached to the plug objects as private data (in the AlsaPcmCtlT structure). Since the AlsaPcmCtlT are always recorded in the creation transaction, it is easy to call the cleaning callback (when it exists) when the transaction is deleted. Change-Id: I952871518a20bfe0be6398887bc747338cf574fb Signed-off-by: Thierry Bultel --- plugins/alsa/alsa-api-streams.c | 13 ------------- plugins/alsa/alsa-api-zones.c | 7 ------- plugins/alsa/alsa-plug-dmix.c | 12 +++++++++++- plugins/alsa/alsa-plug-rate.c | 12 ++++++++++-- plugins/alsa/alsa-plug-route.c | 13 +++++++++++-- plugins/alsa/alsa-plug-vol.c | 10 ++++++++-- plugins/alsa/alsa-softmixer.h | 10 +++++++--- plugins/alsa/alsa-utils-bypath.c | 3 +++ 8 files changed, 50 insertions(+), 30 deletions(-) diff --git a/plugins/alsa/alsa-api-streams.c b/plugins/alsa/alsa-api-streams.c index 43c14da..bc5ea6b 100644 --- a/plugins/alsa/alsa-api-streams.c +++ b/plugins/alsa/alsa-api-streams.c @@ -687,19 +687,6 @@ static void streamDestroy(SoftMixerT * mixer, void * arg) { AlsaPcmCopyStop(mixer, stream->copy); freemem: - if (stream->softvolConfig) { - AFB_ApiDebug(mixer->api, "%s... %s delete softvol config", __func__, stream->uid); - snd_config_delete(stream->softvolConfig); - snd_config_update(); - stream->softvolConfig = NULL; - } - - if (stream->rateConfig) { - AFB_ApiDebug(mixer->api, "%s... %s delete rate config", __func__, stream->uid); - snd_config_delete(stream->rateConfig); - snd_config_update(); - stream->rateConfig = NULL; - } free((char*)stream->uid); free((char*)stream->playback); diff --git a/plugins/alsa/alsa-api-zones.c b/plugins/alsa/alsa-api-zones.c index 03a2264..83f6e3a 100644 --- a/plugins/alsa/alsa-api-zones.c +++ b/plugins/alsa/alsa-api-zones.c @@ -200,13 +200,6 @@ static void zoneDestroy(SoftMixerT* mixer, void * arg) { AFB_ApiDebug(mixer->api, "%s... %s (%d sinks, %d sources)", __func__, zone->uid, zone->nbSinks, zone->nbSources); - if (zone->routeConfig) { - AFB_ApiDebug(mixer->api, "%s... %s delete route config", __func__, zone->uid); - snd_config_delete(zone->routeConfig); - snd_config_update(); - zone->routeConfig = NULL; - } - AlsaPcmChannelT * channel, *tmp; cds_list_for_each_entry_safe(channel, tmp, &zone->sinks.list, list) { diff --git a/plugins/alsa/alsa-plug-dmix.c b/plugins/alsa/alsa-plug-dmix.c index 9aaf866..bb58a29 100644 --- a/plugins/alsa/alsa-plug-dmix.c +++ b/plugins/alsa/alsa-plug-dmix.c @@ -24,6 +24,13 @@ static int uniqueIpcIndex = 1024; ALSA_PLUG_PROTO(dmix); +static void dmixConfigClean(SoftMixerT *mixer, void * arg) { + snd_config_t * dmixConfig = arg; + AFB_API_DEBUG(mixer->api, "%s... ", __func__); + snd_config_delete(dmixConfig); + snd_config_update(); +} + PUBLIC AlsaPcmCtlT* AlsaCreateDmix(SoftMixerT *mixer, const char* pcmName, AlsaSndPcmT *pcmSlave, int open) { @@ -92,7 +99,7 @@ PUBLIC AlsaPcmCtlT* AlsaCreateDmix(SoftMixerT *mixer, const char* pcmName, AlsaS if (error) goto OnErrorExit; } - /* It is critical to set the right number of channels ... know. + /* It is critical to set the right number of channels ... now. * Trying to set another value later leads to silent failure * */ @@ -121,6 +128,9 @@ PUBLIC AlsaPcmCtlT* AlsaCreateDmix(SoftMixerT *mixer, const char* pcmName, AlsaS goto OnErrorExit; } + pcmPlug->private_data = dmixConfig; + pcmPlug->private_data_clean = dmixConfigClean; + // Debug config & pcm AlsaDumpCtlConfig(mixer, "plug-dmix", dmixConfig, 1); AFB_ApiNotice(mixer->api, "%s: %s done", __func__, pcmPlug->cid.cardid); diff --git a/plugins/alsa/alsa-plug-rate.c b/plugins/alsa/alsa-plug-rate.c index d4a3068..ea96da2 100644 --- a/plugins/alsa/alsa-plug-rate.c +++ b/plugins/alsa/alsa-plug-rate.c @@ -23,6 +23,13 @@ ALSA_PLUG_PROTO(rate); +static void rateConfigClean(SoftMixerT *mixer, void * arg) { + snd_config_t * rateConfig = arg; + AFB_API_DEBUG(mixer->api, "%s... rate config", __func__); + snd_config_delete(rateConfig); + snd_config_update(); +} + PUBLIC AlsaPcmCtlT* AlsaCreateRate(SoftMixerT *mixer, AlsaStreamAudioT * stream, const char* pcmName, AlsaPcmCtlT *pcmSlave, AlsaPcmHwInfoT *params, int open) { snd_config_t *rateConfig, *slaveConfig, *elemConfig, *pcmConfig; @@ -71,8 +78,6 @@ PUBLIC AlsaPcmCtlT* AlsaCreateRate(SoftMixerT *mixer, AlsaStreamAudioT * stream, goto OnErrorExit; } - stream->rateConfig = rateConfig; - if (open) error = _snd_pcm_rate_open(&pcmPlug->handle, pcmPlug->cid.cardid, snd_config, rateConfig, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK); if (error) { AFB_ApiError(mixer->api, @@ -81,6 +86,9 @@ PUBLIC AlsaPcmCtlT* AlsaCreateRate(SoftMixerT *mixer, AlsaStreamAudioT * stream, goto OnErrorExit; } + pcmPlug->private_data = rateConfig; + pcmPlug->private_data_clean = rateConfigClean; + // Debug config & pcm //AlsaDumpCtlConfig(mixer, "plug-rate", pcmConfig, 1); AlsaDumpCtlConfig (mixer, "plug-rate", rateConfig, 1); diff --git a/plugins/alsa/alsa-plug-route.c b/plugins/alsa/alsa-plug-route.c index d3f22a7..df39698 100644 --- a/plugins/alsa/alsa-plug-route.c +++ b/plugins/alsa/alsa-plug-route.c @@ -23,6 +23,14 @@ ALSA_PLUG_PROTO(route); +static void routeConfigClean(SoftMixerT *mixer, void * arg) { + snd_config_t * routeConfig = arg; + AFB_API_DEBUG(mixer->api, "%s... route config", __func__); + snd_config_delete(routeConfig); + snd_config_update(); +} + + typedef struct { const char *uid; const char *cardid; @@ -81,7 +89,7 @@ OnErrorExit: } PUBLIC AlsaPcmCtlT* AlsaCreateRoute(SoftMixerT *mixer, AlsaSndZoneT *zone, int open) { - snd_config_t *routeConfig, *elemConfig, *slaveConfig, *tableConfig, *pcmConfig; + snd_config_t *routeConfig = NULL, *elemConfig, *slaveConfig, *tableConfig, *pcmConfig; int error = 0; ChannelCardPortT slave, channelCardPort; AlsaPcmCtlT *pcmRoute = NULL; @@ -279,7 +287,8 @@ PUBLIC AlsaPcmCtlT* AlsaCreateRoute(SoftMixerT *mixer, AlsaSndZoneT *zone, int o goto fail; } - zone->routeConfig = routeConfig; + pcmRoute->private_data = routeConfig; + pcmRoute->private_data_clean = routeConfigClean; // Debug config & pcm AFB_ApiNotice(mixer->api, "%s: zone(%s) DONE", __func__, zone->uid); diff --git a/plugins/alsa/alsa-plug-vol.c b/plugins/alsa/alsa-plug-vol.c index 09025c4..ade29ac 100644 --- a/plugins/alsa/alsa-plug-vol.c +++ b/plugins/alsa/alsa-plug-vol.c @@ -22,8 +22,13 @@ ALSA_PLUG_PROTO(softvol); // stream uses softvol plugin -PUBLIC void AlsaDeleteSoftvol(SoftMixerT *mixer, AlsaPcmCtlT * ctl) { + +static void softVolConfigClean(SoftMixerT *mixer, void * arg) { + snd_config_t * softvolConfig = arg; + AFB_API_DEBUG(mixer->api, "%s... softvol config", __func__); + snd_config_delete(softvolConfig); + snd_config_update(); } PUBLIC AlsaPcmCtlT *AlsaCreateSoftvol(SoftMixerT *mixer, AlsaStreamAudioT *stream, char* slaveid, AlsaSndCtlT *sndcard, char* ctlName, int max, int open) { @@ -95,7 +100,8 @@ PUBLIC AlsaPcmCtlT *AlsaCreateSoftvol(SoftMixerT *mixer, AlsaStreamAudioT *strea goto OnErrorExit; } - stream->softvolConfig = streamConfig; + pcmVol->private_data = streamConfig; + pcmVol->private_data_clean = softVolConfigClean; // Debug config & pcm //AlsaDumpCtlConfig (mixer, "plug-config", pcmConfig, 1); diff --git a/plugins/alsa/alsa-softmixer.h b/plugins/alsa/alsa-softmixer.h index d895cef..781411d 100644 --- a/plugins/alsa/alsa-softmixer.h +++ b/plugins/alsa/alsa-softmixer.h @@ -128,6 +128,9 @@ typedef struct { bool closeAtDeletion; // intermediate pcms in the pcm chain must not be closed, else it make libasound abort() bool isPcmPlug; + + void * private_data; + void (*private_data_clean) (struct SoftMixerT_ *, void *); unsigned int quirks; } AlsaPcmCtlT; @@ -224,14 +227,17 @@ typedef struct { int ccount; AlsaPcmHwInfoT *params; struct cds_list_head list; - snd_config_t * routeConfig; + bool isPcmPlug; unsigned int quirks; } AlsaSndZoneT; + /* This is a list of known sound card (hardware or driver) specific bugs */ + /* - do not trust the result of snd_pcm_poll_descriptors_revents */ #define QUIRK_BOGUS_POLL_REVENTS_DEMANGLING (1<<0) + typedef struct { const char *uid; const char *verb; @@ -286,8 +292,6 @@ typedef struct AlsaStreamAudioT_ { struct cds_list_head list; /* link to the global list*/ AlsaPcmCtlT * softvol; - snd_config_t * softvolConfig; - snd_config_t * rateConfig; void * verbApiHandle; bool optional; bool noHwDetected; diff --git a/plugins/alsa/alsa-utils-bypath.c b/plugins/alsa/alsa-utils-bypath.c index 3a8afb7..674b4d6 100644 --- a/plugins/alsa/alsa-utils-bypath.c +++ b/plugins/alsa/alsa-utils-bypath.c @@ -93,6 +93,9 @@ PUBLIC void AlsaPcmCtlDelete(SoftMixerT* mixer, void * arg) { AFB_ApiDebug(mixer->api, "%s of %s (plug: %d)", __func__, pcmCtl->name, pcmCtl->isPcmPlug); + if (pcmCtl->private_data_clean) + pcmCtl->private_data_clean(mixer, pcmCtl->private_data); + if (!pcmCtl->closeAtDeletion) goto done; -- cgit 1.2.3-korg From 293fe6981f55aff68e42b668a565c5db65cc8468 Mon Sep 17 00:00:00 2001 From: Thierry Bultel Date: Wed, 13 Feb 2019 14:32:14 +0100 Subject: core pcm: use the same model for writing and reading audio Reading sound frames relies on usage of poll before calling snd_pcm_writei Recent seeks for bugs and CPU load issues have shown that the same thing must be done on the write side. Thus this commit uses the same mechanism, including the dedicated event pipe for the thread termination. Change-Id: Ifd5407c9e27da7801623583a6f7d4a2e845b43ea Signed-off-by: Thierry Bultel --- plugins/alsa/alsa-core-pcm.c | 237 ++++++++++++++++++++++++++++++------------ plugins/alsa/alsa-softmixer.h | 7 +- 2 files changed, 177 insertions(+), 67 deletions(-) diff --git a/plugins/alsa/alsa-core-pcm.c b/plugins/alsa/alsa-core-pcm.c index a45d463..9d46dcc 100644 --- a/plugins/alsa/alsa-core-pcm.c +++ b/plugins/alsa/alsa-core-pcm.c @@ -169,7 +169,7 @@ PUBLIC int AlsaPcmConf(SoftMixerT *mixer, AlsaPcmCtlT *pcm, int mode) { /* The following code, that * 1) sets period time/size; buffer time/size hardware params - * 2) sets start and stop threashold in software params + * 2) sets start and stop threshold in software params * ... is taken as such from 'aplay' in alsa-utils */ unsigned buffer_time = 0; @@ -275,6 +275,8 @@ PUBLIC int AlsaPcmConf(SoftMixerT *mixer, AlsaPcmCtlT *pcm, int mode) { // available_min is the minimum number of frames available to read, // when the call to poll returns. Assume this is the same for playback (not checked that) + AFB_API_DEBUG(mixer->api, "(%s) try AVAIL_MIN: %ld", card, n); + if ((error = snd_pcm_sw_params_set_avail_min(pcm->handle, pxmSwParams, n)) < 0) { AFB_ApiError(mixer->api, "%s (%s): mixer=%s Fail set_buffersize error=%s", @@ -283,11 +285,12 @@ PUBLIC int AlsaPcmConf(SoftMixerT *mixer, AlsaPcmCtlT *pcm, int mode) { }; snd_pcm_sw_params_get_avail_min(pxmSwParams, &pcm->avail_min); + AFB_API_DEBUG(mixer->api, "(%s) AVAIL_MIN set to : %ld", card, pcm->avail_min); int start_delay = 0; snd_pcm_uframes_t start_threshold; - /* round up to closest transfer boundary */ + /* round up to closest transfer boundary. Start delay is in microseconds */ n = buffer_size; if (start_delay <= 0) { start_threshold = n + (size_t)((double)rate * start_delay / 1000000); @@ -340,13 +343,14 @@ STATIC int AlsaPcmReadCB( AlsaPcmCopyHandleT * pcmCopyHandle) { snd_pcm_uframes_t bufSize = alsa_ringbuf_buffer_size(rbuf); int err; + const char * uid = ALSA_PCM_UID(pcmIn, string); // do we have waiting frames ? availIn = snd_pcm_avail_update(pcmIn); if (availIn <= 0) { if (availIn == -EPIPE) { int ret = xrun(pcmIn, (int)availIn); - AFB_ApiDebug(pcmCopyHandle->api, "XXX read EPIPE (recov=%d) {%s}!", ret, ALSA_PCM_UID(pcmIn, string)); + AFB_ApiDebug(pcmCopyHandle->api, "XXX read EPIPE (recov=%d) {%s}!", ret, uid); // For some (undocumented...) reason, a start is mandatory. snd_pcm_start(pcmIn); @@ -376,14 +380,13 @@ STATIC int AlsaPcmReadCB( AlsaPcmCopyHandleT * pcmCopyHandle) { pthread_mutex_unlock(&pcmCopyHandle->mutex); nbRead = snd_pcm_readi(pcmIn, buf, remain); - if (nbRead == 0) { break; } if (nbRead < 0) { if (nbRead== -EPIPE) { err = xrun(pcmIn, (int)nbRead); - AFB_ApiDebug(pcmCopyHandle->api, "read EPIPE (%d), recov %d", ++pcmCopyHandle->read_err_count, err); + AFB_ApiDebug(pcmCopyHandle->api, "read EPIPE (%d), recov %d {%s}", ++pcmCopyHandle->read_err_count, err, uid); goto ExitOnSuccess; } else if (nbRead== -ESTRPIPE) { AFB_ApiDebug(pcmCopyHandle->api, "read ESTRPIPE"); @@ -391,6 +394,7 @@ STATIC int AlsaPcmReadCB( AlsaPcmCopyHandleT * pcmCopyHandle) { goto ExitOnSuccess; nbRead = 0; } else { + AFB_API_DEBUG(pcmCopyHandle->api, "read error: %ld (%s) {%s}", -nbRead, strerror((int)-nbRead), uid); goto ExitOnSuccess; } } @@ -422,7 +426,6 @@ ExitOnSuccess: static int xrun( snd_pcm_t * pcm, int error) { int err; - if ((err = snd_pcm_recover(pcm, error, 1)) < 0) { return err; } @@ -445,8 +448,8 @@ static int suspend( snd_pcm_t * pcm, int error) static void readSuspend(AlsaPcmCopyHandleT * pcmCopyHandle) { // will be deaf - pcmCopyHandle->saveFd = pcmCopyHandle->pollFds[1].fd; - pcmCopyHandle->pollFds[1].fd = -1; + pcmCopyHandle->saveFd = pcmCopyHandle->pollFdsIn[1].fd; + pcmCopyHandle->pollFdsIn[1].fd = -1; AFB_ApiNotice(pcmCopyHandle->api, "capture muted"); } @@ -454,7 +457,7 @@ static void readSuspend(AlsaPcmCopyHandleT * pcmCopyHandle) { static void readResume(AlsaPcmCopyHandleT * pcmCopyHandle) { // undeaf it - pcmCopyHandle->pollFds[1].fd = pcmCopyHandle->saveFd; + pcmCopyHandle->pollFdsIn[1].fd = pcmCopyHandle->saveFd; snd_pcm_prepare(pcmCopyHandle->pcmIn->handle); snd_pcm_start(pcmCopyHandle->pcmIn->handle); AFB_ApiNotice(pcmCopyHandle->api, "capture unmuted"); @@ -466,32 +469,26 @@ static void *readThreadEntry(void *handle) { AlsaPcmCopyHandleT *pcmCopyHandle = (AlsaPcmCopyHandleT*) handle; pcmCopyHandle->tid = (int) syscall(SYS_gettid); - int ix; AFB_ApiNotice(pcmCopyHandle->api, "%s :%s/%d Started, muted=%d", __func__, pcmCopyHandle->info, pcmCopyHandle->tid, pcmCopyHandle->pcmIn->mute); - struct pollfd * eventFd = &pcmCopyHandle->pollFds[0]; - struct pollfd * framePfds = &pcmCopyHandle->pollFds[1]; - - eventFd->events = POLLIN | POLLHUP; + struct pollfd * eventFd = &pcmCopyHandle->pollFdsIn[0]; + struct pollfd * framePfds = &pcmCopyHandle->pollFdsIn[1]; - for (ix = 0; ix nbPcmFds-1; ix++) { - framePfds[ix].events = POLLIN | POLLHUP; - } + eventFd->events = POLLIN; bool muted = pcmCopyHandle->pcmIn->mute; if (muted) readSuspend(pcmCopyHandle); - /* loop until end */ for (;;) { - int err = poll(pcmCopyHandle->pollFds, pcmCopyHandle->nbPcmFds, LOOP_TIMEOUT_MSEC); - if (err < 0) { + int err = poll(pcmCopyHandle->pollFdsIn, pcmCopyHandle->nbPcmFdsIn, LOOP_TIMEOUT_MSEC); + if (err < 0) { AFB_ApiError(pcmCopyHandle->api, "%s: poll err %s", __func__, strerror(errno)); continue; } @@ -503,7 +500,7 @@ static void *readThreadEntry(void *handle) { } // handle the incoming events/mute order - if ((eventFd->revents & EPOLLIN) != 0) { + if ((eventFd->revents & POLLIN) != 0) { PcmCopyEvent event; size_t ret = read(eventFd->fd, &event, sizeof(event)); @@ -537,8 +534,7 @@ static void *readThreadEntry(void *handle) { unsigned short revents = 0; - int res = snd_pcm_poll_descriptors_revents(pcmCopyHandle->pcmIn->handle, framePfds, pcmCopyHandle->nbPcmFds, &revents); - + int res = snd_pcm_poll_descriptors_revents(pcmCopyHandle->pcmIn->handle, framePfds, pcmCopyHandle->nbPcmFdsIn-1, &revents); if (res == -ENODEV) { sleep(1); continue; @@ -549,6 +545,11 @@ static void *readThreadEntry(void *handle) { continue; } + if (!(revents & POLLIN)) { + AFB_API_DEBUG(pcmCopyHandle->api, "no POLLIN. try again !"); + continue; + } + AlsaPcmReadCB(pcmCopyHandle); } done: @@ -564,23 +565,14 @@ static void *writeThreadEntry(void *handle) { alsa_ringbuf_t * rbuf = pcmCopyHandle->rbuf; - snd_pcm_status_t *pcmOutStatus; - snd_pcm_uframes_t pcmOutSize; - - snd_pcm_sframes_t threshold; - - snd_pcm_status_alloca(&pcmOutStatus); - snd_pcm_status(pcmOut, pcmOutStatus); - pcmOutSize = snd_pcm_status_get_avail_max(pcmOutStatus); - const char * cardid = pcmCopyHandle->pcmOut->cid.cardid; - /* This threshold is the expected space available in the hw output buffer - * The aim is to wait to have a significant amount of space, in order to - * avoid to write to the device too often, or take a very small amount of - * frames from the ring buffer. So basically, this saves some CPU load */ + struct pollfd * eventFd = &pcmCopyHandle->pollFdsOut[0]; + struct pollfd * framePfds = &pcmCopyHandle->pollFdsOut[1]; - threshold = pcmOutSize / 3; + eventFd->events = POLLIN; + + const char * name = pcmCopyHandle->pcmOut->name; for (;;) { @@ -593,6 +585,62 @@ static void *writeThreadEntry(void *handle) { goto done; } + int err = poll(pcmCopyHandle->pollFdsOut, pcmCopyHandle->nbPcmFdsOut, LOOP_TIMEOUT_MSEC); + if (err < 0) { + AFB_API_ERROR(pcmCopyHandle->api, "%s: %s poll out err %s", __func__, name, strerror(errno)); + continue; + } + + // timeout + if (err == 0) + continue; + + // handle the incoming events/mute order + if ((eventFd->revents & POLLIN) != 0) { + PcmCopyEvent event; + + size_t ret = read(eventFd->fd, &event, sizeof(event)); + if (ret <= 0) + continue; + + switch (event.eventType) { + case PCM_COPY_MUTE: + case PCM_COPY_UNMUTE: + break; + case PCM_COPY_END: + AFB_API_DEBUG(pcmCopyHandle->api, "%s ending -> EXIT", __func__); + goto done; + break; + case PCM_COPY_LAST: + default: + AFB_API_ERROR(pcmCopyHandle->api, "%s: Unexpected event 0x%x", __func__, event.eventType); + break; + } + continue; + } + + unsigned short revents = 0; + + int res = snd_pcm_poll_descriptors_revents(pcmCopyHandle->pcmOut->handle, framePfds, pcmCopyHandle->nbPcmFdsOut-1, &revents); + if (res == -ENODEV) { + sleep(1); + continue; + } + + if (revents & POLLHUP) { + AFB_API_NOTICE(pcmCopyHandle->api, "Frame POLLHUP"); + continue; + } + + // not ready ... wait again + if ((revents & POLLOUT) == 0) { + // When that quirk is set, continuing to poll again would block too much time + // and led to a EPIPE + // Instead, we ignore the "non ready state" and perform the write directly + if ((pcmCopyHandle->pcmOut->quirks & QUIRK_BOGUS_POLL_REVENTS_DEMANGLING) == 0) + continue; + } + snd_pcm_sframes_t used, nbWritten; snd_pcm_sframes_t availOut = snd_pcm_avail(pcmOut); @@ -609,15 +657,9 @@ static void *writeThreadEntry(void *handle) { } } - // no space for output - if (availOut <= threshold) { - usleep(500); - continue; - } - pthread_mutex_lock(&pcmCopyHandle->mutex); used = alsa_ringbuf_frames_used(rbuf); - if (used <= 0) { + if (used <= 0) { /* no more frames*/ pthread_mutex_unlock(&pcmCopyHandle->mutex); break; // will wait again } @@ -640,8 +682,11 @@ static void *writeThreadEntry(void *handle) { } else if (nbWritten == -ESTRPIPE) { AFB_ApiDebug(pcmCopyHandle->api, "XXX write ESTRPIPE"); break; + } else if (nbWritten == 0) { + AFB_API_DEBUG(pcmCopyHandle->api, "%s: nothing was written", __func__); + break; } - AFB_ApiDebug(pcmCopyHandle->api, "Unhandled error %s", strerror(errno)); + AFB_ApiDebug(pcmCopyHandle->api, "%s: Unhandled error %s", __func__, strerror((int)-nbWritten)); break; } @@ -676,8 +721,11 @@ static int AlsaPcmCopyEnd(SoftMixerT *mixer, AlsaPcmCopyHandleT * handle) { handle->ending = true; // wake up the reader through the eventfd AlsaPcmCopyEndSignal(mixer, handle->pcmIn); + // wake up the writer through the wait semaphore sem_post(&handle->sem); + // and make in exit from poll + AlsaPcmCopyEndSignal(mixer, handle->pcmOut); return 0; } @@ -700,7 +748,8 @@ PUBLIC int AlsaPcmCopyStop(SoftMixerT *mixer, AlsaPcmCopyHandleT * handle) { alsa_ringbuf_free(handle->rbuf); - free(handle->pollFds); + free(handle->pollFdsIn); + free(handle->pollFdsOut); free(handle); return 0; @@ -791,7 +840,7 @@ PUBLIC int AlsaPcmCopyStart(SoftMixerT *mixer, AlsaStreamAudioT *stream, AlsaPcm cHandle->stream = stream; cHandle->frame_size = (snd_pcm_format_physical_width(opts->format) / 8) * opts->channels; - AFB_ApiDebug(mixer->api, "%s: Frame size is %zu", __func__, cHandle->frame_size); + AFB_ApiDebug(mixer->api, "%s: Frame size is %zu (%d channels)", __func__, cHandle->frame_size, opts->channels); AFB_ApiDebug(mixer->api, "%s: Buffer delay is %d ms", __func__, stream->delayms); snd_pcm_uframes_t nbFrames = (stream->delayms * opts->rate)/1000; @@ -807,6 +856,29 @@ PUBLIC int AlsaPcmCopyStart(SoftMixerT *mixer, AlsaStreamAudioT *stream, AlsaPcm AFB_ApiDebug(mixer->api, "%s Copy buffer: nbframes is %zu", __func__, nbFrames); + // get FD poll descriptor for playback PCM + int pcmOutCount = snd_pcm_poll_descriptors_count(pcmOut->handle); + if (pcmOutCount <= 0) { + AFB_API_ERROR(mixer->api, + "%s: Fail pcmOut=%s get fds count error=%s", + __func__, ALSA_PCM_UID(pcmOut->handle, string), snd_strerror(error)); + goto OnErrorExit; + } + + cHandle->nbPcmFdsOut = pcmOutCount+1; + cHandle->pollFdsOut = (struct pollfd *) malloc((cHandle->nbPcmFdsOut)*sizeof(struct pollfd)); + if (cHandle->pollFdsOut == NULL){ + SOFTMIXER_NOMEM(mixer->api); + goto OnErrorExit; + } + + if ((error = snd_pcm_poll_descriptors(pcmOut->handle, cHandle->pollFdsOut+1, pcmOutCount)) < 0) { + AFB_API_ERROR(mixer->api, + "%s: Fail pcmOut=%s get pollfds error=%s", + __func__, ALSA_PCM_UID(pcmOut->handle, string), snd_strerror(error)); + goto OnErrorExit; + }; + // get FD poll descriptor for capture PCM int pcmInCount = snd_pcm_poll_descriptors_count(pcmIn->handle); if (pcmInCount <= 0) { @@ -816,43 +888,61 @@ PUBLIC int AlsaPcmCopyStart(SoftMixerT *mixer, AlsaStreamAudioT *stream, AlsaPcm goto OnErrorExit; } - cHandle->nbPcmFds = pcmInCount+1; - cHandle->pollFds = (struct pollfd *) malloc((cHandle->nbPcmFds)*sizeof(struct pollfd)); - if (cHandle->pollFds == NULL){ + cHandle->nbPcmFdsIn = pcmInCount+1; + cHandle->pollFdsIn = (struct pollfd *) malloc((cHandle->nbPcmFdsIn)*sizeof(struct pollfd)); + if (cHandle->pollFdsIn == NULL){ SOFTMIXER_NOMEM(mixer->api); goto OnErrorExit; } - if ((error = snd_pcm_poll_descriptors(pcmIn->handle, cHandle->pollFds+1, pcmInCount)) < 0) { + if ((error = snd_pcm_poll_descriptors(pcmIn->handle, cHandle->pollFdsIn+1, pcmInCount)) < 0) { AFB_ApiError(mixer->api, "%s: Fail pcmIn=%s get pollfds error=%s", __func__, ALSA_PCM_UID(pcmOut->handle, string), snd_strerror(error)); goto OnErrorExit; }; - // create the mute pipe - int eventFdPipe[2]; - error = pipe(eventFdPipe); + // create the event pipe for capture + int eventFdPipeIn[2]; + error = pipe(eventFdPipeIn); if (error < 0) { AFB_ApiError(mixer->api, - "Unable to create the mute signaling pipe"); + "Unable to create the mute signaling pipe for capture"); goto OnErrorExit; } - struct pollfd * eventPollFd = &cHandle->pollFds[0]; + struct pollfd * eventPollFdIn = &cHandle->pollFdsIn[0]; // read end - eventPollFd->fd = eventFdPipe[0]; - eventPollFd->events = POLLIN; - eventPollFd->revents = 0; + eventPollFdIn->fd = eventFdPipeIn[0]; + eventPollFdIn->events = POLLIN; + eventPollFdIn->revents = 0; // write end - pcmIn->eventFd = eventFdPipe[1]; + pcmIn->eventFd = eventFdPipeIn[1]; + + // create the event pipe for playback + int eventFdPipeOut[2]; + error = pipe(eventFdPipeOut); + if (error < 0) { + AFB_API_ERROR(mixer->api, + "Unable to create the signaling pipe for playback"); + goto OnErrorExit; + } + + struct pollfd * eventPollFdOut = &cHandle->pollFdsOut[0]; + // read end + eventPollFdOut->fd = eventFdPipeOut[0]; + eventPollFdOut->events = POLLIN; + eventPollFdOut->revents = 0; + + // write end + pcmOut->eventFd = eventFdPipeOut[1]; error = sem_init(&cHandle->sem, 0 , 0); if (error < 0) { AFB_ApiError(mixer->api, - "%s Fail initialize loop semaphore pcmIn=%s err=%d", - __func__, ALSA_PCM_UID(pcmIn->handle, string), error); + "%s Fail to initialize the loop semaphore pcmIn=%s err=%d", + __func__, ALSA_PCM_UID(pcmIn->handle, string), error); goto OnErrorExit; } @@ -910,11 +1000,28 @@ OnErrorExit: AFB_ApiError(mixer->api, "%s: - pcmIn=%s" , __func__, ALSA_PCM_UID(pcmIn->handle, string)); AFB_ApiError(mixer->api, "%s: - pcmOut=%s", __func__, ALSA_PCM_UID(pcmOut->handle, string)); - if (cHandle &&cHandle->pollFds) { - free (cHandle->pollFds); - cHandle->pollFds = NULL; + if (cHandle &&cHandle->pollFdsIn) { + free (cHandle->pollFdsIn); + cHandle->pollFdsIn = NULL; + } + + if (cHandle && cHandle->pollFdsOut) { + free (cHandle->pollFdsOut); + cHandle->pollFdsOut = NULL; } + if (eventFdPipeIn[0]) + close(eventFdPipeIn[0]); + + if (eventFdPipeIn[1]) + close(eventFdPipeIn[1]); + + if (eventFdPipeOut[0]) + close(eventFdPipeOut[0]); + + if (eventFdPipeOut[1]) + close(eventFdPipeOut[1]); + if (cHandle) free(cHandle); diff --git a/plugins/alsa/alsa-softmixer.h b/plugins/alsa/alsa-softmixer.h index 781411d..7b98b5b 100644 --- a/plugins/alsa/alsa-softmixer.h +++ b/plugins/alsa/alsa-softmixer.h @@ -158,8 +158,11 @@ typedef struct { int tid; char* info; - int nbPcmFds; - struct pollfd * pollFds; + int nbPcmFdsIn; + struct pollfd * pollFdsIn; + + int nbPcmFdsOut; + struct pollfd * pollFdsOut; sem_t sem; pthread_mutex_t mutex; -- cgit 1.2.3-korg From 7520fbf30159a02366c56d0e91e5262a6b88fd37 Mon Sep 17 00:00:00 2001 From: Thierry Bultel Date: Wed, 13 Feb 2019 14:37:01 +0100 Subject: core-pcm: fixed channels setting issues Recent tries on the minnow board have show that the setting of channels on a PCM must be done -after- the setting of the rate. When not doing that, there can be issues (really, complicated to find) around the boundaries values (open intervals) on the period bytes. Additionally, the number of channels can now be forced in the json file. Change-Id: I2c20853daadf9ccab06be6e04d1a185440f57780 Signed-off-by: Thierry Bultel --- plugins/alsa/alsa-api-pcm.c | 12 ++++++++---- plugins/alsa/alsa-core-pcm.c | 25 ++++++++++++++++--------- 2 files changed, 24 insertions(+), 13 deletions(-) diff --git a/plugins/alsa/alsa-api-pcm.c b/plugins/alsa/alsa-api-pcm.c index 0421119..464a725 100644 --- a/plugins/alsa/alsa-api-pcm.c +++ b/plugins/alsa/alsa-api-pcm.c @@ -384,8 +384,8 @@ PUBLIC void ApiPcmDelParams(SoftMixerT* mixer, AlsaPcmHwInfoT* params) { } PUBLIC void ApiPcmParamsShow(SoftMixerT * mixer, const char *msg, const AlsaPcmHwInfoT * params) { - AFB_ApiInfo(mixer->api, "%s PARAMS: rate=%d, format=%d, formatString=%s", - msg, params->rate, params->format, params->formatString); + AFB_ApiInfo(mixer->api, "%s PARAMS: rate=%d, format=%d, formatString=%s, channels %d", + msg, params->rate, params->format, params->formatString, params->channels); } @@ -408,10 +408,11 @@ PUBLIC AlsaPcmHwInfoT * ApiPcmSetParams(SoftMixerT *mixer, const char *uid, json if (paramsJ) { int error = - wrap_json_unpack(paramsJ, "{s?i,s?s,s?s !}", + wrap_json_unpack(paramsJ, "{s?i,s?s,s?s,s?i !}", "rate", ¶ms->rate, "format", &format, - "access", &access); + "access", &access, + "channels", ¶ms->channels); if (error) { AFB_ApiError(mixer->api, "%s: sndcard=%s invalid params=%s", __func__, uid, json_object_get_string(paramsJ)); @@ -419,6 +420,9 @@ PUBLIC AlsaPcmHwInfoT * ApiPcmSetParams(SoftMixerT *mixer, const char *uid, json } } + AFB_API_NOTICE(mixer->api, "%s:%s rate set to %d", __func__, uid, params->rate); + AFB_API_NOTICE(mixer->api, "%s:%s channels set to %d", __func__, uid, params->channels); + // default format if (!format) { params->format = SND_PCM_FORMAT_S16_LE; diff --git a/plugins/alsa/alsa-core-pcm.c b/plugins/alsa/alsa-core-pcm.c index 9d46dcc..0e8f86f 100644 --- a/plugins/alsa/alsa-core-pcm.c +++ b/plugins/alsa/alsa-core-pcm.c @@ -136,7 +136,22 @@ PUBLIC int AlsaPcmConf(SoftMixerT *mixer, AlsaPcmCtlT *pcm, int mode) { } } - if (opts->rate > 0 ) { + unsigned int * channels = &opts->channels; + + if (*channels) { + AFB_API_DEBUG(mixer->api, "%s: Attempt to set %d channels", __func__, *channels); + if ((error = snd_pcm_hw_params_set_channels(pcm->handle, pxmHwParams, *channels)) < 0) { + AFB_API_ERROR(mixer->api, + "%s (%s): mixer=%s Set_Channels=%d Fail error=%s", + __func__, card, mixer->uid, *channels, snd_strerror(error)); + + AlsaDumpPcmParams(mixer, pxmHwParams); + goto OnErrorExit; + }; + AFB_API_DEBUG(mixer->api, "%s: CHANNELS SET TO %d", __func__, *channels); + } + + if (opts->rate > 0 ) { AFB_ApiDebug(mixer->api,"%s (%s): set rate to %d", __func__, card, opts->rate); unsigned int pcmRate = opts->rate; @@ -158,14 +173,6 @@ PUBLIC int AlsaPcmConf(SoftMixerT *mixer, AlsaPcmCtlT *pcm, int mode) { } } - if (opts->channels) { - if ((error = snd_pcm_hw_params_set_channels(pcm->handle, pxmHwParams, opts->channels)) < 0) { - AFB_ApiError(mixer->api, - "%s (%s): mixer=%s Set_Channels=%d Fail error=%s", - __func__, card, mixer->uid, opts->channels, snd_strerror(error)); - goto OnErrorExit; - }; - } /* The following code, that * 1) sets period time/size; buffer time/size hardware params -- cgit 1.2.3-korg From c950295c56a93d77734769229571eebef4a2deaa Mon Sep 17 00:00:00 2001 From: Thierry Bultel Date: Wed, 13 Feb 2019 14:37:57 +0100 Subject: streams: improved log output Fixed some incomplete log messages Change-Id: Ib620905043b6ae3628d886cd8b4b3155005e6862 Signed-off-by: Thierry Bultel --- plugins/alsa/alsa-api-streams.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/plugins/alsa/alsa-api-streams.c b/plugins/alsa/alsa-api-streams.c index bc5ea6b..e3d7bf9 100644 --- a/plugins/alsa/alsa-api-streams.c +++ b/plugins/alsa/alsa-api-streams.c @@ -263,7 +263,7 @@ STATIC int CreateOneStream(SoftMixerT *mixer, const char * uid, AlsaStreamAudioT } else { // if capture UID is not present in loop search on known sources - AFB_ApiDebug(mixer->api,"%s: %s not found in loop, look in sources", __func__, uid); + AFB_ApiDebug(mixer->api,"%s: %s not found in loop, look in sources", __func__, stream->source); AlsaSndCtlT *sourceDev = ApiSourceFindSubdev(mixer, stream->source); if (sourceDev) { @@ -278,8 +278,8 @@ STATIC int CreateOneStream(SoftMixerT *mixer, const char * uid, AlsaStreamAudioT __func__, uid, captureDev->pcmplug_params, sourceDev->cid.cardid); } else { AFB_ApiError(mixer->api, - "%s: mixer=%s stream=%s not found in loops/sources", - __func__, mixer->uid, stream->uid); + "%s: mixer=%s stream=%s: %s not found in loops/sources", + __func__, mixer->uid, stream->uid, stream->source); goto OnErrorExit; } } @@ -425,7 +425,7 @@ STATIC int CreateOneStream(SoftMixerT *mixer, const char * uid, AlsaStreamAudioT if ((zone->params->rate != stream->params->rate) || (zone->params->format != stream->params->format)) { AFB_ApiNotice(mixer->api, - "%s: Instanciate a RATE CONVERTER (stream [rate %d,%s(%d),%d channels], zone [rate %d,%s(%d), %d channels])", + "%s: Instantiate a RATE CONVERTER (stream [rate %d,%s(%d),%d channels], zone [rate %d,%s(%d), %d channels])", __func__, stream->params->rate, stream->params->formatString, -- cgit 1.2.3-korg From 622096c841403b4756f04692cd4fa20eff5417ed Mon Sep 17 00:00:00 2001 From: Thierry Bultel Date: Wed, 13 Feb 2019 14:40:03 +0100 Subject: plug route: added a calculation for the number of physical channels Adds (mainly for debug purpose) a calculation on the actual number of physical channels that are used by the created ttable Change-Id: I5717046003851ebe297a494dcdd97c4d67e34d0b Signed-off-by: Thierry Bultel --- plugins/alsa/alsa-plug-route.c | 21 ++++++++++++++++----- plugins/alsa/alsa-softmixer.h | 1 + 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/plugins/alsa/alsa-plug-route.c b/plugins/alsa/alsa-plug-route.c index df39698..021ef44 100644 --- a/plugins/alsa/alsa-plug-route.c +++ b/plugins/alsa/alsa-plug-route.c @@ -167,11 +167,15 @@ PUBLIC AlsaPcmCtlT* AlsaCreateRoute(SoftMixerT *mixer, AlsaSndZoneT *zone, int o snd_config_t **cports = alloca(zone->nbSinks * sizeof (void*)); memset(cports, 0, zone->nbSinks * sizeof (void*)); - int zcount = 0; + int portCount = 0; // We create 1st ttable to retrieve sndcard slave and channel count (void)snd_config_make_compound(&tableConfig, "ttable", 0); + // used physical ports + bool targets[16]; + memset(targets, 0, sizeof(targets)); + cds_list_for_each_entry(channel, &zone->sinks.list, list) { AFB_ApiDebug(mixer->api, "%s: zone->sink channel %s ", __func__, channel->uid); @@ -192,12 +196,17 @@ PUBLIC AlsaPcmCtlT* AlsaCreateRoute(SoftMixerT *mixer, AlsaSndZoneT *zone, int o int target = channelCardPort.port; int port = channel->port; + if (!targets[target]) { + zone->physicalChannelCount++; + targets[target] = true; + } + double volume = 1.0; // currently only support 100% // if channel entry does not exist into ttable create it now if (!cports[port]) { - char channelS[4]; // 999 channel should be more than enough + char channelS[4]; // 999 channels should be more than enough snprintf(channelS, sizeof (channelS), "%d", port); error = snd_config_make_compound(&cports[port], channelS, 0); @@ -213,7 +222,7 @@ PUBLIC AlsaPcmCtlT* AlsaCreateRoute(SoftMixerT *mixer, AlsaSndZoneT *zone, int o } } - zcount++; + portCount++; // ttable require target port as a table and volume as a value char targetS[4]; @@ -239,8 +248,10 @@ PUBLIC AlsaPcmCtlT* AlsaCreateRoute(SoftMixerT *mixer, AlsaSndZoneT *zone, int o } // update zone with route channel count and sndcard params - pcmRoute->ccount = zcount; - zone->ccount=zcount; + pcmRoute->ccount = portCount; + zone->ccount=portCount; + + AFB_API_DEBUG(mixer->api, "%s: ZONE has %d ports and %d hardware channels", __func__, zone->ccount, zone->physicalChannelCount); // refresh global alsalib config and create PCM top config snd_config_update(); diff --git a/plugins/alsa/alsa-softmixer.h b/plugins/alsa/alsa-softmixer.h index 7b98b5b..ea3e201 100644 --- a/plugins/alsa/alsa-softmixer.h +++ b/plugins/alsa/alsa-softmixer.h @@ -228,6 +228,7 @@ typedef struct { unsigned int nbSinks; AlsaPcmChannelT sinks; int ccount; + int physicalChannelCount; AlsaPcmHwInfoT *params; struct cds_list_head list; -- cgit 1.2.3-korg From 43df82896d8535f99233b4d3ab35a744b07871e0 Mon Sep 17 00:00:00 2001 From: Thierry Bultel Date: Wed, 20 Feb 2019 12:24:41 +0100 Subject: loops/avirt: forget saved loops after creation avirt needs a backup of loops, before creating the streams. However, this breaks software dynamic streams such as those of of type bluez-alsa, because the logic was re-calling the loop creation at each call of the 'attach' verb. The fix simply consists in forgetting the saved loops once they are created Bug-AGL: SPEC-2387 Change-Id: I63f492b89233bed12de583de6e1077ac1c9c3ccf Signed-off-by: Thierry Bultel --- plugins/alsa/alsa-api-loop.c | 11 ++++++----- plugins/alsa/alsa-api-mixer.c | 6 +++++- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/plugins/alsa/alsa-api-loop.c b/plugins/alsa/alsa-api-loop.c index bc3c62a..a4b19f5 100644 --- a/plugins/alsa/alsa-api-loop.c +++ b/plugins/alsa/alsa-api-loop.c @@ -445,10 +445,12 @@ PUBLIC int ApiLoopAttach(SoftMixerT *mixer, AFB_ReqT request, const char * uid, AlsaSndLoopT * newLoop = NULL; + AFB_API_INFO(mixer->api, "%s: %s", __func__, json_object_get_string(argsJ)); + if (mixer->nbLoops >= mixer->max.loops) { - AFB_IfReqFailF(mixer, request, "too-small", "mixer=%s hal=%s max loop=%d", mixer->uid, uid, mixer->max.loops); + AFB_IfReqFailF(mixer, request, "too many loops", "mixer=%s hal=%s max loop=%d", mixer->uid, uid, mixer->max.loops); goto fail; - } + } switch (json_object_get_type(argsJ)) { size_t count; @@ -464,14 +466,14 @@ PUBLIC int ApiLoopAttach(SoftMixerT *mixer, AFB_ReqT request, const char * uid, count = json_object_array_length(argsJ); if (count > (mixer->max.loops - mixer->nbLoops)) { AFB_IfReqFailF(mixer, request, "too-small", "mixer=%s hal=%s max loop=%d", mixer->uid, uid, mixer->max.loops); - goto fail_loop; + goto fail; } for (int idx = 0; idx < count; idx++) { json_object *loopJ = json_object_array_get_idx(argsJ, idx); newLoop = loopCreate(mixer, uid, loopJ, streamsJ); if (newLoop == NULL) { - goto fail_loop; + goto fail; } } break; @@ -482,7 +484,6 @@ PUBLIC int ApiLoopAttach(SoftMixerT *mixer, AFB_ReqT request, const char * uid, return 0; -fail_loop: fail: return -1; } diff --git a/plugins/alsa/alsa-api-mixer.c b/plugins/alsa/alsa-api-mixer.c index c000ea8..15dae20 100644 --- a/plugins/alsa/alsa-api-mixer.c +++ b/plugins/alsa/alsa-api-mixer.c @@ -618,7 +618,11 @@ STATIC void MixerAttachVerb(AFB_ReqT request) { AFB_ApiInfo(mixer->api, "%s set LOOPS/AVIRT", __func__); error = ApiLoopAttach(mixer, request, uid, ((loopsJ) ? loopsJ : LoopsJ), streamsJ); - if (error) goto fail_loop; + if (error) + goto fail; + // Now, forget the saved LoopsJ + if (LoopsJ) + LoopsJ = NULL; } AFB_ApiInfo(mixer->api, "%s set RAMPS", __func__); -- cgit 1.2.3-korg