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-plug-route.c | 214 ++++++++++++++++++++++++++++------------- 1 file changed, 145 insertions(+), 69 deletions(-) (limited to 'plugins/alsa/alsa-plug-route.c') diff --git a/plugins/alsa/alsa-plug-route.c b/plugins/alsa/alsa-plug-route.c index 2699701..c00c0cf 100644 --- a/plugins/alsa/alsa-plug-route.c +++ b/plugins/alsa/alsa-plug-route.c @@ -26,37 +26,42 @@ ALSA_PLUG_PROTO(route); typedef struct { const char *uid; const char *cardid; + const char * pcmplug_params; int cardidx; int ccount; int port; + bool isPcmPlug; } ChannelCardPortT; STATIC int CardChannelByUid(SoftMixerT *mixer, const char *uid, ChannelCardPortT *response) { + + AlsaSndPcmT * pcm = NULL; bool found = false; // search for channel within all sound card sink (channel port target is computed by order) - for (int idx = 0; mixer->sinks[idx]; idx++) { - int jdx; - AlsaSndPcmT * pcm = mixer->sinks[idx]; - AlsaPcmChannelT **channels = pcm->channels; + cds_list_for_each_entry(pcm, &mixer->sinks.list, list) { - if (!channels) { + if (pcm->nbChannels == 0) { AFB_ApiError(mixer->api, "%s: No Sink card=%s [should declare channels]", __func__, pcm->uid); goto OnErrorExit; } - for (jdx = 0; jdx < pcm->ccount; jdx++) { - if (!strcasecmp(channels[jdx]->uid, uid)) { - response->port = channels[jdx]->port; - response->uid = pcm->uid; - response->ccount = pcm->ccount; - response->cardid = pcm->sndcard->cid.cardid; - response->cardidx = pcm->sndcard->cid.cardidx; - found = true; - break; - } + AlsaPcmChannelT * channel = NULL; + cds_list_for_each_entry(channel, &pcm->channels.list, list) { + + if (channel->uid && !strcasecmp(channel->uid, uid)) { + response->port = channel->port; + response->uid = pcm->uid; + response->ccount = pcm->nbChannels; + response->cardid = pcm->sndcard->cid.cardid; + response->cardidx = pcm->sndcard->cid.cardidx; + response->pcmplug_params = pcm->sndcard->cid.pcmplug_params; + response->isPcmPlug= pcm->isPcmPlug; + found = true; + break; + } } } @@ -75,78 +80,141 @@ OnErrorExit: PUBLIC AlsaPcmCtlT* AlsaCreateRoute(SoftMixerT *mixer, AlsaSndZoneT *zone, int open) { snd_config_t *routeConfig, *elemConfig, *slaveConfig, *tableConfig, *pcmConfig; - int scount=0, error = 0; - ChannelCardPortT slave, channel; - AlsaPcmCtlT *pcmRoute = calloc(1, sizeof (AlsaPcmCtlT)); + int error = 0; + ChannelCardPortT slave, channelCardPort; + AlsaPcmCtlT *pcmRoute = NULL; + char *cardid = NULL; - char *dmixUid = NULL; + char *slaveUid = NULL; - if (!mixer->sinks) { - AFB_ApiError(mixer->api, "AlsaCreateRoute: mixer=%s zone(%s)(zone) No sink found [should Registry sound card first]", mixer->uid, zone->uid); - goto OnErrorExit; + AFB_ApiDebug(mixer->api, "%s (zone %s)", __func__, zone->uid); + + if (mixer->nbSinks == 0) { + AFB_ApiError(mixer->api, "%s: mixer=%s zone(%s)(zone) No sink found [should Registry sound card first]", __func__, mixer->uid, zone->uid); + goto fail; } - if (asprintf(&cardid, "route-%s", zone->uid) == -1) - goto OnErrorExit; + if (asprintf(&cardid, "route-%s", zone->uid) == -1) { + SOFTMIXER_NOMEM(mixer->api); + goto fail; + } + + pcmRoute = AlsaPcmCtlNew(mixer, cardid); + if (!pcmRoute) { + SOFTMIXER_NOMEM(mixer->api); + goto fail; + } pcmRoute->cid.cardid = (const char *) cardid; pcmRoute->params = ApiSinkGetParamsByZone(mixer, zone->uid); - zone->params=pcmRoute->params; + if (pcmRoute->params == NULL) { + AFB_ApiError(mixer->api, "%s: Failed to retrieve zone parameters", __func__); + goto fail_nodump; + } + + zone->params = pcmRoute->params; // use 1st zone channel to retrieve sound card name + channel count. - error = CardChannelByUid(mixer, zone->sinks[0]->uid, &slave); + + AlsaPcmChannelT * channel = cds_list_first_entry(&zone->sinks.list, AlsaPcmChannelT, list); + error = CardChannelByUid(mixer, channel->uid, &slave); if (error) { - AFB_ApiError(mixer->api, "AlsaCreateRoute:zone(%s) fail to find channel=%s", zone->uid, zone->sinks[0]->uid); - goto OnErrorExit; + AFB_ApiError(mixer->api, "%s:zone(%s) fail to find channel=%s", __func__, zone->uid, channel->uid); + goto fail; } - // move from hardware to DMIX attach to sndcard - if (asprintf(&dmixUid, "dmix-%s", slave.uid) == -1) - goto OnErrorExit; + // DMIX only supports real hardware as a slave + + if (!slave.isPcmPlug) { + // move from hardware to DMIX attach to sndcard + if (asprintf(&slaveUid, "dmix-%s", slave.uid) == -1) { + SOFTMIXER_NOMEM(mixer->api); + goto fail; + } + } else { + slaveUid = strdup(slave.pcmplug_params); + if (!slaveUid) { + SOFTMIXER_NOMEM(mixer->api); + goto fail; + } + pcmRoute->isPcmPlug = true; + } // temporary store to unable multiple channel to route to the same port - snd_config_t **cports = alloca(slave.ccount * sizeof (void*)); - memset(cports, 0, slave.ccount * sizeof (void*)); + + snd_config_t **cports = alloca(zone->nbSinks * sizeof (void*)); + memset(cports, 0, zone->nbSinks * sizeof (void*)); int zcount = 0; // We create 1st ttable to retrieve sndcard slave and channel count (void)snd_config_make_compound(&tableConfig, "ttable", 0); - for (scount = 0; zone->sinks[scount] != NULL; scount++) { + cds_list_for_each_entry(channel, &zone->sinks.list, list) { - error = CardChannelByUid(mixer, zone->sinks[scount]->uid, &channel); + AFB_ApiDebug(mixer->api, "%s: zone->sink channel %s ", __func__, channel->uid); + + error = CardChannelByUid(mixer, channel->uid, &channelCardPort); if (error) { - AFB_ApiError(mixer->api, "AlsaCreateRoute:zone(%s) fail to find channel=%s", zone->uid, zone->sinks[scount]->uid); - goto OnErrorExit; + AFB_ApiError(mixer->api, "%s: zone(%s) fail to find channel=%s", __func__, zone->uid, channel->uid); + goto fail; } - if (slave.cardidx != channel.cardidx) { - AFB_ApiError(mixer->api, "AlsaCreateRoute:zone(%s) cannot span over multiple sound card %s != %s ", zone->uid, slave.cardid, channel.cardid); - goto OnErrorExit; + if (slave.cardidx != channelCardPort.cardidx) { + AFB_ApiError(mixer->api, + "%s: zone(%s) cannot span over multiple sound card %s != %s ", + __func__, zone->uid, slave.cardid, channelCardPort.cardid); + goto fail; } - int port = zone->sinks[scount]->port; - int target = channel.port; + int target = channelCardPort.port; + int port = channel->port; + double volume = 1.0; // currently only support 100% - // if channel entry does not exit into ttable create it now - if (!cports[port]) { - zcount++; + // if channel entry does not exist into ttable create it now + if (!cports[port]) { + char channelS[4]; // 999 channel should be more than enough snprintf(channelS, sizeof (channelS), "%d", port); - error += snd_config_make_compound(&cports[port], channelS, 0); - error += snd_config_add(tableConfig, cports[port]); + + error = snd_config_make_compound(&cports[port], channelS, 0); + if (error) { + AFB_ApiError(mixer->api, "%s: make_compound failed , err %s", __func__, snd_strerror(error)); + goto fail; + } + + error = snd_config_add(tableConfig, cports[port]); + if (error) { + AFB_ApiError(mixer->api, "%s: add compound to table failed, err %s", __func__, snd_strerror(error)); + goto fail; + } } + zcount++; + // ttable require target port as a table and volume as a value char targetS[4]; snprintf(targetS, sizeof (targetS), "%d", target); - error += snd_config_imake_real(&elemConfig, targetS, volume); - error += snd_config_add(cports[port], elemConfig); - if (error) goto OnErrorExit; + + if (channel->volume != -1) + volume = channel->volume; + else + volume = 1.0; + + error = snd_config_imake_real(&elemConfig, targetS, volume); + if (error) { + AFB_ApiError(mixer->api, "%s: Failed to interpret volume real value, err %s", __func__, snd_strerror(error)); + goto fail; + } + + error = snd_config_add(cports[port], elemConfig); + if (error) { + AFB_ApiError(mixer->api, "%s Failed to add ttable entry:err=%s", __func__, snd_strerror(error)); + goto fail; + } + } - if (error) goto OnErrorExit; // update zone with route channel count and sndcard params pcmRoute->ccount = zcount; @@ -155,36 +223,36 @@ PUBLIC AlsaPcmCtlT* AlsaCreateRoute(SoftMixerT *mixer, AlsaSndZoneT *zone, int o // refresh global alsalib config and create PCM top config snd_config_update(); error += snd_config_top(&routeConfig); - if (error) goto OnErrorExit; + if (error) goto fail; error += snd_config_set_id(routeConfig, cardid); - if (error) goto OnErrorExit; + if (error) goto fail; error += snd_config_imake_string(&elemConfig, "type", "route"); - if (error) goto OnErrorExit; + if (error) goto fail; error += snd_config_add(routeConfig, elemConfig); - if (error) goto OnErrorExit; + if (error) goto fail; error += snd_config_make_compound(&slaveConfig, "slave", 0); - if (error) goto OnErrorExit; - error += snd_config_imake_string(&elemConfig, "pcm", dmixUid); - if (error) goto OnErrorExit; + if (error) goto fail; + error += snd_config_imake_string(&elemConfig, "pcm", slaveUid); + if (error) goto fail; error += snd_config_add(slaveConfig, elemConfig); - if (error) goto OnErrorExit; + if (error) goto fail; error += snd_config_imake_integer(&elemConfig, "channels", slave.ccount); - if (error) goto OnErrorExit; + if (error) goto fail; error += snd_config_add(slaveConfig, elemConfig); - if (error) goto OnErrorExit; + if (error) goto fail; error += snd_config_add(routeConfig, slaveConfig); - if (error) goto OnErrorExit; + if (error) goto fail; error += snd_config_add(routeConfig, tableConfig); - if (error) goto OnErrorExit; + if (error) goto fail; if (open) error = _snd_pcm_route_open(&pcmRoute->handle, pcmRoute->cid.cardid, snd_config, routeConfig, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK); if (error) { AFB_ApiError(mixer->api, "%s: zone(%s) fail to create Plug=%s error=%s", __func__, zone->uid, pcmRoute->cid.cardid, snd_strerror(error)); - goto OnErrorExit; + goto fail; } snd_config_update(); @@ -194,20 +262,28 @@ PUBLIC AlsaPcmCtlT* AlsaCreateRoute(SoftMixerT *mixer, AlsaSndZoneT *zone, int o AFB_ApiError(mixer->api, "%s: %s fail to add config route=%s error=%s", __func__, zone->uid, pcmRoute->cid.cardid, snd_strerror(error)); - goto OnErrorExit; + goto fail; } + zone->routeConfig = routeConfig; + // Debug config & pcm AFB_ApiNotice(mixer->api, "%s: zone(%s) DONE", __func__, zone->uid); AlsaDumpCtlConfig(mixer, "plug-route", routeConfig, 1); + return pcmRoute; -OnErrorExit: +fail: +// AFB_ApiError(mixer->api, "%s ERROR, DUMPING PCM...", __func__); +// AlsaDumpCtlConfig(mixer, "plug-pcm", snd_config, 1); + AFB_ApiError(mixer->api, "%s ERROR, DUMPING ROUTE...", __func__); + AlsaDumpCtlConfig(mixer, "plug-route", routeConfig, 1); + +fail_nodump: free(pcmRoute); free(cardid); - free(dmixUid); - AlsaDumpCtlConfig(mixer, "plug-pcm", snd_config, 1); - AlsaDumpCtlConfig(mixer, "plug-route", routeConfig, 1); + free(slaveUid); + AFB_ApiNotice(mixer->api, "%s: zone(%s) FAIL", __func__, zone->uid); return NULL; } -- cgit 1.2.3-korg