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-zones.c | 207 +++++++++++++++++++++++++++++------------- 1 file changed, 142 insertions(+), 65 deletions(-) (limited to 'plugins/alsa/alsa-api-zones.c') diff --git a/plugins/alsa/alsa-api-zones.c b/plugins/alsa/alsa-api-zones.c index c8ab107..299037f 100644 --- a/plugins/alsa/alsa-api-zones.c +++ b/plugins/alsa/alsa-api-zones.c @@ -25,166 +25,242 @@ extern Lua2cWrapperT Lua2cWrap; PUBLIC AlsaSndZoneT *ApiZoneGetByUid(SoftMixerT *mixer, const char *target) { + AlsaSndZoneT * zone = NULL; - assert(mixer->zones[0]); + if (mixer->nbZones == 0) { + AFB_ApiError(mixer->api, "%s mixer=%s does not have any zone", __func__, mixer->uid); + goto fail; + } // search for subdev into every registered loop - for (int idx = 0; mixer->zones[idx]; idx++) { - if (mixer->zones[idx]->uid && !strcasecmp(mixer->zones[idx]->uid, target)) { - return mixer->zones[idx]; - } + cds_list_for_each_entry(zone, &mixer->zones.list, list) { + if (zone->uid && !strcasecmp(zone->uid, target)) { + return zone; + } } - AFB_ApiError(mixer->api, "ApiZoneGetByUid mixer=%s fail to find zone=%s", mixer->uid, target); +fail: + AFB_ApiError(mixer->api, "%s mixer=%s fail to find zone=%s", __func__, mixer->uid, target); return NULL; } STATIC AlsaPcmChannelT* ProcessOneChannel(SoftMixerT *mixer, const char* uid, json_object *channelJ) { + AlsaPcmChannelT *channel = calloc(1, sizeof (AlsaPcmChannelT)); + if (!channel) { + SOFTMIXER_NOMEM(mixer->api); + goto fail; + } + + CDS_INIT_LIST_HEAD(&channel->list); + + channel->volume = -1; - int error = wrap_json_unpack(channelJ, "{ss,si !}" + int error = wrap_json_unpack(channelJ, "{ss,si,s?f !}" , "target", &channel->uid , "channel", &channel->port + , "volume", &channel->volume ); - if (error) goto OnErrorExit; + if (error) + goto fail; channel->uid = strdup(channel->uid); + if (!channel->uid) { + SOFTMIXER_NOMEM(mixer->api); + goto fail_channel; + } + + AFB_ApiDebug(mixer->api, "%s: uid %s, channel uid %s, port %d, volume %f", + __func__, uid, channel->uid, channel->port, channel->volume); + return channel; -OnErrorExit: - AFB_ApiError(mixer->api, "ProcessOneChannel: zone=%s channel: missing (target|channel) json=%s", uid, json_object_get_string(channelJ)); +fail_channel: + free(channel); +fail: + AFB_ApiError(mixer->api, "%s: zone=%s channel: missing (target|channel) json=%s", __func__, uid, json_object_get_string(channelJ)); return NULL; } -STATIC AlsaSndZoneT *AttacheOneZone(SoftMixerT *mixer, const char *uid, json_object *zoneJ) { - AlsaSndZoneT *zone = calloc(1, sizeof (AlsaSndZoneT)); +STATIC AlsaSndZoneT *AttachOneZone(SoftMixerT *mixer, const char *uid, json_object *zoneJ) { + + AFB_ApiDebug(mixer->api, "%s uid %s", __func__, uid); + json_object *sinkJ = NULL, *sourceJ = NULL; size_t count; int error; + AlsaSndZoneT *zone = calloc(1, sizeof (AlsaSndZoneT)); + if (!zone) { + SOFTMIXER_NOMEM(mixer->api); + goto fail; + } + + CDS_INIT_LIST_HEAD(&zone->list); + CDS_INIT_LIST_HEAD(&zone->sinks.list); + CDS_INIT_LIST_HEAD(&zone->sources.list); + error = wrap_json_unpack(zoneJ, "{ss,s?o,s?o !}" , "uid", &zone->uid , "sink", &sinkJ , "source", &sourceJ ); if (error || (!sinkJ && sourceJ)) { - AFB_ApiNotice(mixer->api, "AttacheOneZone missing 'uid|sink|source' error=%s zone=%s", wrap_json_get_error_string(error), json_object_get_string(zoneJ)); - goto OnErrorExit; + AFB_ApiNotice(mixer->api, "%s missing 'uid|sink|source' error=%s zone=%s", __func__, wrap_json_get_error_string(error), json_object_get_string(zoneJ)); + goto fail_zone; } // make sure remain valid even when json object is removed zone->uid = strdup(zone->uid); + if (!zone->uid) { + SOFTMIXER_NOMEM(mixer->api); + goto fail_zone; + } + + AlsaPcmChannelT * channel = NULL; if (sinkJ) { switch (json_object_get_type(sinkJ)) { case json_type_object: - zone->sinks = calloc(2, sizeof (void*)); - zone->sinks[0] = ProcessOneChannel(mixer, zone->uid, sinkJ); - if (!zone->sinks[0]) goto OnErrorExit; + + channel = ProcessOneChannel(mixer, zone->uid, sinkJ); + if (!channel) + goto fail_uid; + + zone->nbSinks++; + cds_list_add_tail(&channel->list, &zone->sinks.list); break; case json_type_array: count = json_object_array_length(sinkJ); - zone->sinks = calloc(count + 1, sizeof (void*)); + for (int idx = 0; idx < count; idx++) { json_object *subdevJ = json_object_array_get_idx(sinkJ, idx); - zone->sinks[idx] = ProcessOneChannel(mixer, zone->uid, subdevJ); - if (error) goto OnErrorExit; + + channel = ProcessOneChannel(mixer, zone->uid, subdevJ); + if (!channel) + goto fail_uid; + + zone->nbSinks++; + cds_list_add_tail(&channel->list, &zone->sinks.list); + } break; default: - AFB_ApiError(mixer->api, "AttacheOneZone: Mixer=%s Hal=%s zone=%s invalid mapping=%s", mixer->uid, uid, zone->uid, json_object_get_string(sinkJ)); - goto OnErrorExit; + AFB_ApiError(mixer->api, "%s: Mixer=%s Hal=%s zone=%s invalid mapping=%s", __func__, mixer->uid, uid, zone->uid, json_object_get_string(sinkJ)); + goto fail; } } if (sourceJ) { + switch (json_object_get_type(sourceJ)) { case json_type_object: - zone->sources = calloc(2, sizeof (void*)); - zone->sources[0] = ProcessOneChannel(mixer, zone->uid, sourceJ); - if (!zone->sources[0]) goto OnErrorExit; + channel = ProcessOneChannel(mixer, zone->uid, sourceJ); + if (!channel) + goto fail_uid; + zone->nbSources++; + cds_list_add(&channel->list, &zone->sources.list); break; case json_type_array: count = json_object_array_length(sourceJ); - zone->sources = calloc(count + 1, sizeof (void*)); + for (int idx = 0; idx < count; idx++) { json_object *subdevJ = json_object_array_get_idx(sourceJ, idx); - zone->sources[idx] = ProcessOneChannel(mixer, zone->uid, subdevJ); - if (error) goto OnErrorExit; + channel = ProcessOneChannel(mixer, zone->uid, subdevJ); + if (!zone) + goto fail_uid; + + zone->nbSources++; + cds_list_add(&channel->list, &zone->sources.list); } break; default: - AFB_ApiError(mixer->api, "AttacheOneZone:Mixer=%s Hal=%s zone=%s mapping=%s", mixer->uid, uid, zone->uid, json_object_get_string(sourceJ)); - goto OnErrorExit; + AFB_ApiError(mixer->api, "%s: Mixer=%s Hal=%s zone=%s mapping=%s", + __func__, mixer->uid, uid, zone->uid, json_object_get_string(sourceJ)); + goto fail; } } + AFB_ApiDebug(mixer->api, "%s uid %s DONE", __func__, uid); return zone; -OnErrorExit: +fail_uid: + free((char*)zone->uid); +fail_zone: + free(zone); +fail: return NULL; } -PUBLIC int ApiZoneAttach(SoftMixerT *mixer, AFB_ReqT request, const char *uid, json_object * argsJ) { +static void zoneDestroy(SoftMixerT* mixer, void * arg) { + AlsaSndZoneT * zone = (AlsaSndZoneT*) arg; - int index; + 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; + } +} - for (index = 0; index < mixer->max.zones; index++) { - if (!mixer->zones[index]) break; - } +PUBLIC AlsaSndZoneT * zoneCreate(SoftMixerT* mixer, const char * uid, json_object * argsJ) { + AlsaSndZoneT * zone = AttachOneZone(mixer, uid, argsJ); + if (!zone) + goto fail; + + // must be set now; AlsaCreateRoute needs it ! + mixer->nbZones++; + cds_list_add_tail(&zone->list, &mixer->zones.list); + AlsaMixerTransactionObjectAdd(mixer->transaction, zone, zoneDestroy); - if (index == mixer->max.zones) { - AFB_ReqFailF(request, "too-small", "mixer=%s max zone=%d", mixer->uid, mixer->max.zones); - goto OnErrorExit; + AlsaPcmCtlT *routeConfig = AlsaCreateRoute(mixer, zone, 0); + if (!routeConfig) { + AFB_ApiError(mixer->api, + "%s: Mixer=%s Hal=%s zone=%s Fail to attach PCM Route", + __func__, mixer->uid, uid, zone->uid); + goto fail; } +fail: + return zone; +} + +PUBLIC int ApiZoneAttach(SoftMixerT *mixer, AFB_ReqT request, const char * uid, json_object * argsJ) { + + AFB_ApiDebug(mixer->api, "%s uid %s", __func__, uid); + + if (mixer->nbZones >= mixer->max.zones) { + AFB_ReqFailF(request, "too-small", "mixer=%s max zone=%d", mixer->uid, mixer->max.zones); + goto OnErrorExit; + } + switch (json_object_get_type(argsJ)) { - long count; + long count; case json_type_object: { - AlsaSndZoneT * zone = AttacheOneZone(mixer, uid, argsJ); - if (!zone) { + AlsaSndZoneT * newZone = zoneCreate(mixer, uid, argsJ); + if (!newZone) { AFB_ReqFailF(request, "bad-zone", "mixer=%s invalid zone= %s", mixer->uid, json_object_get_string(argsJ)); goto OnErrorExit; } - - // must be set now; AlsaCreateRoute needs it ! - mixer->zones[index] = zone; - AlsaPcmCtlT *routeConfig = AlsaCreateRoute(mixer, zone, 0); - if (!routeConfig) { - AFB_ApiError(mixer->api, - "%s: Mixer=%s Hal=%s zone=%s Fail to attach PCM Route", - __func__, mixer->uid, uid, zone->uid); - goto OnErrorExit; - } break; } case json_type_array: count = json_object_array_length(argsJ); - if (count > (mixer->max.zones - index)) { + if (count > (mixer->max.zones - mixer->nbZones)) { AFB_ReqFailF(request, "too-small", "mixer=%s max zone=%d", mixer->uid, mixer->max.zones); goto OnErrorExit; } for (int idx = 0; idx < count; idx++) { json_object *zoneJ = json_object_array_get_idx(argsJ, idx); - AlsaSndZoneT * zone = AttacheOneZone(mixer, uid, zoneJ); - if (!zone) { + AlsaSndZoneT * newZone = zoneCreate(mixer, uid, zoneJ); + if (!newZone) { AFB_ReqFailF(request, "bad-zone", "mixer=%s invalid zone= %s", mixer->uid, json_object_get_string(zoneJ)); goto OnErrorExit; } - - mixer->zones[index + idx] = zone; - AlsaPcmCtlT *routeConfig = AlsaCreateRoute(mixer, zone, 0); - if (!routeConfig) { - AFB_ApiError(mixer->api, - "%s: Mixer=%s Hal=%s zone=%s Fail to attach PCM Route", - __func__, mixer->uid, uid, zone->uid); - goto OnErrorExit; - } - } break; default: @@ -192,6 +268,7 @@ PUBLIC int ApiZoneAttach(SoftMixerT *mixer, AFB_ReqT request, const char *uid, j goto OnErrorExit; } + AFB_ApiDebug(mixer->api, "%s uid %s DONE", __func__, uid); return 0; OnErrorExit: -- cgit 1.2.3-korg