From b6aec989e0620322250b033bf339db7868d117d3 Mon Sep 17 00:00:00 2001 From: Mark Farrugia Date: Tue, 29 Jan 2019 11:35:44 +1100 Subject: Add support for AVIRT Leverage the new AVIRT driver for a more secure, more dynamically configurable loopback sound driver. To use, replace the file smixer-4a-default.json with smixer-4a-avirt.json, at /usr/libexec/agl/smixer/etc The existing snd-aloop configuration is not broken by this change. The submodule libavirt is temporary, and will be transformed into a library. Change-Id: I827636656c109a7393ad77997e05069a2462ea46 Signed-off-by: Mark Farrugia --- plugins/alsa/alsa-api-loop.c | 308 +++++++++++++++++++++++++++++++++---------- 1 file changed, 235 insertions(+), 73 deletions(-) (limited to 'plugins/alsa/alsa-api-loop.c') diff --git a/plugins/alsa/alsa-api-loop.c b/plugins/alsa/alsa-api-loop.c index ffab6bc..8cf0c4e 100644 --- a/plugins/alsa/alsa-api-loop.c +++ b/plugins/alsa/alsa-api-loop.c @@ -19,8 +19,50 @@ #define _GNU_SOURCE // needed for vasprintf #include "alsa-softmixer.h" +#include #include +#define UID_AVIRT_LOOP "AVIRT-Loopback" + +#define REGNUMID_0 51 +#define REGNUMID_1 57 +#define REGNUMID_2 63 +#define REGNUMID_3 69 +#define REGNUMID_4 75 +#define REGNUMID_5 81 +#define REGNUMID_6 87 +#define REGNUMID_7 93 +#define REGNUMID_8 99 +#define REGNUMID_9 105 +#define REGNUMID_10 111 +#define REGNUMID_11 117 +#define REGNUMID_12 123 +#define REGNUMID_13 129 +#define REGNUMID_14 135 +#define REGNUMID_15 141 + +struct RegistryNumidMap { + int index; + int numid; +} numidmap[] = { + { 0, REGNUMID_0 }, + { 1, REGNUMID_1 }, + { 2, REGNUMID_2 }, + { 3, REGNUMID_3 }, + { 4, REGNUMID_4 }, + { 5, REGNUMID_5 }, + { 6, REGNUMID_6 }, + { 7, REGNUMID_7 }, + { 8, REGNUMID_8 }, + { 9, REGNUMID_9 }, + { 10, REGNUMID_10 }, + { 11, REGNUMID_11 }, + { 12, REGNUMID_12 }, + { 13, REGNUMID_13 }, + { 14, REGNUMID_14 }, + { 15, REGNUMID_15 }, +}; + 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 @@ -55,6 +97,90 @@ fail: return NULL; } +STATIC int CheckOneSubdev(SoftMixerT *mixer, AlsaSndLoopT *loop, AlsaLoopSubdevT *subdev) { + // create loop subdev entry point with cardidx+device+subdev in order to open subdev and not sndcard + AlsaDevInfoT loopSubdev; + loopSubdev.devpath=NULL; + loopSubdev.cardid=NULL; + loopSubdev.pcmplug_params = NULL; + loopSubdev.cardidx = loop->sndcard->cid.cardidx; + loopSubdev.device = loop->capture; + loopSubdev.subdev = subdev->index; + + // assert we may open this loopback subdev in capture mode + AlsaPcmCtlT *pcmCtl = AlsaByPathOpenPcmCtl(mixer, &loopSubdev, SND_PCM_STREAM_CAPTURE); + if (!pcmCtl) { + AFB_ApiError(mixer->api, "%s: Failed", __func__); + return -1; + } + + pcmCtl->closeAtDeletion = true; + + // free PCM as we only open loop to assert it's a valid capture device + AlsaMixerTransactionObjectForget(mixer->transaction, pcmCtl); + AlsaPcmCtlDelete(mixer, pcmCtl); + + return 0; +} + +STATIC AlsaLoopSubdevT *ProcessOneAvirtSubdev(SoftMixerT *mixer, AlsaSndLoopT *loop, json_object *streamJ, int index) { + char *uid, *zone_uid; + AlsaSndZoneT *zone; + AFB_ApiInfo(mixer->api, "%s: %d", __func__, index); + int error; + + uid = alloca(32); + + error = wrap_json_unpack(streamJ, "{ss,s?s,s?s,ss,s?s,s?i,s?b,s?o,s?s !}" + , "uid", &uid + , "verb", NULL + , "info", NULL + , "zone", &zone_uid + , "source", NULL + , "volume", NULL + , "mute", NULL + , "params", NULL + , "ramp", NULL + ); + if (error) { + AFB_ApiNotice(mixer->api, + "%s: hal=%s missing 'uid|[info]|zone|source||[volume]|[mute]|[params]' error=%s stream=%s", + __func__, uid, wrap_json_get_error_string(error), json_object_get_string(streamJ)); + goto OnErrorExit; + } + + if (mixer->nbZones != 0) { + zone = ApiZoneGetByUid(mixer, zone_uid); + if (!zone) + goto OnErrorExit; + } else { + AFB_ApiError(mixer->api, "%s: No zones defined!", __func__); + goto OnErrorExit; + } + + error = snd_avirt_stream_new(uid, zone->ccount, + SND_PCM_STREAM_PLAYBACK, "ap_loopback"); + if (error < 0) { + AFB_ApiError(mixer->api, + "%s: mixer=%s stream=%s could not create AVIRT stream [errno=%d]", + __func__, mixer->uid, uid, error); + return NULL; + } + + AlsaLoopSubdevT *subdev = calloc(1, sizeof (AlsaPcmCtlT)); + subdev->uid = NULL; + subdev->index = index; + subdev->numid = numidmap[index].index; + + if (CheckOneSubdev(mixer, loop, subdev) < 0) + return NULL; + + return subdev; + +OnErrorExit: + AFB_ApiError(mixer->api, "%s fail", __func__); + return NULL; +} STATIC AlsaLoopSubdevT *ProcessOneSubdev(SoftMixerT *mixer, AlsaSndLoopT *loop, json_object *subdevJ) { @@ -87,26 +213,8 @@ STATIC AlsaLoopSubdevT *ProcessOneSubdev(SoftMixerT *mixer, AlsaSndLoopT *loop, } } - // create loop subdev entry point with cardidx+device+subdev in order to open subdev and not sndcard - AlsaDevInfoT loopSubdev; - loopSubdev.devpath=NULL; - loopSubdev.cardid=NULL; - loopSubdev.pcmplug_params = NULL; - loopSubdev.cardidx = loop->sndcard->cid.cardidx; - loopSubdev.device = loop->capture; - loopSubdev.subdev = subdev->index; - - // assert we may open this loopback subdev in capture mode - 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 - AlsaMixerTransactionObjectForget(mixer->transaction, pcmCtl); - AlsaPcmCtlDelete(mixer, pcmCtl); + if (CheckOneSubdev(mixer, loop, subdev) < 0) + goto fail_subdev_uid; AFB_ApiDebug(mixer->api, "%s DONE", __func__); @@ -125,8 +233,7 @@ static void freeSubdev(SoftMixerT* mixer, AlsaLoopSubdevT * subdev) { free(subdev); } - -STATIC AlsaSndLoopT *AttachOneLoop(SoftMixerT *mixer, const char *uid, json_object *argsJ) { +STATIC AlsaSndLoopT *AttachOneLoop(SoftMixerT *mixer, const char *uid, json_object *argsJ, json_object *streamsJ) { json_object *subdevsJ = NULL, *devicesJ = NULL; int error; @@ -152,67 +259,122 @@ STATIC AlsaSndLoopT *AttachOneLoop(SoftMixerT *mixer, const char *uid, json_obje loop->sndcard = sndctl; - error = wrap_json_unpack(argsJ, "{ss,s?s,s?s,so,so !}" + error = wrap_json_unpack(argsJ, "{ss,s?s,s?s,s?o,s?o !}" , "uid", &loop->uid , "path", &loop->sndcard->cid.devpath , "cardid", &loop->sndcard->cid.cardid , "devices", &devicesJ , "subdevs", &subdevsJ ); - 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 fail_snd_card; + + if (loop->uid) { + if (!strcmp(loop->uid, UID_AVIRT_LOOP)) + loop->avirt = true; + else + loop->avirt = false; } - // try to open sound card control interface - loop->sndcard->ctl = AlsaByPathOpenCtl(mixer, loop->uid, loop->sndcard); - 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); + if (error || !loop->uid || (!loop->avirt && !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 fail_snd_card; } - // Default devices is playback=0 capture=1 - if (!devicesJ) { - loop->playback = 0; - loop->capture = 1; - } else { - error = wrap_json_unpack(devicesJ, "{si,si !}", "capture", &loop->capture, "playback", &loop->playback); - 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 fail_snd_card_ctl; + if (!loop->avirt) { + // try to open sound card control interface + loop->sndcard->ctl = AlsaByPathOpenCtl(mixer, loop->uid, loop->sndcard); + 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 fail_snd_card; } - } - AlsaLoopSubdevT * subdev; + // Default devices is playback=0 capture=1 + if (!devicesJ) { + loop->playback = 0; + loop->capture = 1; + } else { + error = wrap_json_unpack(devicesJ, "{si,si !}", "capture", &loop->capture, "playback", &loop->playback); + 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 fail_snd_card_ctl; + } + } - switch (json_object_get_type(subdevsJ)) { - case json_type_object: - 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: { - 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); - subdev = ProcessOneSubdev(mixer, loop, subdevJ); - if (!subdev) { - goto fail_loop_subdev; - } - loop->nbSubdevs++; - cds_list_add_tail(&subdev->list, &loop->subdevs.list); + AlsaLoopSubdevT * subdev; + + switch (json_object_get_type(subdevsJ)) { + case json_type_object: + 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: { + 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); + subdev = ProcessOneSubdev(mixer, loop, subdevJ); + if (!subdev) { + goto fail_loop_subdev; + } + loop->nbSubdevs++; + cds_list_add_tail(&subdev->list, &loop->subdevs.list); + } + break; } - 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 fail_snd_card_ctl; + 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 fail_snd_card_ctl; + } + } else { // loop->avirt == true + AFB_ApiNotice(mixer->api, "nbStreams: %d max.streams: %d", mixer->nbStreams, mixer->max.streams); + + if (mixer->nbStreams >= mixer->max.streams) + goto fail_snd_card_ctl; + + AlsaLoopSubdevT * subdev; + + // Create AVIRT streams + switch (json_object_get_type(streamsJ)) { + case json_type_object: + subdev = ProcessOneAvirtSubdev(mixer, loop, streamsJ, 0); + if (!subdev) + goto fail_loop_subdev; + loop->nbSubdevs++; + cds_list_add_tail(&subdev->list, &loop->subdevs.list); + break; + case json_type_array: { + int nbSubDevs = (int) json_object_array_length(streamsJ); + if (nbSubDevs >= mixer->max.streams) + goto fail_loop_subdev; + for (int idx = 0; idx < nbSubDevs; idx++) { + json_object *streamJ = json_object_array_get_idx(streamsJ, idx); + subdev = ProcessOneAvirtSubdev(mixer, loop, streamJ, idx); + if (!subdev) { + goto fail_loop_subdev; + } + loop->nbSubdevs++; + cds_list_add_tail(&subdev->list, &loop->subdevs.list); + } + break; + } + default: + goto fail_snd_card_ctl; + } + + snd_avirt_card_seal(); + + // try to open sound card control interface + loop->sndcard->ctl = AlsaByPathOpenCtl(mixer, loop->uid, loop->sndcard); + 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 fail_snd_card; + } } AFB_ApiNotice(mixer->api, "%s, uid %s DONE", __func__, uid); @@ -258,9 +420,9 @@ static void loopDestroy(SoftMixerT * mixer, void* arg) { } -static AlsaSndLoopT * loopCreate(SoftMixerT *mixer, const char *uid, json_object *argsJ) { +static AlsaSndLoopT * loopCreate(SoftMixerT *mixer, const char *uid, json_object *argsJ, json_object *streamsJ) { - AlsaSndLoopT * newLoop = AttachOneLoop(mixer, uid, argsJ); + AlsaSndLoopT * newLoop = AttachOneLoop(mixer, uid, argsJ, streamsJ); if (!newLoop) { goto fail; } @@ -276,7 +438,7 @@ fail: return newLoop; } -PUBLIC int ApiLoopAttach(SoftMixerT *mixer, AFB_ReqT request, const char * uid, json_object * argsJ) { +PUBLIC int ApiLoopAttach(SoftMixerT *mixer, AFB_ReqT request, const char * uid, json_object * argsJ, json_object *streamsJ) { AlsaSndLoopT * newLoop = NULL; @@ -289,7 +451,7 @@ PUBLIC int ApiLoopAttach(SoftMixerT *mixer, AFB_ReqT request, const char * uid, size_t count; case json_type_object: - newLoop = loopCreate(mixer, uid, argsJ); + newLoop = loopCreate(mixer, uid, argsJ, streamsJ); if (!newLoop) { goto fail; } @@ -304,7 +466,7 @@ PUBLIC int ApiLoopAttach(SoftMixerT *mixer, AFB_ReqT request, const char * uid, for (int idx = 0; idx < count; idx++) { json_object *loopJ = json_object_array_get_idx(argsJ, idx); - newLoop = loopCreate(mixer, uid, loopJ); + newLoop = loopCreate(mixer, uid, loopJ, streamsJ); if (newLoop == NULL) { goto fail_loop; } -- cgit 1.2.3-korg