diff options
author | Mark Farrugia <mark.farrugia@fiberdyne.com.au> | 2019-04-01 14:12:27 +1100 |
---|---|---|
committer | Mark Farrugia <mark.farrugia@fiberdyne.com.au> | 2019-04-01 14:13:16 +1100 |
commit | f415e56c855c97a1bb0179734f649a623b209f65 (patch) | |
tree | af9df26a1879252ae3fc3853d539e90f01072417 | |
parent | 09f65363ce3981cebd3c355dd0dcac479118b7bb (diff) |
Add support for routes
Signed-off-by: Mark Farrugia <mark.farrugia@fiberdyne.com.au>
-rw-r--r-- | include/avirt/avirt.h | 64 | ||||
-rw-r--r-- | src/avirt-config.c | 185 |
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; } |