diff options
author | Thierry Bultel <thierry.bultel@iot.bzh> | 2018-12-04 23:11:20 +0100 |
---|---|---|
committer | Thierry Bultel <thierry.bultel@iot.bzh> | 2018-12-19 23:09:21 +0100 |
commit | e0f57e523112e1bc73a04e8615d7a21355f0ce0e (patch) | |
tree | 19198c38d7433862cee733fde3efca8170228279 /plugins/alsa/alsa-api-loop.c | |
parent | 7df040a3742af8d800852dd39f8e921cd82a4cf2 (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-loop.c')
-rw-r--r-- | plugins/alsa/alsa-api-loop.c | 247 |
1 files changed, 178 insertions, 69 deletions
diff --git a/plugins/alsa/alsa-api-loop.c b/plugins/alsa/alsa-api-loop.c index 61bdc99..16491a0 100644 --- a/plugins/alsa/alsa-api-loop.c +++ b/plugins/alsa/alsa-api-loop.c @@ -24,31 +24,42 @@ PUBLIC AlsaLoopSubdevT *ApiLoopFindSubdev(SoftMixerT *mixer, const char *streamUid, const char *targetUid, AlsaSndLoopT **loop) { // Either allocate a free loop subdev or search for a specific targetUid when specified - if (targetUid) { - for (int idx = 0; mixer->loops[idx]; idx++) { - for (int jdx = 0; jdx < mixer->loops[idx]->scount; jdx++) { - if (mixer->loops[idx]->subdevs[jdx]->uid && !strcasecmp(mixer->loops[idx]->subdevs[jdx]->uid, targetUid)) { - *loop = mixer->loops[idx]; - return mixer->loops[idx]->subdevs[jdx]; - } - } - } - } else { - for (int idx = 0; mixer->loops[idx]; idx++) { - for (int jdx = 0; mixer->loops[idx]->subdevs[jdx]; jdx++) { - if (!mixer->loops[idx]->subdevs[jdx]->uid) { - mixer->loops[idx]->subdevs[jdx]->uid = streamUid; - *loop = mixer->loops[idx]; - return mixer->loops[idx]->subdevs[jdx]; - } - } - } - } - return NULL; + AlsaSndLoopT * _loop; + if (targetUid) { + cds_list_for_each_entry(_loop, &mixer->loops.list, list) { + AlsaLoopSubdevT * subdev; + cds_list_for_each_entry(subdev, &_loop->subdevs.list, list) { + if (subdev->uid && !strcmp(subdev->uid, targetUid)) { + *loop = _loop; + return subdev; + } + } + } + } else { + cds_list_for_each_entry(_loop, &mixer->loops.list, list) { + AlsaLoopSubdevT * subdev; + cds_list_for_each_entry(subdev, &_loop->subdevs.list, list) { + if (!subdev->uid) { + subdev->uid = streamUid; + *loop = _loop; + return subdev; + } + } + } + } + return NULL; } + STATIC AlsaLoopSubdevT *ProcessOneSubdev(SoftMixerT *mixer, AlsaSndLoopT *loop, json_object *subdevJ) { - AlsaLoopSubdevT *subdev = calloc(1, sizeof (AlsaPcmCtlT)); + + AFB_ApiDebug(mixer->api, "%s", __func__); + + AlsaLoopSubdevT *subdev = calloc(1, sizeof (AlsaLoopSubdevT)); + if (subdev == NULL) { + SOFTMIXER_NOMEM(mixer->api); + goto fail; + } int error = wrap_json_unpack(subdevJ, "{s?s, si,si !}" , "uid", &subdev->uid @@ -56,12 +67,20 @@ STATIC AlsaLoopSubdevT *ProcessOneSubdev(SoftMixerT *mixer, AlsaSndLoopT *loop, , "numid", &subdev->numid ); if (error) { - AFB_ApiError(mixer->api, "ProcessOneSubdev: loop=%s missing (uid|subdev|numid) error=%s json=%s", loop->uid, wrap_json_get_error_string(error),json_object_get_string(subdevJ)); - goto OnErrorExit; + AFB_ApiError(mixer->api, + "%s: loop=%s missing (uid|subdev|numid) error=%s json=%s", + __func__, loop->uid, wrap_json_get_error_string(error),json_object_get_string(subdevJ)); + goto fail_subdev; } // subdev with no UID are dynamically attached - if (subdev->uid) subdev->uid = strdup(subdev->uid); + if (subdev->uid) { + subdev->uid = strdup(subdev->uid); + if (subdev->uid == NULL) { + SOFTMIXER_NOMEM(mixer->api); + goto fail_subdev; + } + } // create loop subdev entry point with cardidx+device+subdev in order to open subdev and not sndcard AlsaDevInfoT loopSubdev; @@ -73,25 +92,61 @@ STATIC AlsaLoopSubdevT *ProcessOneSubdev(SoftMixerT *mixer, AlsaSndLoopT *loop, loopSubdev.subdev = subdev->index; // assert we may open this loopback subdev in capture mode - AlsaPcmCtlT *pcmInfo = AlsaByPathOpenPcm(mixer, &loopSubdev, SND_PCM_STREAM_CAPTURE); - if (!pcmInfo) goto OnErrorExit; + AlsaPcmCtlT *pcmCtl = AlsaByPathOpenPcmCtl(mixer, &loopSubdev, SND_PCM_STREAM_CAPTURE); + if (!pcmCtl) { + goto fail_subdev_uid; + } + + pcmCtl->closeAtDeletion = true; // free PCM as we only open loop to assert it's a valid capture device - snd_pcm_close(pcmInfo->handle); - free(pcmInfo); + AlsaPcmCtlDelete(mixer, pcmCtl); + + AFB_ApiDebug(mixer->api, "%s DONE", __func__); return subdev; -OnErrorExit: +fail_subdev_uid: + free((char*)subdev->uid); +fail_subdev: + free(subdev); +fail: return NULL; } +static void freeSubdev(AlsaLoopSubdevT * subdev) { + if (subdev->uid) + free((char*)subdev->uid); + free(subdev); +} + + STATIC AlsaSndLoopT *AttachOneLoop(SoftMixerT *mixer, const char *uid, json_object *argsJ) { - AlsaSndLoopT *loop = calloc(1, sizeof (AlsaSndLoopT)); + json_object *subdevsJ = NULL, *devicesJ = NULL; int error; - loop->sndcard = (AlsaSndCtlT*) calloc(1, sizeof (AlsaSndCtlT)); + AFB_ApiNotice(mixer->api, "%s, uid %s", __func__, uid); + + AlsaSndLoopT *loop = calloc(1, sizeof (AlsaSndLoopT)); + if (loop == NULL) { + SOFTMIXER_NOMEM(mixer->api); + goto fail; + } + + CDS_INIT_LIST_HEAD(&loop->list); + CDS_INIT_LIST_HEAD(&loop->subdevs.list); + + AlsaSndCtlT * sndctl = (AlsaSndCtlT*) calloc(1, sizeof (AlsaSndCtlT)); + if (sndctl == NULL) { + SOFTMIXER_NOMEM(mixer->api); + goto fail_loop; + } + + CDS_INIT_LIST_HEAD(&sndctl->registryList); + + loop->sndcard = sndctl; + error = wrap_json_unpack(argsJ, "{ss,s?s,s?s,so,so !}" , "uid", &loop->uid , "path", &loop->sndcard->cid.devpath @@ -102,7 +157,7 @@ STATIC AlsaSndLoopT *AttachOneLoop(SoftMixerT *mixer, const char *uid, json_obje if (error || !loop->uid || !subdevsJ || (!loop->sndcard->cid.devpath && !loop->sndcard->cid.cardid)) { AFB_ApiNotice(mixer->api, "%s mixer=%s hal=%s missing 'uid|path|cardid|devices|subdevs' error=%s args=%s", __func__, mixer->uid, uid, wrap_json_get_error_string(error),json_object_get_string(argsJ)); - goto OnErrorExit; + goto fail_snd_card; } // try to open sound card control interface @@ -110,10 +165,10 @@ STATIC AlsaSndLoopT *AttachOneLoop(SoftMixerT *mixer, const char *uid, json_obje if (!loop->sndcard->ctl) { AFB_ApiError(mixer->api, "%s mixer=%s hal=%s Fail open sndcard loop=%s devpath=%s cardid=%s (please check 'modprobe snd_aloop')", __func__, mixer->uid, uid, loop->uid, loop->sndcard->cid.devpath, loop->sndcard->cid.cardid); - goto OnErrorExit; + goto fail_snd_card; } - // Default devices is payback=0 capture=1 + // Default devices is playback=0 capture=1 if (!devicesJ) { loop->playback = 0; loop->capture = 1; @@ -122,87 +177,141 @@ STATIC AlsaSndLoopT *AttachOneLoop(SoftMixerT *mixer, const char *uid, json_obje if (error) { AFB_ApiNotice(mixer->api, "%s mixer=%s hal=%s Loop=%s missing 'capture|playback' error=%s devices=%s", __func__, mixer->uid, uid, loop->uid, wrap_json_get_error_string(error),json_object_get_string(devicesJ)); - goto OnErrorExit; + goto fail_snd_card_ctl; } } + AlsaLoopSubdevT * subdev; + switch (json_object_get_type(subdevsJ)) { case json_type_object: - loop->scount = 1; - loop->subdevs = calloc(2, sizeof (void*)); - loop->subdevs[0] = ProcessOneSubdev(mixer, loop, subdevsJ); - if (!loop->subdevs[0]) goto OnErrorExit; + subdev = ProcessOneSubdev(mixer, loop, subdevsJ); + if (!subdev) + goto fail_loop_subdev; + loop->nbSubdevs++; + cds_list_add_tail(&subdev->list, &loop->subdevs.list); break; - case json_type_array: - loop->scount = (int) json_object_array_length(subdevsJ); - loop->subdevs = calloc(loop->scount + 1, sizeof (void*)); - for (int idx = 0; idx < loop->scount; idx++) { + case json_type_array: { + int nbSubDevs = (int) json_object_array_length(subdevsJ); + for (int idx = 0; idx < nbSubDevs; idx++) { json_object *subdevJ = json_object_array_get_idx(subdevsJ, idx); - loop->subdevs[idx] = ProcessOneSubdev(mixer, loop, subdevJ); - if (!loop->subdevs[idx]) goto OnErrorExit; + subdev = ProcessOneSubdev(mixer, loop, subdevJ); + if (!subdev) { + goto fail_loop_subdev; + } + loop->nbSubdevs++; + cds_list_add_tail(&subdev->list, &loop->subdevs.list); } break; + } default: AFB_ApiError(mixer->api, "%s mixer=%s hal=%s Loop=%s invalid subdevs= %s", __func__, mixer->uid, uid, loop->uid, json_object_get_string(subdevsJ)); - goto OnErrorExit; + goto fail_snd_card_ctl; } - // we may have to register up to 3 control per subdevice (vol, pause, actif) - loop->sndcard->registry = calloc(loop->scount * SMIXER_SUBDS_CTLS + 1, sizeof (RegistryEntryPcmT)); - loop->sndcard->rcount = loop->scount*SMIXER_SUBDS_CTLS; + AFB_ApiNotice(mixer->api, "%s, uid %s DONE", __func__, uid); return loop; -OnErrorExit: +fail_loop_subdev: { + AFB_ApiDebug(mixer->api, "%s cleanup", __func__); + AlsaLoopSubdevT * subdev, *tmp; + cds_list_for_each_entry_safe(subdev, tmp, &loop->subdevs.list, list) { + cds_list_del(&subdev->list); + freeSubdev(subdev); + } +} + +fail_snd_card_ctl: + snd_ctl_close(loop->sndcard->ctl); +fail_snd_card: + free(loop->sndcard); +fail_loop: + free(loop); +fail: return NULL; } -PUBLIC int ApiLoopAttach(SoftMixerT *mixer, AFB_ReqT request, const char *uid, json_object * argsJ) { +static void loopDestroy(SoftMixerT * mixer, void* arg) { + AlsaSndLoopT * loop = (AlsaSndLoopT*) arg; + AFB_ApiDebug(mixer->api, "%s... %s not implemented", __func__, loop->uid); + return; - int index; - for (index = 0; index < mixer->max.loops; index++) { - if (!mixer->loops[index]) break; - } + if (loop->sndcard) { + if (loop->sndcard->ctl) { + snd_ctl_close(loop->sndcard->ctl); + free(loop->sndcard->ctl); + } + free(loop->sndcard); + } + + AlsaLoopSubdevT * subdev, *tmp; + cds_list_for_each_entry_safe(subdev, tmp, &loop->subdevs.list, list) { + cds_list_del(&subdev->list); + freeSubdev(subdev); + } + mixer->nbLoops--; + free(loop); - if (index == mixer->max.loops) { +} + +static AlsaSndLoopT * loopCreate(SoftMixerT *mixer, const char *uid, json_object *argsJ) { + AlsaSndLoopT * newLoop = AttachOneLoop(mixer, uid, argsJ); + if (!newLoop) { + goto fail; + } + + mixer->nbLoops++; + cds_list_add_tail(&newLoop->list, &mixer->loops.list); + AlsaMixerTransactionObjectAdd(mixer->transaction, newLoop, loopDestroy); + +fail: + return newLoop; +} + +PUBLIC int ApiLoopAttach(SoftMixerT *mixer, AFB_ReqT request, const char * uid, json_object * argsJ) { + + AlsaSndLoopT * newLoop = NULL; + + if (mixer->nbLoops >= mixer->max.loops) { AFB_IfReqFailF(mixer, request, "too-small", "mixer=%s hal=%s max loop=%d", mixer->uid, uid, mixer->max.loops); - goto OnErrorExit; + goto fail; } switch (json_object_get_type(argsJ)) { size_t count; case json_type_object: - mixer->loops[index] = AttachOneLoop(mixer, uid, argsJ); - if (!mixer->loops[index]) { - goto OnErrorExit; + newLoop = loopCreate(mixer, uid, argsJ); + if (!newLoop) { + goto fail; } break; case json_type_array: count = json_object_array_length(argsJ); - if (count > (mixer->max.loops - index)) { + if (count > (mixer->max.loops - mixer->nbLoops)) { AFB_IfReqFailF(mixer, request, "too-small", "mixer=%s hal=%s max loop=%d", mixer->uid, uid, mixer->max.loops); - goto OnErrorExit; - + goto fail_loop; } for (int idx = 0; idx < count; idx++) { json_object *loopJ = json_object_array_get_idx(argsJ, idx); - mixer->loops[index + idx] = AttachOneLoop(mixer, uid, loopJ); - if (!mixer->loops[index + idx]) { - goto OnErrorExit; + newLoop = loopCreate(mixer, uid, loopJ); + if (newLoop == NULL) { + goto fail_loop; } } break; default: AFB_IfReqFailF(mixer, request, "bad-loop", "mixer=%s hal=%s loops invalid argsJ= %s", mixer->uid, uid, json_object_get_string(argsJ)); - goto OnErrorExit; + goto fail; } return 0; -OnErrorExit: +fail_loop: +fail: return -1; } |