From 23cda61198d29010f5a86116b88b8b48eea5e797 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-pcm.c | 357 +++++++++++++++++++++++++++++++++----------- 1 file changed, 273 insertions(+), 84 deletions(-) (limited to 'plugins/alsa/alsa-api-pcm.c') diff --git a/plugins/alsa/alsa-api-pcm.c b/plugins/alsa/alsa-api-pcm.c index 1bba75c..cc2817d 100644 --- a/plugins/alsa/alsa-api-pcm.c +++ b/plugins/alsa/alsa-api-pcm.c @@ -51,17 +51,31 @@ typedef struct { AlsaSndPcmT* pcm; } apiVerbHandleT; -STATIC AlsaPcmChannelT *ProcessOneChannel(SoftMixerT *mixer, const char *uid, json_object *argsJ) { +STATIC AlsaPcmChannelT * ProcessOneChannel(SoftMixerT *mixer, const char *uid, json_object *argsJ) { AlsaPcmChannelT *channel = calloc(1, sizeof (AlsaPcmChannelT)); + if (channel == NULL) { + SOFTMIXER_NOMEM(mixer->api); + goto fail; + } int error = wrap_json_unpack(argsJ, "{ss,si !}", "uid", &channel->uid, "port", &channel->port); - if (error) goto OnErrorExit; + if (error) { + AFB_ApiError(mixer->api, "%s: sndcard=%s channel: missing (uid||port) error=%s json=%s", + __func__, uid, wrap_json_get_error_string(error), json_object_get_string(argsJ)); + goto fail_channel; + } + + CDS_INIT_LIST_HEAD(&channel->list); channel->uid = strdup(channel->uid); + if (channel->uid == NULL) { + SOFTMIXER_NOMEM(mixer->api); + goto fail_channel; + } return channel; -OnErrorExit: - AFB_ApiError(mixer->api, "ProcessOneChannel: sndcard=%s channel: missing (uid||port) error=%s json=%s", uid, wrap_json_get_error_string(error), json_object_get_string(argsJ)); +fail_channel: free(channel); +fail: return NULL; } @@ -81,37 +95,45 @@ STATIC int PcmAttachOneCtl(SoftMixerT *mixer, AlsaSndCtlT *sndcard, json_object AFB_ApiError(mixer->api, "%s: cardid=%s channel: missing (numid|name|value) error=%s json=%s", __func__, sndcard->cid.name, wrap_json_get_error_string(error), json_object_get_string(argsJ)); - goto OnErrorExit; + goto fail; } if (numid > 0) { elemId = AlsaCtlGetNumidElemId(mixer, sndcard, numid); if (!elemId) { - AFB_ApiError(mixer->api, "PcmAttachOneCtl sndard=%s fail to find control numid=%d", sndcard->cid.cardid, numid); - goto OnErrorExit; + AFB_ApiError(mixer->api, "%s sndard=%s fail to find control numid=%d", + __func__, sndcard->cid.cardid, numid); + goto fail; } } else { elemId = AlsaCtlGetNameElemId(mixer, sndcard, name); if (!elemId) { - AFB_ApiError(mixer->api, "PcmAttachOneCtl sndard=%s fail to find control name=%s", sndcard->cid.cardid, name); - goto OnErrorExit; + AFB_ApiError(mixer->api, "%s sndard=%s fail to find control name=%s", + __func__, sndcard->cid.cardid, name); + goto fail; } } snd_ctl_elem_info_alloca(&elemInfo); snd_ctl_elem_info_set_id(elemInfo, elemId); control->name = strdup(snd_ctl_elem_info_get_name(elemInfo)); + if (!control->name) { + SOFTMIXER_NOMEM(mixer->api); + goto fail_elemId; + } control->numid = snd_ctl_elem_info_get_numid(elemInfo); if (snd_ctl_elem_info(sndcard->ctl, elemInfo) < 0) { - AFB_ApiError(mixer->api, "PcmAttachOneCtl: sndard=%s numid=%d name='%s' not loadable", sndcard->cid.cardid, control->numid, control->name); - goto OnErrorExit; + AFB_ApiError(mixer->api, "%s: sndard=%s numid=%d name='%s' not loadable", + __func__, sndcard->cid.cardid, control->numid, control->name); + goto fail_control_name; } if (!snd_ctl_elem_info_is_writable(elemInfo)) { - AFB_ApiError(mixer->api, "PcmAttachOneCtl: sndard=%s numid=%d name='%s' not writable", sndcard->cid.cardid, control->numid, control->name); - goto OnErrorExit; + AFB_ApiError(mixer->api, "%s: sndard=%s numid=%d name='%s' not writable", + __func__, sndcard->cid.cardid, control->numid, control->name); + goto fail_control_name; } control->count = snd_ctl_elem_info_get_count(elemInfo); @@ -132,22 +154,25 @@ STATIC int PcmAttachOneCtl(SoftMixerT *mixer, AlsaSndCtlT *sndcard, json_object break; default: - AFB_ApiError(mixer->api, "PcmAttachOneCtl: sndard=%s numid=%d name='%s' invalid/unsupported type=%d", - sndcard->cid.cardid, control->numid, control->name, snd_ctl_elem_info_get_type(elemInfo)); - goto OnErrorExit; + AFB_ApiError(mixer->api, "%s: sndard=%s numid=%d name='%s' invalid/unsupported type=%d", + __func__, sndcard->cid.cardid, control->numid, control->name, snd_ctl_elem_info_get_type(elemInfo)); + goto fail_control_name; } if (error) { - AFB_ApiError(mixer->api, "PcmAttachOneCtl: sndard=%s numid=%d name='%s' not writable", sndcard->cid.cardid, control->numid, control->name); - goto OnErrorExit; + AFB_ApiError(mixer->api, "%s: sndard=%s numid=%d name='%s' not writable", + __func__, sndcard->cid.cardid, control->numid, control->name); + goto fail_control_name; } free(elemId); - return 0; -OnErrorExit: - if (elemId)free(elemId); +fail_control_name: + free((char*)control->name); +fail_elemId: + free(elemId); +fail: return -1; } @@ -161,7 +186,7 @@ STATIC int PcmSetControl(SoftMixerT *mixer, AlsaSndCtlT *sndcard, AlsaSndControl elemId = AlsaCtlGetNumidElemId(mixer, sndcard, control->numid); if (!elemId) { - AFB_ApiError(mixer->api, "PcmSetControl sndard=%s fail to find control numid=%d", sndcard->cid.cardid, control->numid); + AFB_ApiError(mixer->api, "%s sndard=%s fail to find control numid=%d", __func__, sndcard->cid.cardid, control->numid); goto OnErrorExit; } @@ -169,18 +194,18 @@ STATIC int PcmSetControl(SoftMixerT *mixer, AlsaSndCtlT *sndcard, AlsaSndControl snd_ctl_elem_info_set_id(elemInfo, elemId); if (snd_ctl_elem_info(sndcard->ctl, elemInfo) < 0) { - AFB_ApiError(mixer->api, "PcmSetControl: sndard=%s numid=%d name='%s' not loadable", sndcard->cid.cardid, control->numid, control->name); + AFB_ApiError(mixer->api, "%s: sndard=%s numid=%d name='%s' not loadable", __func__, sndcard->cid.cardid, control->numid, control->name); goto OnErrorExit; } if (!snd_ctl_elem_info_is_writable(elemInfo)) { - AFB_ApiError(mixer->api, "PcmSetControl: sndard=%s numid=%d name='%s' not writable", sndcard->cid.cardid, control->numid, control->name); + AFB_ApiError(mixer->api, "%s: sndard=%s numid=%d name='%s' not writable", __func__, sndcard->cid.cardid, control->numid, control->name); goto OnErrorExit; } error = CtlElemIdGetLong(mixer, sndcard, elemId, &curval); if (error) { - AFB_ApiError(mixer->api, "PcmSetControl sndard=%s fail to read control numid=%d", sndcard->cid.cardid, control->numid); + AFB_ApiError(mixer->api, "%s sndcard=%s fail to read control numid=%d", __func__, sndcard->cid.cardid, control->numid); goto OnErrorExit; } @@ -347,9 +372,20 @@ OnErrorExit: return; } +PUBLIC void ApiPcmDelParams(SoftMixerT* mixer, AlsaPcmHwInfoT* params) { + AFB_ApiDebug(mixer->api, "%s... free params formatS %s", __func__, params->formatS); + free((char*)params->formatS); + free(params); +} + PUBLIC AlsaPcmHwInfoT * ApiPcmSetParams(SoftMixerT *mixer, const char *uid, json_object * paramsJ) { - AlsaPcmHwInfoT *params = calloc(1, sizeof (AlsaPcmHwInfoT)); + const char *format = NULL, *access = NULL; + AlsaPcmHwInfoT *params = calloc(1, sizeof (AlsaPcmHwInfoT)); + if (params == NULL) { + SOFTMIXER_NOMEM(mixer->api); + goto fail; + } // some default values params->rate = ALSA_DEFAULT_PCM_RATE; @@ -364,17 +400,26 @@ PUBLIC AlsaPcmHwInfoT * ApiPcmSetParams(SoftMixerT *mixer, const char *uid, json "format", &format, "access", &access); if (error) { - AFB_ApiError(mixer->api, "ApiPcmSetParams: sndcard=%s invalid params=%s", uid, json_object_get_string(paramsJ)); - goto OnErrorExit; + AFB_ApiError(mixer->api, "%s: sndcard=%s invalid params=%s", + __func__, uid, json_object_get_string(paramsJ)); + goto fail_params; } } if (!format) { params->format = SND_PCM_FORMAT_S16_LE; - params->formatS = "S16_LE"; + params->formatS = strdup("S16_LE"); + if (params->formatS == NULL) { + SOFTMIXER_NOMEM(mixer->api); + goto fail_params; + } goto check_access; } params->formatS = strdup(format); + if (params->formatS == NULL) { + SOFTMIXER_NOMEM(mixer->api); + goto fail_params; + } #define FORMAT_CHECK(arg) if (!strcmp(format,#arg)) { params->format = SND_PCM_FORMAT_##arg; goto check_access; } FORMAT_CHECK(S16_LE); @@ -394,11 +439,11 @@ PUBLIC AlsaPcmHwInfoT * ApiPcmSetParams(SoftMixerT *mixer, const char *uid, json FORMAT_CHECK(FLOAT_LE); FORMAT_CHECK(FLOAT_BE); - AFB_ApiError(mixer->api, "ApiPcmSetParams:%s(params) unsupported format 'S16_LE|S32_L|...' format=%s", uid, format); - goto OnErrorExit; + AFB_ApiError(mixer->api, "%s: %s(params) unsupported format 'S16_LE|S32_L|...' format=%s", __func__, uid, format); + goto fail_params; check_access: - AFB_ApiNotice(mixer->api, "ApiPcmSetParams:%s format set to SND_PCM_FORMAT_%s", uid, params->formatS); + AFB_ApiNotice(mixer->api, "%s: %s format set to SND_PCM_FORMAT_%s", __func__, uid, params->formatS); #define ACCESS_CHECK(arg) if (!strcmp(access,#arg)) { params->access = SND_PCM_ACCESS_##arg; goto success;} @@ -413,25 +458,99 @@ check_access: ACCESS_CHECK(RW_INTERLEAVED); ACCESS_CHECK(RW_NONINTERLEAVED); - AFB_ApiNotice(mixer->api, "ApiPcmSetParams:%s(params) unsupported access 'RW_INTERLEAVED|MMAP_INTERLEAVED|MMAP_COMPLEX' access=%s", uid, access); - goto OnErrorExit; + AFB_ApiNotice(mixer->api, + "%s:%s(params) unsupported access 'RW_INTERLEAVED|MMAP_INTERLEAVED|MMAP_COMPLEX' access=%s", + __func__,uid, access); + goto fail_params; success: - AFB_ApiNotice(mixer->api, "ApiPcmSetParams:%s access set to %s", uid, access); + AFB_ApiNotice(mixer->api, "%s:%s access set to %s", __func__, uid, access); return params; -OnErrorExit: +fail_params: free(params); +fail: return NULL; } -PUBLIC AlsaSndPcmT * ApiPcmAttachOne(SoftMixerT *mixer, const char *uid, snd_pcm_stream_t direction, json_object * argsJ) { + +static void pcmChannelsDestroy(SoftMixerT * mixer, AlsaSndPcmT * pcm) { + AFB_ApiDebug(mixer->api, "%s: pcm %s", __func__, pcm->uid); + + AlsaPcmChannelT * channel, *tmp; + cds_list_for_each_entry_safe(channel, tmp, &pcm->channels.list, list) { + cds_list_del(&channel->list); + + free((char *)channel->uid); + free(channel); + } + AFB_ApiDebug(mixer->api, "%s: pcm %s DONE", __func__, pcm->uid); +} + +PUBLIC void ApiPcmDelete(SoftMixerT * mixer, AlsaSndPcmT * pcm) { + + AFB_ApiDebug(mixer->api, "%s: pcm %s", __func__, pcm->uid); + + free(pcm->apiVerbHandle); + AlsaSndCtlT * card = pcm->sndcard; + if (card->cid.pcmplug_params) + free((char*)card->cid.pcmplug_params); + + pcmChannelsDestroy(mixer, pcm); + + snd_ctl_close(card->ctl); + free(card); + free ((char*)pcm->mute.name); + free ((char*)pcm->volume.name); + + cds_list_del(&pcm->list); + + if (pcm->verb && + (strcmp(pcm->verb, SOFTMIXER_VERB_NONE)!=0)) { + int error = afb_api_del_verb(mixer->api, pcm->verb, (void**)&pcm->apiVerbHandle); + if (error) { + AFB_ApiError(mixer->api, "Failed to remove verb %s", pcm->verb); + } + } + + free((char*)pcm->uid); + free(pcm); + AFB_ApiDebug(mixer->api, "%s: done", __func__); +} + +PUBLIC AlsaSndPcmT * ApiPcmNew(SoftMixerT* mixer) { AlsaSndPcmT *pcm = calloc(1, sizeof (AlsaSndPcmT)); + if (pcm == NULL) { + SOFTMIXER_NOMEM(mixer->api); + goto fail; + } + + CDS_INIT_LIST_HEAD(&pcm->list); + CDS_INIT_LIST_HEAD(&pcm->channels.list); +fail: + return pcm; +} + +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; char *apiVerb = NULL, *apiInfo = NULL; + apiVerbHandleT *handle = NULL; int error; + AlsaSndPcmT *pcm = ApiPcmNew(mixer); + if (pcm == NULL) { + SOFTMIXER_NOMEM(mixer->api); + goto fail; + } + pcm->sndcard = (AlsaSndCtlT*) calloc(1, sizeof (AlsaSndCtlT)); + if (pcm->sndcard == NULL) { + SOFTMIXER_NOMEM(mixer->api); + goto fail_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 !}" , "uid", &pcm->uid , "pcmplug_params", &pcm->sndcard->cid.pcmplug_params @@ -444,42 +563,48 @@ PUBLIC AlsaSndPcmT * ApiPcmAttachOne(SoftMixerT *mixer, const char *uid, snd_pcm , "params", ¶msJ ); if (error) { - AFB_ApiError(mixer->api, "ApiPcmAttachOne: hal=%s missing 'uid|path|cardid|device|sink|source|params' error=%s args=%s", uid, wrap_json_get_error_string(error), json_object_get_string(argsJ)); - goto OnErrorExit; + AFB_ApiError(mixer->api, "%s: hal=%s missing 'uid|path|cardid|device|sink|source|params' error=%s args=%s", + __func__, uid, wrap_json_get_error_string(error), json_object_get_string(argsJ)); + goto fail_pcm_sndcard; } + if (pcm->sndcard->cid.pcmplug_params) + pcm->isPcmPlug = true; + else + pcm->isPcmPlug = false; + // try to open sound card control interface pcm->sndcard->ctl = AlsaByPathOpenCtl(mixer, pcm->uid, pcm->sndcard); if (!pcm->sndcard->ctl) { - AFB_ApiError(mixer->api, "ApiPcmAttachOne: hal=%s Fail to open sndcard uid=%s devpath=%s cardid=%s", uid, pcm->uid, pcm->sndcard->cid.devpath, pcm->sndcard->cid.cardid); - goto OnErrorExit; + 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; } // check sndcard accepts params pcm->sndcard->params = ApiPcmSetParams(mixer, pcm->uid, paramsJ); if (!pcm->sndcard->params) { - AFB_ApiError(mixer->api, "ApiPcmAttachOne: hal=%s Fail to set params sndcard uid=%s params=%s", uid, pcm->uid, json_object_get_string(paramsJ)); - goto OnErrorExit; + AFB_ApiError(mixer->api, "%s: hal=%s Fail to set params sndcard uid=%s params=%s", + __func__, uid, pcm->uid, json_object_get_string(paramsJ)); + goto fail_pcm_sndcard_ctl; } if (direction == SND_PCM_STREAM_PLAYBACK) { if (!sinkJ) { - AFB_ApiError(mixer->api, "ApiPcmAttachOne: hal=%s SND_PCM_STREAM_PLAYBACK require sinks args=%s", uid, json_object_get_string(argsJ)); - goto OnErrorExit; + AFB_ApiError(mixer->api, "%s: hal=%s SND_PCM_STREAM_PLAYBACK require sinks args=%s", + __func__, uid, json_object_get_string(argsJ)); + goto fail_pcm_sndcard_ctl; } targetJ = sinkJ; } if (direction == SND_PCM_STREAM_CAPTURE) { if (!sourceJ) { - AFB_ApiError(mixer->api, "ApiPcmAttachOne: hal=%s SND_PCM_STREAM_CAPTURE require sources args=%s", uid, json_object_get_string(argsJ)); - goto OnErrorExit; + AFB_ApiError(mixer->api, "%s: hal=%s SND_PCM_STREAM_CAPTURE require sources args=%s", + __func__, uid, json_object_get_string(argsJ)); + goto fail_pcm_sndcard_ctl; } targetJ = sourceJ; - - // we may have to register SMIXER_SUBDS_CTLS per subdev (Fulup ToBeDone when sndcard get multiple device/subdev) - pcm->sndcard->registry = calloc(SMIXER_SUBDS_CTLS + 1, sizeof (RegistryEntryPcmT)); - pcm->sndcard->rcount = SMIXER_SUBDS_CTLS; } json_object *channelsJ = NULL, *controlsJ = NULL; @@ -488,30 +613,43 @@ PUBLIC AlsaSndPcmT * ApiPcmAttachOne(SoftMixerT *mixer, const char *uid, snd_pcm , "controls", &controlsJ ); if (error) { - AFB_ApiNotice(mixer->api, "ApiPcmAttachOne: hal=%s pcms missing channels|[controls] error=%s paybacks=%s", uid, wrap_json_get_error_string(error), json_object_get_string(argsJ)); - goto OnErrorExit; + AFB_ApiNotice(mixer->api, "%s: hal=%s pcms missing channels|[controls] error=%s (%s)", + __func__, uid, wrap_json_get_error_string(error), json_object_get_string(argsJ)); + goto fail_pcm_sndcard_ctl; } + + AlsaPcmChannelT * channel = NULL; + if (channelsJ) { switch (json_object_get_type(channelsJ)) { case json_type_object: - pcm->ccount = 1; - pcm->channels = calloc(2, sizeof (void*)); - pcm->channels[0] = ProcessOneChannel(mixer, pcm->uid, channelsJ); - if (!pcm->channels[0]) goto OnErrorExit; + channel = ProcessOneChannel(mixer, pcm->uid, channelsJ); + if (channel == NULL) { + goto fail_pcm_sndcard_ctl; + } + + pcm->nbChannels++; + cds_list_add_tail(&channel->list, &pcm->channels.list); + break; - case json_type_array: - pcm->ccount = (int) json_object_array_length(channelsJ); - pcm->channels = calloc(pcm->ccount + 1, sizeof (void*)); - for (int idx = 0; idx < pcm->ccount; idx++) { + case json_type_array: { + int nbChannels = (int) json_object_array_length(channelsJ); + + for (int idx = 0; idx < nbChannels; idx++) { json_object *channelJ = json_object_array_get_idx(channelsJ, idx); - pcm->channels[idx] = ProcessOneChannel(mixer, pcm->uid, channelJ); - if (!pcm->channels[idx]) goto OnErrorExit; + channel = ProcessOneChannel(mixer, pcm->uid, channelJ); + if (!channel) + goto fail_pcm_channels; + + pcm->nbChannels++; + cds_list_add_tail(&channel->list, &pcm->channels.list); } break; + } default: - AFB_ApiError(mixer->api, "ApiPcmAttachOne:%s invalid pcm=%s", pcm->uid, json_object_get_string(channelsJ)); - goto OnErrorExit; + AFB_ApiError(mixer->api, "%s:%s invalid pcm=%s", pcm->uid, __func__, json_object_get_string(channelsJ)); + goto fail_pcm_sndcard_ctl; } } @@ -525,50 +663,101 @@ PUBLIC AlsaSndPcmT * ApiPcmAttachOne(SoftMixerT *mixer, const char *uid, snd_pcm AFB_ApiNotice(mixer->api, "%s: source missing [volume]|[mute] error=%s control=%s", __func__, wrap_json_get_error_string(error), json_object_get_string(controlsJ)); - goto OnErrorExit; + goto fail_pcm_channels; } - if (volJ) error += PcmAttachOneCtl(mixer, pcm->sndcard, volJ, &pcm->volume); - if (muteJ) error += PcmAttachOneCtl(mixer, pcm->sndcard, muteJ, &pcm->mute); - if (error) goto OnErrorExit; + if (volJ) { + error = PcmAttachOneCtl(mixer, pcm->sndcard, volJ, &pcm->volume); + if (error) + goto fail_pcm_channels; + } + if (muteJ) { + error = PcmAttachOneCtl(mixer, pcm->sndcard, muteJ, &pcm->mute); + if (error) + goto fail_pcm_channels; + } // create master control for this sink if (direction == SND_PCM_STREAM_PLAYBACK) { - if (asprintf(&apiVerb, "%s:playback", pcm->uid) == -1) - goto OnErrorExit; - if (asprintf(&apiInfo, "HAL:%s SND_PCM_STREAM_PLAYBACK", uid) == -1) - goto OnErrorExit; + if (asprintf(&apiVerb, "%s:playback", pcm->uid) == -1) { + SOFTMIXER_NOMEM(mixer->api); + goto fail_pcm_channels; + } + if (asprintf(&apiInfo, "HAL:%s SND_PCM_STREAM_PLAYBACK", uid) == -1) { + SOFTMIXER_NOMEM(mixer->api); + goto fail_verb_name; + } + } else { - if (asprintf(&apiVerb, "%s:capture", pcm->uid) == -1) - goto OnErrorExit; - if (asprintf(&apiInfo, "HAL:%s SND_PCM_STREAM_PLAYBACK", uid) == -1) - goto OnErrorExit; + if (asprintf(&apiVerb, "%s:capture", pcm->uid) == -1) { + SOFTMIXER_NOMEM(mixer->api); + goto fail_pcm_channels; + } + if (asprintf(&apiInfo, "HAL:%s SND_PCM_STREAM_PLAYBACK", uid) == -1) { + SOFTMIXER_NOMEM(mixer->api); + goto fail_verb_name; + } } apiVerbHandleT *handle = calloc(1, sizeof (apiVerbHandleT)); + if (handle == NULL) { + SOFTMIXER_NOMEM(mixer->api); + goto fail_api_info; + } + handle->uid = uid; handle->pcm = pcm; handle->mixer = mixer; pcm->verb=apiVerb; + pcm->apiVerbHandle = handle; + error = afb_api_add_verb(mixer->api, apiVerb, apiInfo, ApiPcmVerbCB, handle, NULL, 0, 0); if (error) { - AFB_ApiError(mixer->api, "ApiPcmAttachOne mixer=%s verb=%s fail to Register Master control ", mixer->uid, apiVerb); - goto OnErrorExit; + AFB_ApiError(mixer->api, "%s mixer=%s verb=%s fail to Register Master control ", + __func__, mixer->uid, apiVerb); + goto fail_handle; } } else { /* no controls -> put dummy verb */ - pcm->verb = "none"; + pcm->verb = SOFTMIXER_VERB_NONE; } // free useless resource and secure others pcm->uid = strdup(pcm->uid); + if (pcm->uid == NULL) { + SOFTMIXER_NOMEM(mixer->api); + goto fail_handle; + } + const char ** ppcmplug = &(pcm->sndcard->cid.pcmplug_params); + if (*ppcmplug) { + *ppcmplug = strdup(*ppcmplug); + if (*ppcmplug == NULL) { + SOFTMIXER_NOMEM(mixer->api); + goto fail_pcm_uid; + } + } return pcm; -OnErrorExit: - free(pcm); - free(apiVerb); - free(apiInfo); +fail_pcm_uid: + free((char*)pcm->uid); +fail_handle: + free(handle); +fail_api_info: + free(apiInfo); +fail_verb_name: + free(apiVerb); +fail_pcm_channels: + pcmChannelsDestroy(mixer, pcm); +fail_pcm_sndcard_ctl: + free(pcm->sndcard->ctl); +fail_pcm_sndcard: + free(pcm->sndcard); +fail_pcm: + free ((char*)pcm->mute.name); + free ((char*)pcm->volume.name); + free(pcm); +fail: return NULL; } -- cgit 1.2.3-korg