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-sink.c | 153 +++++++++++++++++++++++++------------------ 1 file changed, 91 insertions(+), 62 deletions(-) (limited to 'plugins/alsa/alsa-api-sink.c') diff --git a/plugins/alsa/alsa-api-sink.c b/plugins/alsa/alsa-api-sink.c index 96437f5..a1137c5 100644 --- a/plugins/alsa/alsa-api-sink.c +++ b/plugins/alsa/alsa-api-sink.c @@ -24,106 +24,135 @@ PUBLIC AlsaPcmHwInfoT *ApiSinkGetParamsByZone(SoftMixerT *mixer, const char *tar // try to attach a zone as stream playback sink AlsaSndZoneT *zone = ApiZoneGetByUid(mixer, target); - if (zone && zone->sinks) { - // use 1st channel to find attached sound card. - const char *channel = zone->sinks[0]->uid; - - // search for channel uid into mixer sinks and derive sound card - for (int idx = 0; mixer->sinks[idx]; idx++) { - for (int jdx = 0; jdx < mixer->sinks[idx]->ccount; jdx++) { - if (mixer->sinks[idx]->channels[jdx]->uid && !strcasecmp(channel, mixer->sinks[idx]->channels[jdx]->uid)) { - return mixer->sinks[idx]->sndcard->params; - } - } - } + if (!zone) { + AFB_ApiError(mixer->api, "%s: Unable to find zone %s", __func__, target); + goto fail; + } + if (zone->nbSinks == 0) { + AFB_ApiError(mixer->api, "%s: Zone %s has no sinks", __func__, target); + goto fail; + } + + // use 1st channel to find attached sound card. + AlsaPcmChannelT * pcmChannel = cds_list_first_entry(&zone->sinks.list, AlsaPcmChannelT , list); + const char *channel = pcmChannel->uid; + + AFB_ApiDebug(mixer->api, "%s: first channel uid is %s, zone has %d sinks", __func__, channel, zone->nbSinks); + + AlsaSndPcmT * sink = NULL; + + cds_list_for_each_entry(sink, &mixer->sinks.list, list) { + AlsaPcmChannelT * pcmChannel; + cds_list_for_each_entry(pcmChannel, &sink->channels.list, list) { + if (pcmChannel->uid && !strcasecmp(channel, pcmChannel->uid)) { + AFB_ApiDebug(mixer->api, "%s: found %s, return", __func__, pcmChannel->uid); + return sink->sndcard->params; + } + } } + +fail: return NULL; } PUBLIC AlsaSndPcmT *ApiSinkGetByUid(SoftMixerT *mixer, const char *target) { // if no attached zone found, then try direct sink attachment - for (int idx = 0; mixer->sinks[idx]; idx++) { - if (mixer->sinks[idx]->uid && !strcasecmp(mixer->sinks[idx]->uid, target)) { - return mixer->sinks[idx]; - } - } + AlsaSndPcmT * sink; + cds_list_for_each_entry(sink, &mixer->sinks.list, list) { + if (sink->uid && !strcasecmp(sink->uid, target)) + return sink; + } return NULL; } -PUBLIC int ApiSinkAttach(SoftMixerT *mixer, AFB_ReqT request, const char *uid, json_object * argsJ) { +static void sinkDestroy(SoftMixerT* mixer, void * arg) { + AlsaSndPcmT * sink = (AlsaSndPcmT*) arg; + AFB_ApiDebug(mixer->api, "%s... %s", __func__, sink->uid); - int index; - char *dmixUid = NULL; + cds_list_del(&sink->list); + mixer->nbSinks--; + ApiPcmDelete(mixer, sink); +} - for (index = 0; index < mixer->max.sinks; index++) { - if (!mixer->sinks[index]) break; +AlsaSndPcmT * sinkCreate(SoftMixerT * mixer, AFB_ReqT request, const char * uid, json_object * argsJ) { + AlsaPcmCtlT* dmixConfig = NULL; + char *dmixUid = NULL; + AlsaSndPcmT * newSink = ApiPcmAttachOne(mixer, uid, SND_PCM_STREAM_PLAYBACK, argsJ); + if (!newSink) { + goto fail; + } + /* DMIX only supports REAL hardware */ + if (!newSink->isPcmPlug) { + // move from hardware to DMIX attach to sndcard + if (asprintf(&dmixUid, "dmix-%s", newSink->uid) == -1) { + SOFTMIXER_NOMEM(mixer->api); + goto fail_pcm; + } + dmixConfig = AlsaCreateDmix(mixer, dmixUid, newSink, 0); + if (!dmixConfig) { + AFB_ReqFailF(request, "internal-error", "mixer=%s sink=%s fail to create DMIX config", mixer->uid, newSink->uid); + goto fail_dmix; + } } - if (index == mixer->max.sinks) { + mixer->nbSinks++; + cds_list_add_tail(&newSink->list, &mixer->sinks.list); + AlsaMixerTransactionObjectAdd(mixer->transaction, newSink, sinkDestroy); + + return newSink; +fail: +fail_pcm: + // TODO delete PCM +fail_dmix: + free(dmixUid); + return NULL; +} + + +PUBLIC int ApiSinkAttach(SoftMixerT *mixer, AFB_ReqT request, const char * uid, json_object * argsJ) { + + if (mixer->nbSinks >= mixer->max.sinks) { AFB_ReqFailF(request, "too-small", "mixer=%s max sink=%d", mixer->uid, mixer->max.sinks); - goto OnErrorExit; + goto fail; } switch (json_object_get_type(argsJ)) { - long count; - - AlsaPcmCtlT* dmixConfig; + long count; - case json_type_object: - mixer->sinks[index] = ApiPcmAttachOne(mixer, uid, SND_PCM_STREAM_PLAYBACK, argsJ); - if (!mixer->sinks[index]) { + case json_type_object: { + AlsaSndPcmT * newSink = sinkCreate(mixer, request, uid, argsJ); + if (!newSink) { AFB_ReqFailF(request, "bad-pcm", "mixer=%s invalid sink= %s", mixer->uid, json_object_get_string(argsJ)); - goto OnErrorExit; - } - - // move from hardware to DMIX attach to sndcard - if (asprintf(&dmixUid, "dmix-%s", mixer->sinks[index]->uid) == -1) - goto OnErrorExit; - - dmixConfig = AlsaCreateDmix(mixer, dmixUid, mixer->sinks[index], 0); - if (!dmixConfig) { - AFB_ReqFailF(request, "internal-error", "mixer=%s sink=%s fail to create DMIX config", mixer->uid, mixer->sinks[index]->uid); - goto OnErrorExit; + goto fail; } - break; - - case json_type_array: + } + case json_type_array: { count = json_object_array_length(argsJ); - if (count > (mixer->max.sinks - index)) { + if (count > (mixer->max.sinks - mixer->nbSinks)) { AFB_ReqFailF(request, "too-small", "mixer=%s max sink=%d", mixer->uid, mixer->max.sinks); - goto OnErrorExit; + goto fail; } for (int idx = 0; idx < count; idx++) { json_object *sinkJ = json_object_array_get_idx(argsJ, idx); - AlsaSndPcmT * pcm = ApiPcmAttachOne(mixer, uid, SND_PCM_STREAM_PLAYBACK, sinkJ); - if (!pcm) { + AlsaSndPcmT * newSink = sinkCreate(mixer, request, uid, sinkJ); + if (!newSink) { AFB_ReqFailF(request, "bad-pcm", "mixer=%s invalid sink= %s", mixer->uid, json_object_get_string(sinkJ)); - goto OnErrorExit; + goto fail; } - // move from hardware to DMIX attach to sndcard - if (asprintf(&dmixUid, "dmix-%s", pcm->uid) == -1) - goto OnErrorExit; - - dmixConfig = AlsaCreateDmix(mixer, dmixUid, pcm, 0); - if (!dmixConfig) { - AFB_ReqFailF(request, "internal-error", "mixer=%s sink=%s fail to create DMIX config", mixer->uid, pcm->uid); - goto OnErrorExit; - } - mixer->sinks[index + idx] = pcm; } break; + } default: AFB_ReqFailF(request, "invalid-syntax", "mixer=%s sinks invalid argsJ= %s", mixer->uid, json_object_get_string(argsJ)); - goto OnErrorExit; + goto fail; } return 0; -OnErrorExit: - free(dmixUid); +fail: return -1; } -- cgit 1.2.3-korg