aboutsummaryrefslogtreecommitdiffstats
path: root/core.c
diff options
context:
space:
mode:
Diffstat (limited to 'core.c')
-rw-r--r--core.c389
1 files changed, 351 insertions, 38 deletions
diff --git a/core.c b/core.c
index 2c643fc..c03bf1c 100644
--- a/core.c
+++ b/core.c
@@ -35,7 +35,7 @@ MODULE_LICENSE("GPL v2");
static struct snd_avirt_core core = {
.version = { 0, 0, 1 },
.stream_count = 0,
- .streams_sealed = false,
+ .streams_configured = false,
};
static LIST_HEAD(audiopath_list);
@@ -51,7 +51,7 @@ static void pcm_private_free(struct snd_pcm *pcm)
/* Free Audio Path private data */
avirt_private_data = (struct snd_avirt_private_data *)pcm->private_data;
if (avirt_private_data) {
- if (avirt_private_data->ap_private_data &&
+ if (avirt_private_data->ap_private_data[0] &&
avirt_private_data->ap_private_free)
avirt_private_data->ap_private_free(pcm);
}
@@ -77,8 +77,14 @@ static struct snd_pcm *snd_avirt_pcm_create(struct snd_avirt_stream *stream)
capture = true;
}
- err = snd_pcm_new(core.card, stream->name, stream->device, playback,
- capture, &pcm);
+ if (stream->internal) {
+ err = snd_pcm_new_internal(core.card, stream->name,
+ stream->device, playback, capture,
+ &pcm);
+ } else {
+ err = snd_pcm_new(core.card, stream->name, stream->device,
+ playback, capture, &pcm);
+ }
if (err < 0) {
D_ERRORK("Failed to create PCM device for stream: '%s'",
@@ -94,7 +100,7 @@ static struct snd_pcm *snd_avirt_pcm_create(struct snd_avirt_stream *stream)
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, stream->pcm_ops);
pcm->info_flags = 0;
- strcpy(pcm->name, stream->name);
+ strncpy(pcm->name, stream->name, MAX_NAME_LEN);
avirt_private_data = kzalloc(sizeof(*avirt_private_data), GFP_KERNEL);
pcm->private_data = avirt_private_data;
@@ -106,27 +112,98 @@ static struct snd_pcm *snd_avirt_pcm_create(struct snd_avirt_stream *stream)
return pcm;
}
-int snd_avirt_stream_set_map(struct snd_avirt_stream *stream, const char *map)
+void snd_avirt_stream_try_destroy(struct snd_avirt_stream *stream)
+{
+ unsigned long _flags;
+ struct snd_pcm_substream *substream =
+ stream->pcm->streams[stream->direction].substream;
+
+ snd_pcm_stream_lock_irqsave(substream, _flags);
+ if (substream->runtime) {
+ if (snd_pcm_running(substream)) {
+ if (0 !=
+ snd_pcm_stop(substream, SNDRV_PCM_STATE_SUSPENDED))
+ D_ERRORK("Could not stop PCM '%s'",
+ stream->name);
+ }
+ }
+ snd_pcm_stream_unlock_irqrestore(substream, _flags);
+
+ snd_device_free(core.card, stream->pcm);
+ kfree(stream->pcm_ops);
+ kfree(stream);
+
+ core.stream_count--;
+}
+
+static struct snd_avirt_route *snd_avirt_route_get(const char *uid)
+{
+ struct list_head *entry;
+ struct config_item *item;
+ struct snd_avirt_route *route;
+
+ list_for_each (entry, &core.route_group->cg_children) {
+ item = container_of(entry, struct config_item, ci_entry);
+ route = snd_avirt_route_from_config_item(item);
+ if (!strcmp(route->uid, uid))
+ return route;
+ }
+
+ return NULL;
+}
+
+/**
+ * int snd_avirt_route_try_complete - Set up remaining parameters for a route.
+ * Channels, sink, and source Audio Paths
+ * should be set when calling this function.
+ * @stream: The route to attempt to finalize parameters for.
+ * @return: 0 on success, negative ERRNO on failure
+ */
+int snd_avirt_route_try_complete(struct snd_avirt_route *route)
+{
+ return 0;
+}
+
+/**
+ * int snd_avirt_stream_try_complete - Set up remaining parameters for a stream.
+ * Channels and map should be set when
+ * calling this function.
+ * @stream: The stream to attempt to finalize parameters for.
+ * @return: 0 on success, negative ERRNO on failure
+ */
+int snd_avirt_stream_try_complete(struct snd_avirt_stream *stream)
{
struct snd_avirt_audiopath *audiopath;
+ struct snd_avirt_route *route;
struct snd_pcm_ops *pcm_ops_ap;
- /* If already sealed, we cannot create the stream */
- if (snd_avirt_streams_sealed()) {
- D_ERRORK("Streams already sealed. Cannot set map: '%s'", map);
+ if (snd_avirt_streams_configured())
return -EPERM;
- }
- if (!strcmp(stream->map, map))
- return -1;
+ if ((stream->channels == 0) || (!strcmp(stream->map, "none")))
+ return -EPERM;
- memcpy(stream->map, (char *)map, strlen(map));
- audiopath = snd_avirt_audiopath_get(map);
+ audiopath = snd_avirt_audiopath_get(stream->map);
if (!audiopath) {
D_ERRORK("Cannot find Audio Path uid: '%s'!", stream->map);
- return -EFAULT;
}
+ /* Check for any routes that have been created for this stream */
+ route = snd_avirt_route_get(stream->name);
+ if (route) {
+ if (audiopath == route->endpoint_ap[SND_AVIRT_ROUTE_SOURCE])
+ route->endpoint_stream[SND_AVIRT_ROUTE_SOURCE] = stream;
+ else if (audiopath == route->endpoint_ap[SND_AVIRT_ROUTE_SINK])
+ route->endpoint_stream[SND_AVIRT_ROUTE_SINK] = stream;
+ else {
+ D_INFOK("Cannot set route. Audio Path not compatible");
+ return -EPERM;
+ }
+
+ stream->route = route;
+ }
+
+ /* Set up PCM ops */
if (!stream->direction)
pcm_ops_ap = (struct snd_pcm_ops *)audiopath->pcm_playback_ops;
else
@@ -140,8 +217,6 @@ int snd_avirt_stream_set_map(struct snd_avirt_stream *stream, const char *map)
}
/* Set PCM ops for the Audio Path*/
- PCM_OPS_SET(pcm_ops_ap, &stream->pcm_ops, prepare);
- PCM_OPS_SET(pcm_ops_ap, &stream->pcm_ops, trigger);
PCM_OPS_SET(pcm_ops_ap, &stream->pcm_ops, pointer);
PCM_OPS_SET(pcm_ops_ap, &stream->pcm_ops, get_time_info);
PCM_OPS_SET(pcm_ops_ap, &stream->pcm_ops, silence);
@@ -159,6 +234,12 @@ int snd_avirt_stream_set_map(struct snd_avirt_stream *stream, const char *map)
return 0;
}
+/**
+ * snd_avirt_streams_get - Get AVIRT streams for a given Audio Path map
+ * @map: The Audio Path UID whose streams to find.
+ * @stream_array: To be populated with streams.
+ * @return: The number of streams found for the Audio Path.
+ */
static int snd_avirt_streams_get(const char *map,
struct snd_avirt_stream_array *stream_array)
{
@@ -178,9 +259,124 @@ static int snd_avirt_streams_get(const char *map,
}
/**
- * snd_avirt_audiopath_get - retrieves the Audio Path by its UID
- * @uid: Unique ID for the Audio Path
- * @return: Corresponding Audio Path
+ * snd_avirt_route_endpoint_pos - get route endpoint position for Audio Path
+ * @pcm: The PCM whose route to inspect.
+ * @ap_uid: The Audio Path UID to get
+ * @endpoint: The route position (SND_AVIRT_ROUTE_SOURCE or SND_AVIRT_ROUTE_SINK)
+ * of the Audio Path in it's route (if any).
+ * @return: 0 if an Audio Path is found in the route, -1 if there is no route,
+ * or -2 otherwise.
+ */
+int snd_avirt_route_endpoint_pos(struct snd_pcm *pcm, const char *ap_uid,
+ snd_avirt_route_endpoint *endpoint)
+{
+ struct snd_avirt_audiopath *endpoint_ap;
+ struct snd_avirt_stream *stream;
+ int i;
+
+ stream = snd_avirt_stream_find_by_device(pcm->device);
+ if (IS_ERR_VALUE(stream) || !stream)
+ goto exit_err;
+
+ if (!stream->route)
+ return -1;
+
+ for (i = 0; i < 2; i++) {
+ endpoint_ap = stream->route->endpoint_ap[i];
+ if (endpoint_ap)
+ if (!strcmp(endpoint_ap->uid, ap_uid)) {
+ *endpoint = i;
+ return 0;
+ }
+ }
+
+exit_err:
+ D_ERRORK("Could not find Audio Path '%s' in route '%s'", ap_uid,
+ stream->route->uid);
+ return -2;
+}
+
+/**
+ * snd_avirt_route_endpoint_copy - get endpoint copy function for a given
+ * Audio Path's source/sink.
+ * @ap: The Audio Path whose endpoint copy function to find.
+ * @endpoint: The endpoint (SND_AVIRT_ROUTE_SOURCE or SND_AVIRT_ROUTE_SINK).
+ * @return: A snd_pcm_copy_kernel function pointer that can be used to either:
+ * 1. Source PCM data into the Audio Path, or,
+ * 2. Sink PCM data out of the Audio Path.
+ * If no Audio Path endpoint is routed for 'endpoint', NULL is returned.
+ */
+snd_pcm_copy_kernel
+snd_avirt_route_endpoint_copy(struct snd_pcm_substream *substream,
+ snd_avirt_route_endpoint endpoint)
+{
+ struct snd_avirt_audiopath *endpoint_ap;
+ struct snd_avirt_stream *stream;
+
+ if (endpoint < 0 || endpoint > 1) {
+ D_ERRORK("Route endpoint must be 0 or 1");
+ return NULL;
+ }
+
+ stream = snd_avirt_stream_find_by_device(substream->pcm->device);
+ if (IS_ERR_VALUE(stream) || !stream)
+ return NULL;
+
+ if (!stream->route)
+ return NULL;
+ endpoint_ap = stream->route->endpoint_ap[endpoint];
+ if (!endpoint_ap)
+ return NULL;
+
+ switch (endpoint) {
+ case SND_AVIRT_ROUTE_SOURCE:
+ return endpoint_ap->pcm_capture_ops->copy_kernel;
+ case SND_AVIRT_ROUTE_SINK:
+ return endpoint_ap->pcm_playback_ops->copy_kernel;
+ }
+
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(snd_avirt_route_endpoint_copy);
+
+/**
+ * snd_avirt_route_endpoint_trigger - Trigger an Audio Path's endpoint
+ * (sink/source).
+ * @uid: The Audio Path whose endpoint trigger function to call.
+ * @endpoint: The endpoint (SND_AVIRT_ROUTE_SOURCE or SND_AVIRT_ROUTE_SINK).
+ * @return: 0 on success or -1 on failure.
+ */
+int snd_avirt_route_endpoint_trigger(struct snd_pcm_substream *substream,
+ snd_avirt_route_endpoint endpoint)
+{
+ struct snd_avirt_audiopath *endpoint_ap;
+ struct snd_avirt_stream *stream;
+
+ if (endpoint < 0 || endpoint > 1) {
+ D_ERRORK("Route endpoint must be 0 or 1");
+ return -1;
+ }
+
+ stream = snd_avirt_stream_find_by_device(substream->pcm->device);
+ if (IS_ERR_VALUE(stream) || !stream)
+ return -1;
+
+ if (!stream->route)
+ return -1;
+ endpoint_ap = stream->route->endpoint_ap[endpoint];
+ if (!endpoint_ap)
+ return -1;
+
+ endpoint_ap->pcm_exttrigger();
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_avirt_route_endpoint_trigger);
+
+/**
+ * snd_avirt_audiopath_get - get Audio Path by it's UID
+ * @uid: The Audio Path UID to get
+ * @return: The Audio Path if it exists, NULL otherwise.
*/
struct snd_avirt_audiopath *snd_avirt_audiopath_get(const char *uid)
{
@@ -195,6 +391,86 @@ struct snd_avirt_audiopath *snd_avirt_audiopath_get(const char *uid)
}
EXPORT_SYMBOL_GPL(snd_avirt_audiopath_get);
+/*
+ * snd_avirt_audiopath_set_private_data - set PCM private data for an Audio Path
+ * @ap: The Audio Path whose private data to set.
+ * @pcm: The PCM where the private data is stored.
+ * @ap_private_data: The value to set to the private data.
+ * @return: 0 on success, -1 on failure.
+ */
+int snd_avirt_audiopath_set_private_data(struct snd_avirt_audiopath *ap,
+ struct snd_pcm *pcm,
+ void *ap_private_data)
+{
+ int err = 0;
+ snd_avirt_route_endpoint endpoint = SND_AVIRT_ROUTE_SOURCE;
+ struct snd_avirt_stream *stream;
+ struct snd_avirt_private_data *avirt_private_data;
+
+ stream = snd_avirt_stream_find_by_device(pcm->device);
+ if (IS_ERR_VALUE(stream) || !stream)
+ goto exit_err;
+
+ err = snd_avirt_route_endpoint_pos(pcm, ap->uid, &endpoint);
+ if (err == -2)
+ goto exit_err;
+
+ if (stream->internal && stream->route)
+ pcm = stream->route->endpoint_stream[!endpoint]->pcm;
+
+ avirt_private_data = pcm->private_data;
+ if (!avirt_private_data)
+ goto exit_err;
+
+ avirt_private_data->ap_private_data[endpoint] = ap_private_data;
+
+ return 0;
+
+exit_err:
+ D_ERRORK("Error setting private data for ap:%s, stream:%s, endpoint:%d",
+ ap->uid, pcm->name, endpoint);
+ return -1;
+}
+EXPORT_SYMBOL_GPL(snd_avirt_audiopath_set_private_data);
+
+/*
+ * snd_avirt_audiopath_get_private_data - get PCM private data for an Audio Path
+ * @ap: The Audio Path whose private data to get.
+ * @pcm: The PCM where the private data is stored.
+ * @return: The value assigned to the private data.
+ */
+void *snd_avirt_audiopath_get_private_data(struct snd_avirt_audiopath *ap,
+ struct snd_pcm *pcm)
+{
+ int err = 0;
+ snd_avirt_route_endpoint endpoint = SND_AVIRT_ROUTE_SOURCE;
+ struct snd_avirt_stream *stream;
+ struct snd_avirt_private_data *avirt_private_data;
+
+ stream = snd_avirt_stream_find_by_device(pcm->device);
+ if (IS_ERR_VALUE(stream) || !stream)
+ return NULL;
+
+ err = snd_avirt_route_endpoint_pos(pcm, ap->uid, &endpoint);
+ if (err == -2)
+ goto exit_err;
+
+ if (stream->internal && stream->route)
+ pcm = stream->route->endpoint_stream[!endpoint]->pcm;
+
+ avirt_private_data = pcm->private_data;
+ if (!avirt_private_data)
+ goto exit_err;
+
+ return avirt_private_data->ap_private_data[endpoint];
+
+exit_err:
+ D_ERRORK("Error getting private data for ap:%s, stream:%s, endpoint:%d",
+ ap->uid, pcm->name, endpoint);
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(snd_avirt_audiopath_get_private_data);
+
/**
* snd_avirt_audiopath_register - register Audio Path with AVIRT
* @audiopath: Audio Path to be registered
@@ -222,8 +498,8 @@ int snd_avirt_audiopath_register(struct snd_avirt_audiopath *audiopath)
list_add_tail(&audiopath_obj->list, &audiopath_list);
- // If we have already sealed the streams, configure this AP
- if (core.streams_sealed) {
+ // If we have already configured the streams, configure this AP
+ if (core.streams_configured) {
stream_array.count = 0;
if (snd_avirt_streams_get(audiopath->uid, &stream_array) > 0)
audiopath->configure(core.card, &stream_array);
@@ -264,12 +540,12 @@ EXPORT_SYMBOL_GPL(snd_avirt_audiopath_deregister);
/**
* snd_avirt_route_create - Create audio route
- * @name: The name designated to the audio route
+ * @uid: The unique ID designated to the audio route.
* @direction: The PCM direction (SNDRV_PCM_STREAM_PLAYBACK or
* SNDRV_PCM_STREAM_CAPTURE)
* @return: The newly created audio route if successful, or an error pointer
*/
-struct snd_avirt_route *snd_avirt_route_create(const char *name, int direction)
+struct snd_avirt_route *snd_avirt_route_create(const char *uid, int direction)
{
struct snd_avirt_route *route;
@@ -277,7 +553,7 @@ struct snd_avirt_route *snd_avirt_route_create(const char *name, int direction)
if (!route)
return ERR_PTR(-ENOMEM);
- strcpy(route->name, name);
+ strncpy(route->uid, uid, MAX_NAME_LEN);
route->channels = 0;
route->direction = direction;
@@ -289,10 +565,11 @@ struct snd_avirt_route *snd_avirt_route_create(const char *name, int direction)
* @name: The name designated to the audio stream
* @direction: The PCM direction (SNDRV_PCM_STREAM_PLAYBACK or
* SNDRV_PCM_STREAM_CAPTURE)
+ * @internal: Whether the PCM should be internal or not
* @return: The newly created audio stream if successful, or an error pointer
*/
struct snd_avirt_stream *snd_avirt_stream_create(const char *name,
- int direction)
+ int direction, bool internal)
{
struct snd_avirt_stream *stream;
@@ -305,10 +582,11 @@ struct snd_avirt_stream *snd_avirt_stream_create(const char *name,
if (!stream)
return ERR_PTR(-ENOMEM);
- strcpy(stream->name, name);
- strcpy(stream->map, "none");
+ strncpy(stream->name, name, MAX_NAME_LEN);
+ strncpy(stream->map, "none", MAX_NAME_LEN);
stream->channels = 0;
stream->direction = direction;
+ stream->internal = internal;
stream->device = core.stream_count++;
/* Initialize PCM ops table for this stream.
@@ -320,19 +598,24 @@ struct snd_avirt_stream *snd_avirt_stream_create(const char *name,
}
memcpy(stream->pcm_ops, &pcm_ops_avirt, sizeof(struct snd_pcm_ops));
- D_INFOK("name:%s device:%d", name, stream->device);
+ if (stream->internal) {
+ D_INFOK("name:%s device:%d internal:%d", name, stream->device,
+ stream->internal);
+ } else {
+ D_INFOK("name:%s device:%d", name, stream->device);
+ }
return stream;
}
-int snd_avirt_streams_seal(void)
+int snd_avirt_streams_configure(void)
{
int err = 0, i = 0;
struct snd_avirt_audiopath_obj *ap_obj;
struct snd_avirt_stream_array stream_array;
- if (core.streams_sealed) {
- D_ERRORK("streams are already sealed!");
+ if (core.streams_configured) {
+ D_ERRORK("streams are already configured!");
return -1;
}
@@ -344,20 +627,50 @@ int snd_avirt_streams_seal(void)
0)
continue;
- D_INFOK("Do configure for AP: %s streams:%d cb:%p",
- ap_obj->path->uid, stream_array.count,
- ap_obj->path->configure);
+ if (!ap_obj->path->configure) {
+ D_ERRORK("Cannot do 'configure' for AP: %s",
+ ap_obj->path->uid);
+ return -EFAULT;
+ }
+
+ D_INFOK("Do 'configure' for AP: %s streams:%d",
+ ap_obj->path->uid, stream_array.count);
ap_obj->path->configure(core.card, &stream_array);
}
- core.streams_sealed = true;
+ core.streams_configured = true;
return err;
}
-bool snd_avirt_streams_sealed(void)
+int snd_avirt_streams_unconfigure(void)
+{
+ struct snd_avirt_audiopath_obj *ap_obj;
+
+ if (!core.streams_configured) {
+ D_ERRORK("streams are already unconfigured!");
+ return -1;
+ }
+
+ list_for_each_entry (ap_obj, &audiopath_list, list) {
+ if (!ap_obj->path->unconfigure) {
+ D_ERRORK("Cannot do 'unconfigure' for AP: %s",
+ ap_obj->path->uid);
+ return -EFAULT;
+ }
+
+ D_INFOK("Do 'unconfigure' for AP: %s", ap_obj->path->uid);
+ ap_obj->path->unconfigure();
+ }
+
+ core.streams_configured = false;
+
+ return 0;
+}
+
+bool snd_avirt_streams_configured(void)
{
- return core.streams_sealed;
+ return core.streams_configured;
}
struct snd_avirt_stream *snd_avirt_stream_find_by_device(unsigned int device)