From e0f57e523112e1bc73a04e8615d7a21355f0ce0e Mon Sep 17 00:00:00 2001 From: Thierry Bultel Date: Tue, 4 Dec 2018 23:11:20 +0100 Subject: Add support for bluetooth telephony This adds support for bluetooth telephony. A big rework in the softmixer internals has been mandatory, in order to support dynamic streams creation and deletions. Bluetooth telephony relies on the recent evolutions of bluez-alsa, the most important one being the support of HFP over Ofono. The softmixer opens PCM ioplugs provided by bluez-alsa. Bluetooth SCO needs 2 streams, one for listening and the other for talking. These streams are created upon requests sent by the hal-manager. The hal manager subscribes for bluez-alsa events and request the list of availalble transports. For each "attach" transaction verb, the softmixer maintains a list of the all created objects (sources, sinks, zones, ramps, streams, and more) Additionnally, it creates a new verb when the attach succeeds, that verb is typically something like "sco_XX:XX:XX:XX:XX:XX", and the only supported action at the present time is {"action":"remove"}, that performs all the cleanup of the registered objects. Change-Id: I1b119e6c079e60daf771e63c083a1ef33a39f379 Signed-off-by: Thierry Bultel --- plugins/alsa/alsa-api-mixer.c | 342 +++++++++++++++++++++--------------------- 1 file changed, 170 insertions(+), 172 deletions(-) (limited to 'plugins/alsa/alsa-api-mixer.c') diff --git a/plugins/alsa/alsa-api-mixer.c b/plugins/alsa/alsa-api-mixer.c index 171046b..ae08461 100644 --- a/plugins/alsa/alsa-api-mixer.c +++ b/plugins/alsa/alsa-api-mixer.c @@ -19,58 +19,15 @@ #define _GNU_SOURCE // needed for vasprintf #include "alsa-softmixer.h" -#include "alsa-bluez.h" #include #include -extern Lua2cWrapperT Lua2cWrap; static void MixerRemoveVerb(AFB_ReqT request) { - SoftMixerT *mixer = (SoftMixerT*) afb_req_get_vcbdata(request); - int error; - - for (int idx = 0; mixer->streams[idx]->uid; idx++) { - AlsaStreamAudioT *stream = mixer->streams[idx]; - AlsaPcmCopyHandleT * copy = stream->copy; - - AFB_ApiNotice(mixer->api, "cleaning mixer=%s stream=%s", mixer->uid, stream->uid); - - error = pthread_cancel(stream->copy->rthread); - if (error) { - AFB_ReqFailF(request, "internal-error", "Fail to kill audio-stream threads mixer=%s", mixer->uid); - goto OnErrorExit; - } - - error = afb_api_del_verb(mixer->api, stream->uid, (void**)&mixer); - if (error) { - AFB_ReqFailF(request, "internal-error", "Mixer=%s fail to remove verb=%s error=%s", mixer->uid, stream->uid, strerror(error)); - goto OnErrorExit; - } - - // free audio-stream dynamic structures - snd_pcm_close(copy->pcmIn->handle); - snd_pcm_close(copy->pcmOut->handle); - if (copy->evtsrc) sd_event_source_unref(copy->evtsrc); - if (copy->sdLoop) sd_event_unref(copy->sdLoop); - } - - // // (Fulup to be Done) registry is attached to source - // if (mixer->sources) ApiSourcFree (mixer); - // if (mixer->sinks) ApiSinkFree (mixer); - // if (mixer->loops) ApiLoopFree (mixer); - // if (mixer->ramps) ApiRampFree (mixer); - // if (mixer->zones) ApiZoneFree (mixer); - - // finally free mixer handle - free(mixer); - AFB_ReqSuccess(request, NULL, "Fulup: delete might not clean everything properly"); - + AFB_ReqFailF(request, "internal-error", "Function not implemented"); return; - -OnErrorExit: - AFB_ReqFail(request, "internal-error", "fail to delete mixer"); } STATIC json_object *MixerInfoOneStream(AlsaStreamAudioT *stream, int verbose) { @@ -100,8 +57,7 @@ STATIC json_object * MixerInfoStreams(SoftMixerT *mixer, json_object *streamsJ, const char * key; json_object *valueJ; json_object *responseJ = NULL; - AlsaStreamAudioT **streams = mixer->streams; - + AlsaStreamAudioT * stream; switch (json_object_get_type(streamsJ)) { @@ -109,17 +65,18 @@ STATIC json_object * MixerInfoStreams(SoftMixerT *mixer, json_object *streamsJ, case json_type_boolean: // list every existing stream responseJ = json_object_new_array(); - for (int idx = 0; streams[idx]; idx++) { - valueJ = MixerInfoOneStream(streams[idx], verbose); + cds_list_for_each_entry(stream, &mixer->streams.list, list) { + valueJ = MixerInfoOneStream(stream, verbose); json_object_array_add(responseJ, valueJ); } break; case json_type_string: key = json_object_get_string(streamsJ); - for (int idx = 0; streams[idx]; idx++) { - if (strcasecmp(streams[idx]->uid, key)) continue; - responseJ = MixerInfoOneStream(streams[idx], verbose); + cds_list_for_each_entry(stream, &mixer->streams.list, list) { + if (strcasecmp(stream->uid, key)) + continue; + responseJ = MixerInfoOneStream(stream, verbose); break; } break; @@ -132,9 +89,10 @@ STATIC json_object * MixerInfoStreams(SoftMixerT *mixer, json_object *streamsJ, __func__, json_object_get_string(streamsJ), wrap_json_get_error_string(error), wrap_json_get_error_position(error)); goto OnErrorExit; } - for (int idx = 0; streams[idx]; idx++) { - if (strcasecmp(streams[idx]->uid, key)) continue; - responseJ = MixerInfoOneStream(streams[idx], verbose); + cds_list_for_each_entry(stream, &mixer->streams.list, list) { + if (strcasecmp(stream->uid, key)) + continue; + responseJ = MixerInfoOneStream(stream, verbose); break; } break; @@ -147,8 +105,8 @@ STATIC json_object * MixerInfoStreams(SoftMixerT *mixer, json_object *streamsJ, valueJ = MixerInfoStreams(mixer, streamJ, verbose); if (!valueJ) { AFB_ApiError(mixer->api, - "%s: fail to find stream=%s", - __func__, json_object_get_string(streamsJ)); + "%s: failed to find stream=%s", + __func__, json_object_get_string(streamJ)); goto OnErrorExit; } json_object_array_add(responseJ, valueJ); @@ -156,7 +114,7 @@ STATIC json_object * MixerInfoStreams(SoftMixerT *mixer, json_object *streamsJ, break; default: - AFB_ApiError(mixer->api, "MixerInfoStreams: unsupported json type streamsJ=%s", json_object_get_string(streamsJ)); + AFB_ApiError(mixer->api, "%s: unsupported json type streamsJ=%s", __func__, json_object_get_string(streamsJ)); goto OnErrorExit; } @@ -187,7 +145,7 @@ STATIC json_object *MixerInfoRamps(SoftMixerT *mixer, json_object *rampsJ, int v const char * key; json_object *valueJ; json_object *responseJ = NULL; - AlsaVolRampT **ramps = mixer->ramps; + AlsaVolRampT * ramp; switch (json_object_get_type(rampsJ)) { @@ -195,18 +153,18 @@ STATIC json_object *MixerInfoRamps(SoftMixerT *mixer, json_object *rampsJ, int v case json_type_boolean: // list every existing ramp responseJ = json_object_new_array(); - for (int idx = 0; ramps[idx]; idx++) { + cds_list_for_each_entry(ramp, &mixer->ramps.list, list) { - valueJ = MixerInfoOneRamp(ramps[idx], verbose); + valueJ = MixerInfoOneRamp(ramp, verbose); json_object_array_add(responseJ, valueJ); } break; case json_type_string: key = json_object_get_string(rampsJ); - for (int idx = 0; ramps[idx]; idx++) { - if (strcasecmp(ramps[idx]->uid, key)) continue; - responseJ = MixerInfoOneRamp(ramps[idx], verbose); + cds_list_for_each_entry(ramp, &mixer->ramps.list, list) { + if (strcasecmp(ramp->uid, key)) continue; + responseJ = MixerInfoOneRamp(ramp, verbose); break; } break; @@ -214,12 +172,13 @@ STATIC json_object *MixerInfoRamps(SoftMixerT *mixer, json_object *rampsJ, int v case json_type_object: error = wrap_json_unpack(rampsJ, "{ss}", "uid", &key); if (error) { - AFB_ApiError(mixer->api, "MixerInfoRamps: missing 'uid' request rampJ=%s error=%s position=%d", json_object_get_string(rampsJ), wrap_json_get_error_string(error), wrap_json_get_error_position(error)); + AFB_ApiError(mixer->api, "%s: missing 'uid' request rampJ=%s error=%s position=%d", + __func__, json_object_get_string(rampsJ), wrap_json_get_error_string(error), wrap_json_get_error_position(error)); goto OnErrorExit; } - for (int idx = 0; ramps[idx]; idx++) { - if (strcasecmp(ramps[idx]->uid, key)) continue; - responseJ = MixerInfoOneRamp(ramps[idx], verbose); + cds_list_for_each_entry(ramp, &mixer->ramps.list, list) { + if (strcasecmp(ramp->uid, key)) continue; + responseJ = MixerInfoOneRamp(ramp, verbose); break; } break; @@ -231,7 +190,7 @@ STATIC json_object *MixerInfoRamps(SoftMixerT *mixer, json_object *rampsJ, int v valueJ = MixerInfoRamps(mixer, rampJ, verbose); if (!valueJ) { - AFB_ApiError(mixer->api, "MixerInfoRamps: fail to find ramp=%s", json_object_get_string(rampsJ)); + AFB_ApiError(mixer->api, "%s: fail to find ramp=%s", __func__, json_object_get_string(rampsJ)); goto OnErrorExit; } json_object_array_add(responseJ, valueJ); @@ -239,7 +198,7 @@ STATIC json_object *MixerInfoRamps(SoftMixerT *mixer, json_object *rampsJ, int v break; default: - AFB_ApiError(mixer->api, "MixerInfoRamps: unsupported json type rampsJ=%s", json_object_get_string(rampsJ)); + AFB_ApiError(mixer->api, "%s: unsupported json type rampsJ=%s", __func__, json_object_get_string(rampsJ)); goto OnErrorExit; } @@ -252,30 +211,34 @@ OnErrorExit: STATIC json_object *MixerInfoOneZone(AlsaSndZoneT *zone, int verbose) { json_object *responseJ; + AlsaPcmChannelT * channel; + if (!verbose) { wrap_json_pack(&responseJ, "{ss}", "uid", zone->uid); } else { json_object *responseJ = json_object_new_object(); - if (zone->sinks) { + if (zone->nbSinks > 0) { json_object *sinksJ = json_object_new_array(); - for (int jdx = 0; zone->sinks[jdx]; jdx++) { + + cds_list_for_each_entry(channel, &zone->sinks.list, list) { json_object *channelJ; wrap_json_pack(&channelJ, "{ss,si}" - , "uid", zone->sinks[jdx]->uid - , "port", zone->sinks[jdx]->port + , "uid", channel->uid + , "port", channel->port ); json_object_array_add(sinksJ, channelJ); } json_object_object_add(responseJ, "sinks", sinksJ); } - if (zone->sources) { + if (zone->nbSources > 0) { json_object *sourcesJ = json_object_new_array(); - for (int jdx = 0; zone->sources[jdx]; jdx++) { + + cds_list_for_each_entry(channel, &zone->sources.list, list) { json_object *channelJ; wrap_json_pack(&channelJ, "{ss,si}" - , "uid", zone->sources[jdx]->uid - , "port", zone->sources[jdx]->port + , "uid", channel->uid + , "port", channel->port ); json_object_array_add(sourcesJ, channelJ); } @@ -300,7 +263,8 @@ STATIC json_object *MixerInfoZones(SoftMixerT *mixer, json_object *zonesJ, int v const char * key; json_object *valueJ; json_object *responseJ = NULL; - AlsaSndZoneT **zones = mixer->zones; + + AlsaSndZoneT *zone; switch (json_object_get_type(zonesJ)) { @@ -308,18 +272,18 @@ STATIC json_object *MixerInfoZones(SoftMixerT *mixer, json_object *zonesJ, int v case json_type_boolean: // list every existing zone responseJ = json_object_new_array(); - for (int idx = 0; zones[idx]; idx++) { - - valueJ = MixerInfoOneZone(zones[idx], verbose); + cds_list_for_each_entry(zone, &mixer->zones.list, list) { + valueJ = MixerInfoOneZone(zone, verbose); json_object_array_add(responseJ, valueJ); } break; case json_type_string: key = json_object_get_string(zonesJ); - for (int idx = 0; zones[idx]; idx++) { - if (strcasecmp(zones[idx]->uid, key)) continue; - responseJ = MixerInfoOneZone(zones[idx], verbose); + cds_list_for_each_entry(zone, &mixer->zones.list, list) { + if (strcasecmp(zone->uid, key)) + continue; + responseJ = MixerInfoOneZone(zone, verbose); break; } break; @@ -332,9 +296,9 @@ STATIC json_object *MixerInfoZones(SoftMixerT *mixer, json_object *zonesJ, int v __func__ ,json_object_get_string(zonesJ), wrap_json_get_error_string(error), wrap_json_get_error_position(error)); goto OnErrorExit; } - for (int idx = 0; zones[idx]; idx++) { - if (strcasecmp(zones[idx]->uid, key)) continue; - responseJ = MixerInfoOneZone(zones[idx], verbose); + cds_list_for_each_entry(zone, &mixer->zones.list, list) { + if (strcasecmp(zone->uid, key)) continue; + responseJ = MixerInfoOneZone(zone, verbose); break; } break; @@ -346,7 +310,7 @@ STATIC json_object *MixerInfoZones(SoftMixerT *mixer, json_object *zonesJ, int v valueJ = MixerInfoZones(mixer, zoneJ, verbose); if (!valueJ) { - AFB_ApiError(mixer->api, "MixerInfoZones: fail to find zone=%s", json_object_get_string(zonesJ)); + AFB_ApiError(mixer->api, "%s: fail to find zone=%s", __func__, json_object_get_string(zonesJ)); goto OnErrorExit; } json_object_array_add(responseJ, valueJ); @@ -354,11 +318,11 @@ STATIC json_object *MixerInfoZones(SoftMixerT *mixer, json_object *zonesJ, int v break; default: - AFB_ApiError(mixer->api, "MixerInfoZones: unsupported json type zonesJ=%s", json_object_get_string(zonesJ)); + AFB_ApiError(mixer->api, "%s: unsupported json type zonesJ=%s", __func__, json_object_get_string(zonesJ)); goto OnErrorExit; } - AFB_ApiNotice(mixer->api, "MixerInfoZones: response=%s", json_object_get_string(responseJ)); + AFB_ApiNotice(mixer->api, "%s: response=%s", __func__, json_object_get_string(responseJ)); return (responseJ); OnErrorExit: @@ -384,7 +348,7 @@ STATIC json_object *MixerInfoOnePcm(AlsaSndPcmT *pcm, int verbose) { wrap_json_pack(&alsaJ, "{ss,ss,so}" , "volume", pcm->volume , "mute", pcm->mute - , "ccount", pcm->ccount + , "ccount", pcm->nbChannels ); wrap_json_pack(&responseJ, "{ss,ss,so,so}" , "uid", pcm->uid @@ -401,18 +365,18 @@ STATIC json_object *MixerInfoPcms(SoftMixerT *mixer, json_object *pcmsJ, snd_pcm const char * key; json_object *valueJ; json_object *responseJ = NULL; - AlsaSndPcmT **pcms; + AlsaSndPcmT * pcms = NULL, *pcm; switch (direction) { case SND_PCM_STREAM_PLAYBACK: - pcms = mixer->sinks; + pcms = &mixer->sinks; break; case SND_PCM_STREAM_CAPTURE: - pcms = mixer->sources; + pcms = &mixer->sources; break; default: - AFB_ApiError(mixer->api, "MixerInfoPcms: invalid Direction should be SND_PCM_STREAM_PLAYBACK|SND_PCM_STREAM_capture"); + AFB_ApiError(mixer->api, "%s: invalid Direction should be SND_PCM_STREAM_PLAYBACK|SND_PCM_STREAM_capture", __func__); goto OnErrorExit; } @@ -422,18 +386,18 @@ STATIC json_object *MixerInfoPcms(SoftMixerT *mixer, json_object *pcmsJ, snd_pcm case json_type_boolean: // list every existing pcm responseJ = json_object_new_array(); - for (int idx = 0; pcms[idx]; idx++) { - - valueJ = MixerInfoOnePcm(pcms[idx], verbose); + cds_list_for_each_entry(pcm, &pcms->list, list) { + valueJ = MixerInfoOnePcm(pcm, verbose); json_object_array_add(responseJ, valueJ); } break; case json_type_string: key = json_object_get_string(pcmsJ); - for (int idx = 0; pcms[idx]; idx++) { - if (strcasecmp(pcms[idx]->uid, key)) continue; - responseJ = MixerInfoOnePcm(pcms[idx], verbose); + cds_list_for_each_entry(pcm, &pcms->list, list) { + if (strcasecmp(pcm->uid, key)) + continue; + responseJ = MixerInfoOnePcm(pcm, verbose); break; } break; @@ -446,9 +410,10 @@ STATIC json_object *MixerInfoPcms(SoftMixerT *mixer, json_object *pcmsJ, snd_pcm __func__, json_object_get_string(pcmsJ), wrap_json_get_error_string(error), wrap_json_get_error_position(error)); goto OnErrorExit; } - for (int idx = 0; pcms[idx]; idx++) { - if (strcasecmp(pcms[idx]->uid, key)) continue; - responseJ = MixerInfoOnePcm(pcms[idx], verbose); + cds_list_for_each_entry(pcm, &pcms->list, list) { + if (strcasecmp(pcm->uid, key)) + continue; + responseJ = MixerInfoOnePcm(pcm, verbose); break; } break; @@ -547,6 +512,7 @@ STATIC void MixerAttachVerb(AFB_ReqT request) { json_object *argsJ = afb_req_json(request); json_object *responseJ = json_object_new_object(); int error; + AlsaMixerTransaction * transaction = NULL; error = wrap_json_unpack(argsJ, "{ss,s?s,s?s,s?o,s?o,s?o,s?o,s?o,s?o !}" , "uid", &uid @@ -562,35 +528,43 @@ STATIC void MixerAttachVerb(AFB_ReqT request) { if (error) { AFB_ReqFailF(request, "invalid-syntax", - "mixer=%s missing 'uid|ramps|playbacks|captures|zones|streams' error=%s args=%s", - mixer->uid, wrap_json_get_error_string(error), json_object_get_string(argsJ)); - goto OnErrorExit; + "%s mixer=%s missing 'uid|ramps|playbacks|captures|zones|streams' error=%s args=%s", + __func__, mixer->uid, wrap_json_get_error_string(error), json_object_get_string(argsJ)); + goto fail; + } + + transaction = AlsaMixerTransactionNew(mixer, uid); + if (transaction == NULL) { + SOFTMIXER_NOMEM(mixer->api); + goto fail; } + mixer->transaction = transaction; + AFB_ApiInfo(mixer->api, "%s set LOOPS", __func__); if (loopsJ) { error = ApiLoopAttach(mixer, request, uid, loopsJ); - if (error) goto OnErrorExit; + if (error) goto fail; } - AFB_ApiInfo(mixer->api, "%s set PLAYBACK", __func__); + AFB_ApiInfo(mixer->api, "%s set PLAYBACKS", __func__); if (playbacksJ) { error = ApiSinkAttach(mixer, request, uid, playbacksJ); - if (error) goto OnErrorExit; + if (error) goto fail_loop; json_object *resultJ = MixerInfoPcms(mixer, playbacksJ, SND_PCM_STREAM_PLAYBACK, 0); json_object_object_add(responseJ, "playbacks", resultJ); } - AFB_ApiInfo(mixer->api, "%s set CAPTURE", __func__); + AFB_ApiInfo(mixer->api, "%s set CAPTURES", __func__); if (capturesJ) { error = ApiSourceAttach(mixer, request, uid, capturesJ); if (error) { AFB_ApiError(mixer->api,"%s: source attach failed", __func__); - goto OnErrorExit; + goto fail_sink; } json_object *resultJ = MixerInfoPcms(mixer, capturesJ, SND_PCM_STREAM_CAPTURE, 0); @@ -601,7 +575,8 @@ STATIC void MixerAttachVerb(AFB_ReqT request) { if (zonesJ) { error = ApiZoneAttach(mixer, request, uid, zonesJ); - if (error) goto OnErrorExit; + if (error) + goto fail_source; json_object *resultJ = MixerInfoZones(mixer, zonesJ, 0); json_object_object_add(responseJ, "zone", resultJ); @@ -611,7 +586,8 @@ STATIC void MixerAttachVerb(AFB_ReqT request) { if (rampsJ) { error = ApiRampAttach(mixer, request, uid, rampsJ); - if (error) goto OnErrorExit; + if (error) + goto fail_zone; json_object *resultJ = MixerInfoRamps(mixer, rampsJ, 0); json_object_object_add(responseJ, "ramps", resultJ); @@ -621,71 +597,54 @@ STATIC void MixerAttachVerb(AFB_ReqT request) { if (streamsJ) { error = ApiStreamAttach(mixer, request, uid, prefix, streamsJ); - if (error) goto OnErrorExit; + if (error) + goto fail_ramp; json_object *resultJ = MixerInfoStreams(mixer, streamsJ, 0); json_object_object_add(responseJ, "streams", resultJ); } + error = afb_api_add_verb(mixer->api, uid, "Post Attach API", AlsaMixerTransactionVerbCB, transaction, NULL, 0, 0); + if (error) { + AFB_ApiError(mixer->api, "%s mixer=%s verb=%s fail to register post attach Verb ", + __func__, mixer->uid, uid); + goto fail_stream; + } + AFB_ApiNotice(mixer->api, "%s responseJ=%s", __func__, json_object_get_string(responseJ)); AFB_ReqSuccess(request, responseJ, NULL); AFB_ApiInfo(mixer->api,"%s DONE", __func__); return; -OnErrorExit: +fail_stream: + // TODO remove created streams +fail_ramp: + // TODO remove created ramps +fail_zone: + // TODO remove created zone +fail_source: + // TODO remove created sources +fail_sink: + // TODO remove created sinks +fail_loop: + // TODO remove created loops +fail: + + if (mixer->transaction) + free(mixer->transaction); + AFB_ApiError(mixer->api,"%s FAILED", __func__); return; } -static void MixerBluezAlsaDevVerb(AFB_ReqT request) { - SoftMixerT *mixer = (SoftMixerT*) afb_req_get_vcbdata(request); - char * interface = NULL, *device = NULL, *profile = NULL; - json_object *argsJ = afb_req_json(request); - int error; - - if (json_object_is_type(argsJ,json_type_null)) { - goto parsed; - } - - error = wrap_json_unpack(argsJ, "{ss,ss,ss !}" - , "interface", &interface - , "device", &device - , "profile", &profile - ); - - if (error) { - AFB_ReqFailF(request, - "invalid-syntax", - "mixer=%s missing 'interface|device|profile' error=%s args=%s", - mixer->uid, wrap_json_get_error_string(error), json_object_get_string(argsJ)); - goto OnErrorExit; - } - -parsed: - AFB_ApiNotice(mixer->api, "%s: interface %s, device %s, profile %s\n", __func__, interface, device, profile); - error = alsa_bluez_set_remote_device(interface, device, profile); - if (error) { - AFB_ReqFailF(request, - "runtime error", - "Unable to set device , err %d", error); - goto OnErrorExit; - } - - AFB_ReqSuccess(request, NULL, NULL); - -OnErrorExit: - return; -} - // Every HAL export the same API & Interface Mapping from SndCard to AudioLogic is done through alsaHalSndCardT STATIC AFB_ApiVerbs CtrlApiVerbs[] = { /* VERB'S NAME FUNCTION TO CALL SHORT DESCRIPTION */ { .verb = "attach", .callback = MixerAttachVerb, .info = "attach resources to mixer"}, { .verb = "remove", .callback = MixerRemoveVerb, .info = "remove existing mixer streams, zones, ..."}, { .verb = "info", .callback = MixerInfoVerb, .info = "list existing mixer streams, zones, ..."}, - { .verb = "bluezalsa_dev", .callback = MixerBluezAlsaDevVerb, .info = "set bluez alsa device"}, { .verb = NULL} /* marker for end of the array */ }; @@ -704,6 +663,8 @@ CTLP_CAPI(MixerAttach, source, argsJ, responseJ) { json_object *playbackJ = NULL, *captureJ = NULL, *zonesJ = NULL, *streamsJ = NULL, *rampsJ = NULL, *loopsJ = NULL; const char* uid = source->uid, *prefix = NULL; + AlsaMixerTransaction * transaction = NULL; + int error; error = wrap_json_unpack(argsJ, "{s?s, s?o,s?o,s?o,s?o,s?o,s?o !}" @@ -716,10 +677,17 @@ CTLP_CAPI(MixerAttach, source, argsJ, responseJ) { , "streams", &streamsJ ); if (error) { - AFB_ApiError(mixer->api, "MixerAttachVerb: invalid-syntax mixer=%s error=%s args=%s", mixer->uid, wrap_json_get_error_string(error), json_object_get_string(argsJ)); + AFB_ApiError(mixer->api, "%s: invalid-syntax mixer=%s error=%s args=%s", + __func__, mixer->uid, wrap_json_get_error_string(error), json_object_get_string(argsJ)); goto OnErrorExit; } + transaction = AlsaMixerTransactionNew(mixer, uid); + if (transaction == NULL) { + SOFTMIXER_NOMEM(mixer->api); + goto OnErrorExit; + } + if (playbackJ) { error = ApiSinkAttach(mixer, NULL, uid, playbackJ); if (error) goto OnErrorExit; @@ -759,6 +727,10 @@ OnErrorExit: CTLP_CAPI(MixerCreate, source, argsJ, responseJ) { SoftMixerT *mixer = calloc(1, sizeof (SoftMixerT)); + if (mixer == NULL) { + SOFTMIXER_NOMEM(source->api); + goto fail; + } source->context = mixer; int error; @@ -770,8 +742,8 @@ CTLP_CAPI(MixerCreate, source, argsJ, responseJ) { mixer->max.ramps = SMIXER_DEFLT_RAMPS; if (json_object_get_type(argsJ) != json_type_object) { - AFB_ApiError(source->api, "_mixer_new_: invalid object type= %s", json_object_get_string(argsJ)); - goto OnErrorExit; + AFB_ApiError(source->api, "%s: invalid object type= %s", __func__, json_object_get_string(argsJ)); + goto fail; } error = wrap_json_unpack(argsJ, "{ss,s?s,s?i,s?i,s?i,s?i,s?i,s?i !}" @@ -785,31 +757,57 @@ CTLP_CAPI(MixerCreate, source, argsJ, responseJ) { , "max_ramp", &mixer->max.ramps ); if (error) { - AFB_ApiNotice(source->api, "_mixer_new_ missing 'uid|max_loop|max_sink|max_source|max_zone|max_stream|max_ramp' error=%s mixer=%s", wrap_json_get_error_string(error), json_object_get_string(argsJ)); - goto OnErrorExit; + AFB_ApiNotice(source->api, + "%s missing 'uid|max_loop|max_sink|max_source|max_zone|max_stream|max_ramp' error=%s mixer=%s", + __func__, wrap_json_get_error_string(error), json_object_get_string(argsJ)); + goto fail_mixer; } // make sure string do not get deleted mixer->uid = strdup(mixer->uid); - if (mixer->info)mixer->info = strdup(mixer->info); - - mixer->loops = calloc(mixer->max.loops + 1, sizeof (void*)); - mixer->sinks = calloc(mixer->max.sinks + 1, sizeof (void*)); - mixer->sources = calloc(mixer->max.sources + 1, sizeof (void*)); - mixer->zones = calloc(mixer->max.zones + 1, sizeof (void*)); - mixer->streams = calloc(mixer->max.streams + 1, sizeof (void*)); - mixer->ramps = calloc(mixer->max.ramps + 1, sizeof (void*)); + if (mixer->uid == NULL) { + SOFTMIXER_NOMEM(source->api); + goto fail_mixer; + } + if (mixer->info) { + mixer->info = strdup(mixer->info); + if (mixer->info == NULL) { + SOFTMIXER_NOMEM(source->api); + goto fail_uid; + } + } + + mixer->nbLoops = 0; + mixer->nbSinks = 0; + mixer->nbSources = 0; + mixer->nbZones = 0; + mixer->nbStreams = 0; + mixer->nbZones = 0; + + CDS_INIT_LIST_HEAD(&mixer->loops.list); + CDS_INIT_LIST_HEAD(&mixer->sinks.list); + CDS_INIT_LIST_HEAD(&mixer->sources.list); + CDS_INIT_LIST_HEAD(&mixer->zones.list); + CDS_INIT_LIST_HEAD(&mixer->streams.list); + CDS_INIT_LIST_HEAD(&mixer->ramps.list); mixer->sdLoop = AFB_GetEventLoop(source->api); mixer->api = source->api; afb_api_set_userdata(source->api, mixer); error = LoadStaticVerbs(mixer, CtrlApiVerbs); - if (error) goto OnErrorExit; + if (error) + goto fail_info; return 0; -OnErrorExit: +fail_info: + free((char*)mixer->info); +fail_uid: + free((char*)mixer->uid); +fail_mixer: + free(mixer); +fail: return -1; } -- cgit 1.2.3-korg