summaryrefslogtreecommitdiffstats
path: root/plugins/alsa/alsa-plug-route.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-plug-route.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-plug-route.c')
-rw-r--r--plugins/alsa/alsa-plug-route.c214
1 files changed, 145 insertions, 69 deletions
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;
}