aboutsummaryrefslogtreecommitdiffstats
path: root/plugins/alsa/alsa-api-loop.c
diff options
context:
space:
mode:
authorMark Farrugia <mark.farrugia@fiberdyne.com.au>2019-02-12 12:11:26 +1100
committerMark Farrugia <mark.farrugia@fiberdyne.com.au>2019-02-12 12:12:38 +1100
commitd5ac69750d6d16c88ee009e0106bfd6eeffaa695 (patch)
tree56026a1dc912f49d76ebc24fa3c27b2c9a7e82dd /plugins/alsa/alsa-api-loop.c
parentd5cc09d1ef5c58bb8d34e3a47c391a10d8b48181 (diff)
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 snd-avirt drivers are installed in AGL by default as of 6.99.2. The existing snd-aloop configuration is not broken by this change. Change-Id: I827636656c109a7393ad77997e05069a2462ea46 Signed-off-by: Mark Farrugia <mark.farrugia@fiberdyne.com.au>
Diffstat (limited to 'plugins/alsa/alsa-api-loop.c')
-rw-r--r--plugins/alsa/alsa-api-loop.c307
1 files changed, 235 insertions, 72 deletions
diff --git a/plugins/alsa/alsa-api-loop.c b/plugins/alsa/alsa-api-loop.c
index a123bb3..c0ea549 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 <avirt/avirt.h>
#include <string.h>
+#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_API_ERROR(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_API_INFO(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_API_NOTICE(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_API_ERROR(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_API_ERROR(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_API_ERROR(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_API_DEBUG(mixer->api, "%s DONE", __func__);
@@ -126,7 +234,7 @@ static void freeSubdev(SoftMixerT* mixer, AlsaLoopSubdevT * 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 +260,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_API_NOTICE(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_API_ERROR(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_API_NOTICE(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_API_NOTICE(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_API_ERROR(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_API_NOTICE(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_API_ERROR(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_API_ERROR(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_API_NOTICE(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_API_ERROR(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_API_NOTICE(mixer->api, "%s, uid %s DONE", __func__, uid);
@@ -258,9 +421,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 +439,7 @@ fail:
return newLoop;
}
-PUBLIC int ApiLoopAttach(SoftMixerT *mixer, afb_req_t request, const char * uid, json_object * argsJ) {
+PUBLIC int ApiLoopAttach(SoftMixerT *mixer, afb_req_t request, const char * uid, json_object * argsJ, json_object *streamsJ) {
AlsaSndLoopT * newLoop = NULL;
@@ -289,7 +452,7 @@ PUBLIC int ApiLoopAttach(SoftMixerT *mixer, afb_req_t 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 +467,7 @@ PUBLIC int ApiLoopAttach(SoftMixerT *mixer, afb_req_t 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;
}