summaryrefslogtreecommitdiffstats
path: root/plugins/alsa/alsa-api-zones.c
diff options
context:
space:
mode:
authorThierry Bultel <thierry.bultel@iot.bzh>2018-12-04 23:11:20 +0100
committerThierry Bultel <thierry.bultel@iot.bzh>2018-12-19 23:09:21 +0100
commite0f57e523112e1bc73a04e8615d7a21355f0ce0e (patch)
tree19198c38d7433862cee733fde3efca8170228279 /plugins/alsa/alsa-api-zones.c
parent7df040a3742af8d800852dd39f8e921cd82a4cf2 (diff)
Add support for bluetooth telephonyguppy_6.99.3guppy/6.99.36.99.3
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 <thierry.bultel@iot.bzh>
Diffstat (limited to 'plugins/alsa/alsa-api-zones.c')
-rw-r--r--plugins/alsa/alsa-api-zones.c226
1 files changed, 161 insertions, 65 deletions
diff --git a/plugins/alsa/alsa-api-zones.c b/plugins/alsa/alsa-api-zones.c
index c8ab107..bf3bece 100644
--- a/plugins/alsa/alsa-api-zones.c
+++ b/plugins/alsa/alsa-api-zones.c
@@ -25,166 +25,261 @@
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);
- int error = wrap_json_unpack(channelJ, "{ss,si !}"
+ channel->volume = -1;
+
+ 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;
+ AFB_ApiDebug(mixer->api, "%s... %s (%d sinks, %d sources)", __func__, zone->uid, zone->nbSinks, zone->nbSources);
- for (index = 0; index < mixer->max.zones; index++) {
- if (!mixer->zones[index]) break;
- }
+ 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;
+ }
+
+ AlsaPcmChannelT * channel, *tmp;
- if (index == mixer->max.zones) {
- AFB_ReqFailF(request, "too-small", "mixer=%s max zone=%d", mixer->uid, mixer->max.zones);
- goto OnErrorExit;
+ cds_list_for_each_entry_safe(channel, tmp, &zone->sinks.list, list) {
+ cds_list_del(&channel->list);
+ free(channel);
+ }
+
+ cds_list_del(&zone->list);
+ mixer->nbZones--;
+
+ free((char*) zone->uid);
+ free(zone);
+ AFB_ApiDebug(mixer->api, "%s... DONE !", __func__);
+}
+
+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);
+
+ 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;
}
+ zone->isPcmPlug = routeConfig->isPcmPlug;
+
+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 +287,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: