summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMark Farrugia <mark.farrugia@fiberdyne.com.au>2019-04-01 14:12:27 +1100
committerMark Farrugia <mark.farrugia@fiberdyne.com.au>2019-04-01 14:13:16 +1100
commitf415e56c855c97a1bb0179734f649a623b209f65 (patch)
treeaf9df26a1879252ae3fc3853d539e90f01072417
parent09f65363ce3981cebd3c355dd0dcac479118b7bb (diff)
Add support for routes
Signed-off-by: Mark Farrugia <mark.farrugia@fiberdyne.com.au>
-rw-r--r--include/avirt/avirt.h64
-rw-r--r--src/avirt-config.c185
2 files changed, 210 insertions, 39 deletions
diff --git a/include/avirt/avirt.h b/include/avirt/avirt.h
index f1c4060..1ebed32 100644
--- a/include/avirt/avirt.h
+++ b/include/avirt/avirt.h
@@ -23,7 +23,46 @@
#ifndef _AVIRT_H_
#define _AVIRT_H_
+#define MAX_STREAMS 16
+#define MAX_NAME_LEN 80
+
#include <alsa/asoundlib.h>
+#include <stdbool.h>
+
+/**
+ * AVIRT Audio Path info
+ */
+typedef struct snd_avirt_audiopath snd_avirt_audiopath;
+struct snd_avirt_audiopath {
+ const char *uid; /* Unique identifier */
+ const char *name; /* Pretty name */
+ unsigned int version[3]; /* Version - Major.Minor.Ext */
+
+ // MUST be at the end
+ struct snd_avirt_audiopath *route_from_ap;
+ struct snd_avirt_audiopath *route_to_ap;
+};
+
+/*
+ * Audio stream configuration
+ */
+struct snd_avirt_stream {
+ const char name[MAX_NAME_LEN]; /* Stream name */
+ const char map[MAX_NAME_LEN]; /* Stream Audio Path mapping */
+ unsigned int channels; /* Stream channel count */
+ unsigned int direction; /* Stream direction */
+};
+
+/**
+ * Audio routing
+ */
+struct snd_avirt_route {
+ const char name[MAX_NAME_LEN]; /* Route name */
+ unsigned int channels; /* Route channel count */
+ unsigned int direction; /* Stream direction */
+ struct snd_avirt_audiopath *from_ap;
+ struct snd_avirt_audiopath *to_ap;
+};
/**
* snd_avirt_stream_new - Create a stream in AVIRT
@@ -32,6 +71,7 @@
* @direction: The stream direction
* (SND_PCM_STREAM_PLAYBACK or SND_PCM_STREAM_CAPTURE)
* @map: The audio path to map this stream to
+ * @internal: Make the PCM internal.
* @return: 0 on success, negative ERRNO otherwise
*
* Each stream creates a PCM device for the AVIRT sound card.
@@ -39,7 +79,29 @@
* is called.
*/
int snd_avirt_stream_new(const char *name, unsigned int channels, int direction,
- const char *map);
+ const char *map, bool internal);
+
+/**
+ * snd_avirt_route_new - Create a route between Audio Paths in AVIRT
+ * @name: The name of the route.
+ * @channels: Number of channels for the route
+ * @direction: The route stream direction
+ * (SND_PCM_STREAM_PLAYBACK or SND_PCM_STREAM_CAPTURE)
+ * @from_ap: The Audio Path UID to route audio from
+ * @to_ap: The Audio Path UID to route audio to
+ * @return: 0 on success, negative ERRNO otherwise
+ *
+ * Allows Audio Paths to be piped together to allow for modular processing
+ * and/or networking. The "to" Audio Path must be able to accept a stream
+ * input whose channel count equals to "from" Audio Path's output channel
+ * count.
+ */
+int snd_avirt_route_new(const char *name, int channels, int direction,
+ const char *from_ap, const char *to_ap);
+
+int snd_avirt_routes(struct snd_avirt_route **routes, int *count);
+int snd_avirt_streams(struct snd_avirt_stream **streams, int *count);
+int snd_avirt_audiopaths(struct snd_avirt_audiopath **audiopaths, int *count);
/**
* snd_avirt_card_seal - Finalize AVIRT stream creation and register sound card
diff --git a/src/avirt-config.c b/src/avirt-config.c
index 0f92540..267c727 100644
--- a/src/avirt-config.c
+++ b/src/avirt-config.c
@@ -29,8 +29,12 @@
#include <sys/mount.h>
#define AVIRT_CONFIGFS_PATH_STREAMS "/config/snd-avirt/streams/"
-#define AVIRT_CONFIGFS_PATH_MAXLEN 64
-#define AVIRT_DEVICE_PATH "/dev/snd/by-path/platform-snd_avirt"
+#define AVIRT_CONFIGFS_PATH_ROUTES "/config/snd-avirt/routes/"
+#define AVIRT_SYSFS_PATH_AUDIOPATHS "/sys/class/snd_avirt/core/audiopaths/"
+#define AVIRT_DEVICE_PATH "/dev/snd/by-path/platform-snd_avirt"
+
+#define AVIRT_CONFIGFS_PATH_MAXLEN 64
+#define AVIRT_SYSFS_PATH_MAXLEN 80
/**
* Logging macros
@@ -141,6 +145,32 @@ static int mount_configfs()
return err;
}
+static int audiopath_exists(const char *uid)
+{
+ DIR *dir;
+ char path[AVIRT_SYSFS_PATH_MAXLEN];
+
+ // Check that the Audio Paths exist
+ strcpy(path, AVIRT_SYSFS_PATH_AUDIOPATHS);
+ strcat(path, uid);
+ dir = opendir(path);
+ if (dir)
+ {
+ closedir(dir);
+ return 0;
+ }
+ else if (errno == ENOENT)
+ {
+ AVIRT_ERROR_V("Audio Path '%s' does not exist", uid);
+ }
+ else
+ {
+ AVIRT_ERROR("Could not check for Audio Path existence");
+ }
+
+ return -errno;
+}
+
static int find_mixer_selem(const char *name, snd_mixer_t **handle,
snd_mixer_elem_t **selem)
{
@@ -178,6 +208,52 @@ close_handle:
return retval;
}
+static int snd_avirt_configfs_item_new(const char *name,
+ unsigned int direction,
+ char *path, bool internal)
+{
+ int err;
+
+ IS_CONFIGFS_MOUNTED();
+
+ // Check if card is already sealed
+ if (card_sealed)
+ {
+ AVIRT_ERROR("Card is already sealed!");
+ return -EPERM;
+ }
+
+ // This indicates to AVIRT the direction of the item
+ switch (direction) {
+ case SND_PCM_STREAM_PLAYBACK:
+ strcat(path, "playback_");
+ break;
+ case SND_PCM_STREAM_CAPTURE:
+ strcat(path, "capture_");
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if ((AVIRT_CONFIGFS_PATH_MAXLEN - strlen(path)) < strlen(name))
+ {
+ AVIRT_ERROR_V("Cannot create config item '%s' since name is too long!", name);
+ return -ENOMEM;
+ }
+
+ strcat(path, name);
+ if (internal)
+ strcat(path, "__internal");
+ err = mkdir(path, S_IRWXU | S_IRWXG | S_IRWXO);
+ if (err < 0)
+ {
+ AVIRT_ERROR_V("Cannot create config item '%s' at directory '%s'", name, path);
+ return err;
+ }
+
+ return 0;
+}
+
int snd_avirt_card_index_get(int idx)
{
int open_dev, err = 0;
@@ -303,9 +379,8 @@ close_handle:
}
int snd_avirt_stream_new(const char *name, unsigned int channels, int direction,
- const char *map)
+ const char *map, bool internal)
{
- int err;
char path[AVIRT_CONFIGFS_PATH_MAXLEN];
char path_attr[AVIRT_CONFIGFS_PATH_MAXLEN];
@@ -315,41 +390,8 @@ int snd_avirt_stream_new(const char *name, unsigned int channels, int direction,
return -ERANGE;
}
- IS_CONFIGFS_MOUNTED();
-
- // Check if card is already sealed
- if (card_sealed)
- {
- AVIRT_ERROR("Card is already sealed!");
- return -EPERM;
- }
-
- // This indicates to AVIRT the direction of the stream
strcpy(path, AVIRT_CONFIGFS_PATH_STREAMS);
- switch (direction) {
- case SND_PCM_STREAM_PLAYBACK:
- strcat(path, "playback_");
- break;
- case SND_PCM_STREAM_CAPTURE:
- strcat(path, "capture_");
- break;
- default:
- return -EINVAL;
- }
-
- if ((AVIRT_CONFIGFS_PATH_MAXLEN - strlen(path)) < strlen(name))
- {
- AVIRT_ERROR_V("Cannot create stream '%s' since name is too long!", name);
- return -ENOMEM;
- }
-
- strcat(path, name);
- err = mkdir(path, S_IRWXU | S_IRWXG | S_IRWXO);
- if (err < 0)
- {
- AVIRT_ERROR_V("Cannot create stream '%s' at directory '%s'", name, path);
- return err;
- }
+ CHK_ERR(snd_avirt_configfs_item_new(name, direction, path, internal), "");
// Write channels
strcpy(path_attr, path);
@@ -359,6 +401,7 @@ int snd_avirt_stream_new(const char *name, unsigned int channels, int direction,
if (map)
{
// Write mapping
+ memset(path_attr, 0, AVIRT_CONFIGFS_PATH_MAXLEN);
strcpy(path_attr, path);
strcat(path_attr, "/map");
WRITE_TO_PATH(path_attr, "%s", map);
@@ -373,9 +416,64 @@ int snd_avirt_stream_new(const char *name, unsigned int channels, int direction,
return 0;
}
+int snd_avirt_route_new(const char *name, int channels, int direction,
+ const char *source_ap, const char *sink_ap)
+{
+ int err;
+ char path[AVIRT_CONFIGFS_PATH_MAXLEN];
+ char path_attr[AVIRT_CONFIGFS_PATH_MAXLEN];
+
+ if ((channels > __INT_MAX__) || (channels == 0))
+ {
+ AVIRT_ERROR_V("Channels '%d' is out of range!", channels);
+ return -ERANGE;
+ }
+
+ // Check that the Audio Paths exist
+ err = audiopath_exists(source_ap);
+ if (err < 0)
+ return err;
+ err = audiopath_exists(sink_ap);
+ if (err < 0)
+ return err;
+
+ strcpy(path, AVIRT_CONFIGFS_PATH_ROUTES);
+ CHK_ERR(snd_avirt_configfs_item_new(name, direction, path, false), "");
+
+ // Write channels
+ strcpy(path_attr, path);
+ strcat(path_attr, "/channels");
+ WRITE_TO_PATH(path_attr, "%d", channels);
+
+ // Write route_sink_ap into route_source_ap's 'to' path
+ memset(path_attr, 0, AVIRT_CONFIGFS_PATH_MAXLEN);
+ strcpy(path_attr, path);
+ strcat(path_attr, "/sink_ap");
+ WRITE_TO_PATH(path_attr, sink_ap);
+
+ // Write route_source_ap into route_sink_ap's 'from' path
+ memset(path_attr, 0, AVIRT_CONFIGFS_PATH_MAXLEN);
+ strcpy(path_attr, path);
+ strcat(path_attr, "/source_ap");
+ WRITE_TO_PATH(path_attr, source_ap);
+
+ CHK_ERR(snd_avirt_stream_new(name, channels, !direction, source_ap, true),
+ "Couldn't create stream: %s", name);
+
+ CHK_ERR(snd_avirt_stream_new(name, channels, direction, sink_ap, false),
+ "Couldn't create stream: %s", name);
+
+ AVIRT_DEBUG_V("Created route: %s -> %s", source_ap, sink_ap);
+
+ return 0;
+}
+
int snd_avirt_card_seal()
{
+ char cmd[128];
char path_sealed[AVIRT_CONFIGFS_PATH_MAXLEN];
+ snd_pcm_info_t *route_pcm_info;
+ int err = 0;
// Check if card is already sealed
if (card_sealed)
@@ -398,8 +496,19 @@ int snd_avirt_card_seal()
if (card_index < 0)
return card_index;
+ // TD MF: HACK: Open the UNICENS device
+ snd_pcm_info_alloca(&route_pcm_info);
+ snd_avirt_pcm_info("ep01-6ch", route_pcm_info);
+
+ sprintf(cmd, "speaker-test -Dhw:%d,%d -c6 >/dev/null &",
+ card_index, snd_pcm_info_get_device(route_pcm_info));
+ AVIRT_DEBUG_V("Running router: '%s'", cmd);
+ err = system(cmd);
+ return 0;
+}
+
return 0;
}