From d84d13e2e3b6f6d2674fa4793c37eff6736f8577 Mon Sep 17 00:00:00 2001 From: Mark Farrugia Date: Tue, 20 Nov 2018 18:28:11 +1100 Subject: Ensure snd_avirt_private_free is not called if NULL We only want to call the snd_avirt_private_free callback if it is set. Some Audio Paths may only set the private data, and not the private free callback, so we need to accommodate for this. Signed-off-by: Mark Farrugia --- core.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'core.c') diff --git a/core.c b/core.c index 35998c0..a733118 100644 --- a/core.c +++ b/core.c @@ -210,10 +210,10 @@ static void pcm_private_data_free(struct snd_pcm *pcm) { struct snd_avirt_private_data *avirt_private_data; - D_PRINTK("Issuing free to private data struct"); - if (pcm->private_data) { - avirt_private_data = pcm->private_data; - if (avirt_private_data->ap_private_data) + avirt_private_data = (struct snd_avirt_private_data *)pcm->private_data; + if (avirt_private_data) { + if (avirt_private_data->ap_private_data && + avirt_private_data->ap_private_free) avirt_private_data->ap_private_free(pcm); } -- cgit 1.2.3-korg From 6b9718b2ccd5d1ae50f33047a323acd72531fd13 Mon Sep 17 00:00:00 2001 From: Mark Farrugia Date: Wed, 28 Nov 2018 13:51:37 +1100 Subject: Add null check for supplied Audio Path PCM ops This was not being checked, and resulted in kernel panic when null Signed-off-by: Mark Farrugia --- core.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'core.c') diff --git a/core.c b/core.c index a733118..c758391 100644 --- a/core.c +++ b/core.c @@ -242,6 +242,12 @@ static struct snd_pcm *pcm_create(struct snd_avirt_stream *stream) pcm_ops_ap = (struct snd_pcm_ops *)audiopath->pcm_capture_ops; capture = true; } + if (!pcm_ops_ap) { + D_ERRORK("No PCM ops for direction '%s' for Audio Path: %s", + (stream->direction) ? "capture" : "playback", + stream->map); + return ERR_PTR(-EFAULT); + } pcm_ops = &pcm_ops_avirt; /* Set PCM ops for the Audio Path*/ -- cgit 1.2.3-korg From 42000f29ef6775f092dab2c80f8b3a6a319b5658 Mon Sep 17 00:00:00 2001 From: Mark Farrugia Date: Thu, 13 Dec 2018 17:13:39 +1100 Subject: Ensure that PCM callback 'close' is never NULL ALSA requires a 'close' callback. It does not have any checks internally to not call a NULL callback. Signed-off-by: Mark Farrugia --- core.c | 1 - 1 file changed, 1 deletion(-) (limited to 'core.c') diff --git a/core.c b/core.c index c758391..8d73ed1 100644 --- a/core.c +++ b/core.c @@ -251,7 +251,6 @@ static struct snd_pcm *pcm_create(struct snd_avirt_stream *stream) pcm_ops = &pcm_ops_avirt; /* Set PCM ops for the Audio Path*/ - PCM_OPS_SET(pcm_ops_ap, &pcm_ops, close); PCM_OPS_SET(pcm_ops_ap, &pcm_ops, prepare); PCM_OPS_SET(pcm_ops_ap, &pcm_ops, trigger); PCM_OPS_SET(pcm_ops_ap, &pcm_ops, pointer); -- cgit 1.2.3-korg From 4737bc9ffcdd0ec17c8fa7629c15de6d4b30594d Mon Sep 17 00:00:00 2001 From: Mark Farrugia Date: Mon, 17 Dec 2018 12:06:24 +1100 Subject: Make card discoverable at module load We can achieve this using the snd_device_register function Signed-off-by: Mark Farrugia --- core.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) (limited to 'core.c') diff --git a/core.c b/core.c index 8d73ed1..2fd416e 100644 --- a/core.c +++ b/core.c @@ -287,6 +287,8 @@ static struct snd_pcm *pcm_create(struct snd_avirt_stream *stream) // Set the private free function for the private user data pcm->private_free = pcm_private_data_free; + snd_device_register(core.card, pcm); + return pcm; } @@ -458,12 +460,6 @@ int snd_avirt_streams_seal(void) ap_obj->path->configure(core.card, &stream_array); } - err = snd_card_register(core.card); - if (err < 0) { - D_ERRORK("Sound card registration failed!"); - snd_card_free(core.card); - } - core.streams_sealed = true; return err; @@ -573,6 +569,12 @@ static int __init snd_avirt_core_init(void) goto exit_class_container; } + err = snd_card_register(core.card); + if (err < 0) { + D_ERRORK("Sound card registration failed!"); + snd_card_free(core.card); + } + err = snd_avirt_configfs_init(&core); if (err < 0) goto exit_kset; -- cgit 1.2.3-korg From e423837602e5cdc11a0912f4bd27904d4e0c6946 Mon Sep 17 00:00:00 2001 From: Mark Farrugia Date: Fri, 4 Jan 2019 18:22:35 +1100 Subject: Create PCM devices as soon as the AVIRT map is set We no longer need to wait until the card is sealed to create the PCM device. We can create it right away. Signed-off-by: Mark Farrugia --- configfs.c | 5 +++++ core.c | 17 +++++------------ core.h | 2 ++ 3 files changed, 12 insertions(+), 12 deletions(-) (limited to 'core.c') diff --git a/configfs.c b/configfs.c index 12e95a5..c19c2b5 100644 --- a/configfs.c +++ b/configfs.c @@ -49,6 +49,11 @@ static ssize_t cfg_snd_avirt_stream_map_store(struct config_item *item, split = strsep((char **)&page, "\n"); memcpy(stream->map, (char *)split, count); + /* Create the PCM device now */ + stream->pcm = snd_avirt_pcm_create(stream); + if (IS_ERROR_NULL(stream->pcm)) + return 0; + return count; } CONFIGFS_ATTR(cfg_snd_avirt_stream_, map); diff --git a/core.c b/core.c index 2fd416e..e431402 100644 --- a/core.c +++ b/core.c @@ -220,7 +220,7 @@ static void pcm_private_data_free(struct snd_pcm *pcm) kfree(pcm->private_data); } -static struct snd_pcm *pcm_create(struct snd_avirt_stream *stream) +struct snd_pcm *snd_avirt_pcm_create(struct snd_avirt_stream *stream) { struct snd_avirt_private_data *avirt_private_data; struct snd_avirt_audiopath *audiopath; @@ -270,8 +270,11 @@ static struct snd_pcm *pcm_create(struct snd_avirt_stream *stream) err = snd_pcm_new(core.card, stream->name, stream->device, playback, capture, &pcm); - if (err < 0) + if (err < 0) { + D_ERRORK("Failed to create PCM device for stream: '%s'", + stream->name); return ERR_PTR(err); + } /** Register driver callbacks */ if (playback) @@ -441,16 +444,6 @@ int snd_avirt_streams_seal(void) return -1; } - list_for_each(entry, &core.stream_group->cg_children) { - item = container_of(entry, struct config_item, ci_entry); - stream = snd_avirt_stream_from_config_item(item); - if (!stream) - return -EFAULT; - stream->pcm = pcm_create(stream); - if (IS_ERR_OR_NULL(stream->pcm)) - return (PTR_ERR(stream->pcm)); - } - list_for_each_entry(ap_obj, &audiopath_list, list) { D_INFOK("configure() AP uid: %s", ap_obj->path->uid); for (i = 0; i < MAX_STREAMS; i++) diff --git a/core.h b/core.h index 40252e6..5ea78cd 100644 --- a/core.h +++ b/core.h @@ -69,6 +69,8 @@ struct snd_avirt_stream *snd_avirt_stream_find_by_device(unsigned int device); struct snd_avirt_stream *snd_avirt_stream_create(const char *name, int direction); +struct snd_pcm *snd_avirt_pcm_create(struct snd_avirt_stream *stream); + /** * snd_avirt_audiopath_get - retrieves the Audio Path by it's UID * @uid: Unique ID for the Audio Path -- cgit 1.2.3-korg From 2e1b1a2ec043a89d7720e9b9c00d4f783ce6a62b Mon Sep 17 00:00:00 2001 From: Mark Farrugia Date: Tue, 29 Jan 2019 18:39:53 +1100 Subject: Make snd_avirt_stream_get an exported function Clean up logging Signed-off-by: Mark Farrugia --- core.c | 4 +++- sound/avirt.h | 7 +++++++ 2 files changed, 10 insertions(+), 1 deletion(-) (limited to 'core.c') diff --git a/core.c b/core.c index e431402..23351fc 100644 --- a/core.c +++ b/core.c @@ -323,13 +323,15 @@ struct snd_avirt_audiopath *snd_avirt_audiopath_get(const char *uid) struct snd_avirt_audiopath_obj *ap_obj; list_for_each_entry(ap_obj, &audiopath_list, list) { - // pr_info("get_ap %s\n", ap_obj->path->uid); + //D_INFOK("snd_avirt_audiopath_get, map:%s", uid); + if (!strcmp(ap_obj->path->uid, uid)) return ap_obj->path; } return NULL; } +EXPORT_SYMBOL_GPL(snd_avirt_audiopath_get); /** * snd_avirt_audiopath_register - register Audio Path with AVIRT diff --git a/sound/avirt.h b/sound/avirt.h index 0ebac86..90859d5 100644 --- a/sound/avirt.h +++ b/sound/avirt.h @@ -96,6 +96,13 @@ int snd_avirt_audiopath_register(struct snd_avirt_audiopath *audiopath); */ int snd_avirt_audiopath_deregister(struct snd_avirt_audiopath *audiopath); +/** + * 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); + /** * snd_avirt_pcm_period_elapsed - PCM buffer complete callback * @substream: pointer to ALSA PCM substream -- cgit 1.2.3-korg From 5e431d7a48d5e4ca0e2c040af1ef10dfaf0eba92 Mon Sep 17 00:00:00 2001 From: Mark Farrugia Date: Tue, 29 Jan 2019 18:46:23 +1100 Subject: Rework setting the AVIRT map, and stream creation To ensure that a stream is able to change it's mapping (before card is sealed), we cannot create the stream PCM upon setting the mapping. This had to be reworked to allow multiple setting of the mapping if so required. The PCM ops table for a given stream is now allocated and stored in the snd_avirt_stream struct, to allow retrieval and manipulation upon mapping change. Signed-off-by: Mark Farrugia --- configfs.c | 7 +--- core.c | 105 ++++++++++++++++++++++++++++++++++++++-------------------- core.h | 9 +++-- sound/avirt.h | 1 + 4 files changed, 79 insertions(+), 43 deletions(-) (limited to 'core.c') diff --git a/configfs.c b/configfs.c index c19c2b5..a22c75f 100644 --- a/configfs.c +++ b/configfs.c @@ -47,12 +47,7 @@ static ssize_t cfg_snd_avirt_stream_map_store(struct config_item *item, snd_avirt_stream_from_config_item(item); split = strsep((char **)&page, "\n"); - memcpy(stream->map, (char *)split, count); - - /* Create the PCM device now */ - stream->pcm = snd_avirt_pcm_create(stream); - if (IS_ERROR_NULL(stream->pcm)) - return 0; + snd_avirt_stream_set_map(stream, split); return count; } diff --git a/core.c b/core.c index 23351fc..32b7e22 100644 --- a/core.c +++ b/core.c @@ -220,46 +220,17 @@ static void pcm_private_data_free(struct snd_pcm *pcm) kfree(pcm->private_data); } -struct snd_pcm *snd_avirt_pcm_create(struct snd_avirt_stream *stream) +static struct snd_pcm *snd_avirt_pcm_create(struct snd_avirt_stream *stream) { struct snd_avirt_private_data *avirt_private_data; - struct snd_avirt_audiopath *audiopath; - struct snd_pcm_ops *pcm_ops, *pcm_ops_ap; struct snd_pcm *pcm; bool playback = false, capture = false; int err; - audiopath = snd_avirt_audiopath_get(stream->map); - if (!audiopath) { - D_ERRORK("Cannot find Audio Path uid: '%s'!", stream->map); - return ERR_PTR(-EFAULT); - } - - if (!stream->direction) { - pcm_ops_ap = (struct snd_pcm_ops *)audiopath->pcm_playback_ops; + if (!stream->direction) playback = true; - } else { - pcm_ops_ap = (struct snd_pcm_ops *)audiopath->pcm_capture_ops; + else capture = true; - } - if (!pcm_ops_ap) { - D_ERRORK("No PCM ops for direction '%s' for Audio Path: %s", - (stream->direction) ? "capture" : "playback", - stream->map); - return ERR_PTR(-EFAULT); - } - pcm_ops = &pcm_ops_avirt; - - /* Set PCM ops for the Audio Path*/ - PCM_OPS_SET(pcm_ops_ap, &pcm_ops, prepare); - PCM_OPS_SET(pcm_ops_ap, &pcm_ops, trigger); - PCM_OPS_SET(pcm_ops_ap, &pcm_ops, pointer); - PCM_OPS_SET(pcm_ops_ap, &pcm_ops, get_time_info); - PCM_OPS_SET(pcm_ops_ap, &pcm_ops, fill_silence); - PCM_OPS_SET(pcm_ops_ap, &pcm_ops, copy_user); - PCM_OPS_SET(pcm_ops_ap, &pcm_ops, copy_kernel); - PCM_OPS_SET(pcm_ops_ap, &pcm_ops, mmap); - PCM_OPS_SET(pcm_ops_ap, &pcm_ops, ack); /** Special case: loopback */ if (!strcmp(stream->map, "ap_loopback")) { @@ -278,9 +249,10 @@ struct snd_pcm *snd_avirt_pcm_create(struct snd_avirt_stream *stream) /** Register driver callbacks */ if (playback) - snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, pcm_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, + stream->pcm_ops); if (capture) - snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, pcm_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, stream->pcm_ops); pcm->info_flags = 0; strcpy(pcm->name, stream->name); @@ -295,6 +267,60 @@ 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) +{ + struct snd_avirt_audiopath *audiopath; + 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); + return -EPERM; + } + + if (!strcmp(stream->map, map)) + return -1; + + memcpy(stream->map, (char *)map, strlen(map)); + audiopath = snd_avirt_audiopath_get(map); + if (!audiopath) { + D_ERRORK("Cannot find Audio Path uid: '%s'!", stream->map); + return -EFAULT; + } + + if (!stream->direction) + pcm_ops_ap = (struct snd_pcm_ops *)audiopath->pcm_playback_ops; + else + pcm_ops_ap = (struct snd_pcm_ops *)audiopath->pcm_capture_ops; + + if (!pcm_ops_ap) { + D_ERRORK("No PCM ops for direction '%s' for Audio Path: %s", + (stream->direction) ? "capture" : "playback", + stream->map); + return -EFAULT; + } + + /* 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, fill_silence); + PCM_OPS_SET(pcm_ops_ap, &stream->pcm_ops, copy_user); + PCM_OPS_SET(pcm_ops_ap, &stream->pcm_ops, copy_kernel); + PCM_OPS_SET(pcm_ops_ap, &stream->pcm_ops, mmap); + PCM_OPS_SET(pcm_ops_ap, &stream->pcm_ops, ack); + + /* If not created, create the PCM device now */ + if (!stream->pcm) { + stream->pcm = snd_avirt_pcm_create(stream); + if (IS_ERR_OR_NULL(stream->pcm)) + return -EFAULT; + } + + return 0; +} + static int snd_avirt_streams_get(const char *map, struct snd_avirt_stream_array *stream_array) { @@ -427,7 +453,16 @@ struct snd_avirt_stream *snd_avirt_stream_create(const char *name, stream->direction = direction; stream->device = core.stream_count++; - D_INFOK("name: %s device:%d", name, stream->device); + /* Initialize PCM ops table for this stream. + * Will be populated once map is known */ + stream->pcm_ops = kzalloc(sizeof(struct snd_pcm_ops), GFP_KERNEL); + if (!stream->pcm_ops) { + D_ERRORK("Failed to allocate PCM ops table"); + return ERR_PTR(-EFAULT); + } + memcpy(stream->pcm_ops, &pcm_ops_avirt, sizeof(struct snd_pcm_ops)); + + D_INFOK("name:%s device:%d", name, stream->device); return stream; } diff --git a/core.h b/core.h index 5ea78cd..6dc3333 100644 --- a/core.h +++ b/core.h @@ -68,8 +68,13 @@ struct snd_avirt_stream *snd_avirt_stream_find_by_device(unsigned int device); */ struct snd_avirt_stream *snd_avirt_stream_create(const char *name, int direction); - -struct snd_pcm *snd_avirt_pcm_create(struct snd_avirt_stream *stream); +/** + * snd_avirt_stream_set_map - Set Audio Path mapping for a given stream + * @stream: The stream to assign the mapping to. + * @map: The Audio Path UID to map the stream to. + * @return: 0 on success, negative ERRNO on failure + */ +int snd_avirt_stream_set_map(struct snd_avirt_stream *stream, const char *map); /** * snd_avirt_audiopath_get - retrieves the Audio Path by it's UID diff --git a/sound/avirt.h b/sound/avirt.h index 90859d5..92bb07d 100644 --- a/sound/avirt.h +++ b/sound/avirt.h @@ -61,6 +61,7 @@ struct snd_avirt_stream { unsigned int device; /* Stream PCM device no. */ unsigned int direction; /* Stream direction */ struct snd_pcm *pcm; /* ALSA PCM */ + struct snd_pcm_ops *pcm_ops; /* ALSA PCM ops */ struct config_item item; /* configfs item reference */ }; -- cgit 1.2.3-korg From 0e2278875ea5ec6777018cc792adcc34b8463050 Mon Sep 17 00:00:00 2001 From: Mark Farrugia Date: Wed, 30 Jan 2019 15:11:12 +1100 Subject: Fix configure callback logging, tidy unused variables Signed-off-by: Mark Farrugia --- core.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'core.c') diff --git a/core.c b/core.c index 32b7e22..7541ade 100644 --- a/core.c +++ b/core.c @@ -349,7 +349,7 @@ struct snd_avirt_audiopath *snd_avirt_audiopath_get(const char *uid) struct snd_avirt_audiopath_obj *ap_obj; list_for_each_entry(ap_obj, &audiopath_list, list) { - //D_INFOK("snd_avirt_audiopath_get, map:%s", uid); + //D_INFOK("snd_avirt_audiopath_get, map:%s", uid); if (!strcmp(ap_obj->path->uid, uid)) return ap_obj->path; @@ -471,10 +471,7 @@ int snd_avirt_streams_seal(void) { int err = 0, i = 0; struct snd_avirt_audiopath_obj *ap_obj; - struct snd_avirt_stream *stream; struct snd_avirt_stream_array stream_array; - struct config_item *item; - struct list_head *entry; if (core.streams_sealed) { D_ERRORK("streams are already sealed!"); @@ -482,12 +479,15 @@ int snd_avirt_streams_seal(void) } list_for_each_entry(ap_obj, &audiopath_list, list) { - D_INFOK("configure() AP uid: %s", ap_obj->path->uid); for (i = 0; i < MAX_STREAMS; i++) stream_array.streams[i] = NULL; stream_array.count = 0; - if (snd_avirt_streams_get(ap_obj->path->uid, &stream_array) > 0) + if (snd_avirt_streams_get(ap_obj->path->uid, &stream_array) > + 0) { + 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; -- cgit 1.2.3-korg From 443bdab5db1cd5494e94aa7031c4a25e33a9c6bb Mon Sep 17 00:00:00 2001 From: Mark Farrugia Date: Fri, 1 Mar 2019 17:15:22 +1100 Subject: Refactor avirt_private_free Explicitly define callback type, refactor name Signed-off-by: Mark Farrugia --- core.c | 9 +++++---- sound/avirt.h | 23 ++++++++++++++--------- 2 files changed, 19 insertions(+), 13 deletions(-) (limited to 'core.c') diff --git a/core.c b/core.c index 7541ade..c1f6393 100644 --- a/core.c +++ b/core.c @@ -203,13 +203,14 @@ static void destroy_snd_avirt_audiopath_obj(struct snd_avirt_audiopath_obj *p) } /** - * pcm_private_data_free - callback function to free private data allocated to pcm - * @pcm: the PCM with private data + * pcm_private_free - callback function to free private data allocated to pcm + * @pcm: the PCM object */ -static void pcm_private_data_free(struct snd_pcm *pcm) +static void pcm_private_free(struct snd_pcm *pcm) { struct snd_avirt_private_data *avirt_private_data; + /* 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 && @@ -260,7 +261,7 @@ static struct snd_pcm *snd_avirt_pcm_create(struct snd_avirt_stream *stream) avirt_private_data = kzalloc(sizeof(*avirt_private_data), GFP_KERNEL); pcm->private_data = avirt_private_data; // Set the private free function for the private user data - pcm->private_free = pcm_private_data_free; + pcm->private_free = pcm_private_free; snd_device_register(core.card, pcm); diff --git a/sound/avirt.h b/sound/avirt.h index 6f15143..cb9a61e 100644 --- a/sound/avirt.h +++ b/sound/avirt.h @@ -65,6 +65,19 @@ struct snd_avirt_stream { struct config_item item; /* configfs item reference */ }; +/** + * Audio stream group + */ +struct snd_avirt_stream_array { + struct snd_avirt_stream *streams[MAX_STREAMS]; + int count; +}; + +/** + * snd_avirt_private_free - free Audio Path private data from function prototype + * @pcm: The PCM object + */ +typedef void (*snd_avirt_ap_private_free)(struct snd_pcm *pcm); /** * Private Data Expansion */ @@ -72,15 +85,7 @@ struct snd_avirt_private_data { struct snd_avirt_audiopath *audiopath; void *ap_private_data; - void (*ap_private_free)(struct snd_pcm *pcm); -}; - -/** - * Audio stream group - */ -struct snd_avirt_stream_array { - struct snd_avirt_stream *streams[MAX_STREAMS]; - int count; + snd_avirt_ap_private_free ap_private_free; }; /** -- cgit 1.2.3-korg From 763bbd2abc251d351746bfddfbac9d39a74e4492 Mon Sep 17 00:00:00 2001 From: Mark Farrugia Date: Fri, 1 Mar 2019 17:20:29 +1100 Subject: Move sysfs functionality to sysfs.c Signed-off-by: Mark Farrugia --- Makefile | 1 + core.c | 183 ++++----------------------------------------------------------- core.h | 34 ++++++++++++ sysfs.c | 174 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 218 insertions(+), 174 deletions(-) create mode 100644 sysfs.c (limited to 'core.c') diff --git a/Makefile b/Makefile index c654996..377e8ec 100644 --- a/Makefile +++ b/Makefile @@ -3,6 +3,7 @@ obj-$(CONFIG_AVIRT) += snd-avirt-core.o snd-avirt-core-y := core.o snd-avirt-core-y += pcm.o snd-avirt-core-y += configfs.o +snd-avirt-core-y += sysfs.o ifeq ($(CONFIG_AVIRT_BUILDLOCAL),) CCFLAGS_AVIRT := "drivers/staging/" diff --git a/core.c b/core.c index c1f6393..fd74b08 100644 --- a/core.c +++ b/core.c @@ -40,168 +40,6 @@ static struct snd_avirt_core core = { static LIST_HEAD(audiopath_list); -struct snd_avirt_audiopath_obj { - struct kobject kobj; - struct list_head list; - struct snd_avirt_audiopath *path; -}; - -static struct kset *snd_avirt_audiopath_kset; -static struct kobject *kobj; - -#define to_audiopath_obj(d) \ - container_of(d, struct snd_avirt_audiopath_obj, kobj) -#define to_audiopath_attr(a) \ - container_of(a, struct snd_avirt_audiopath_attribute, attr) - -/** - * struct snd_avirt_audiopath_attribute - access the attributes of Audio Path - * @attr: attributes of an Audio Path - * @show: pointer to the show function - * @store: pointer to the store function - */ -struct snd_avirt_audiopath_attribute { - struct attribute attr; - ssize_t (*show)(struct snd_avirt_audiopath_obj *d, - struct snd_avirt_audiopath_attribute *attr, char *buf); - ssize_t (*store)(struct snd_avirt_audiopath_obj *d, - struct snd_avirt_audiopath_attribute *attr, - const char *buf, size_t count); -}; - -/** - * audiopath_attr_show - show function of an Audio Path - * @kobj: pointer to kobject - * @attr: pointer to attribute struct - * @buf: buffer - */ -static ssize_t audiopath_attr_show(struct kobject *kobj, struct attribute *attr, - char *buf) -{ - struct snd_avirt_audiopath_attribute *audiopath_attr; - struct snd_avirt_audiopath_obj *audiopath_obj; - - audiopath_attr = to_audiopath_attr(attr); - audiopath_obj = to_audiopath_obj(kobj); - - if (!audiopath_attr->show) - return -EIO; - - return audiopath_attr->show(audiopath_obj, audiopath_attr, buf); -} - -/** - * audiopath_attr_store - attribute store function of Audio Path object - * @kobj: pointer to kobject - * @attr: pointer to attribute struct - * @buf: buffer - * @len: length of buffer - */ -static ssize_t audiopath_attr_store(struct kobject *kobj, - struct attribute *attr, const char *buf, - size_t len) -{ - struct snd_avirt_audiopath_attribute *audiopath_attr; - struct snd_avirt_audiopath_obj *audiopath_obj; - - audiopath_attr = to_audiopath_attr(attr); - audiopath_obj = to_audiopath_obj(kobj); - - if (!audiopath_attr->store) - return -EIO; - return audiopath_attr->store(audiopath_obj, audiopath_attr, buf, len); -} - -static const struct sysfs_ops snd_avirt_audiopath_sysfs_ops = { - .show = audiopath_attr_show, - .store = audiopath_attr_store, -}; - -/** - * snd_avirt_audiopath_release - Audio Path release function - * @kobj: pointer to Audio Paths's kobject - */ -static void snd_avirt_audiopath_release(struct kobject *kobj) -{ - struct snd_avirt_audiopath_obj *audiopath_obj = to_audiopath_obj(kobj); - - kfree(audiopath_obj); -} - -static ssize_t - audiopath_name_show(struct snd_avirt_audiopath_obj *audiopath_obj, - struct snd_avirt_audiopath_attribute *attr, - char *buf) -{ - return sprintf(buf, "%s\n", audiopath_obj->path->name); -} - -static ssize_t - audiopath_version_show(struct snd_avirt_audiopath_obj *audiopath_obj, - struct snd_avirt_audiopath_attribute *attr, - char *buf) -{ - struct snd_avirt_audiopath *audiopath = audiopath_obj->path; - - return sprintf(buf, "%d.%d.%d\n", audiopath->version[0], - audiopath->version[1], audiopath->version[2]); -} - -static struct snd_avirt_audiopath_attribute snd_avirt_audiopath_attrs[] = { - __ATTR_RO(audiopath_name), - __ATTR_RO(audiopath_version), -}; - -static struct attribute *snd_avirt_audiopath_def_attrs[] = { - &snd_avirt_audiopath_attrs[0].attr, - &snd_avirt_audiopath_attrs[1].attr, - NULL, -}; - -static struct kobj_type snd_avirt_audiopath_ktype = { - .sysfs_ops = &snd_avirt_audiopath_sysfs_ops, - .release = snd_avirt_audiopath_release, - .default_attrs = snd_avirt_audiopath_def_attrs, -}; - -/** - * create_snd_avirt_audiopath_obj - creates an Audio Path object - * @uid: Unique ID of the Audio Path - * - * This creates an Audio Path object and assigns the kset and registers - * it with sysfs. - * @return: Pointer to the Audio Path object or NULL if it failed. - */ -static struct snd_avirt_audiopath_obj * - create_snd_avirt_audiopath_obj(const char *uid) -{ - struct snd_avirt_audiopath_obj *snd_avirt_audiopath; - int retval; - - snd_avirt_audiopath = kzalloc(sizeof(*snd_avirt_audiopath), GFP_KERNEL); - if (!snd_avirt_audiopath) - return NULL; - snd_avirt_audiopath->kobj.kset = snd_avirt_audiopath_kset; - retval = kobject_init_and_add(&snd_avirt_audiopath->kobj, - &snd_avirt_audiopath_ktype, kobj, "%s", - uid); - if (retval) { - kobject_put(&snd_avirt_audiopath->kobj); - return NULL; - } - kobject_uevent(&snd_avirt_audiopath->kobj, KOBJ_ADD); - return snd_avirt_audiopath; -} - -/** - * destroy_snd_avirt_audiopath_obj - destroys an Audio Path object - * @name: the Audio Path object - */ -static void destroy_snd_avirt_audiopath_obj(struct snd_avirt_audiopath_obj *p) -{ - kobject_put(&p->kobj); -} - /** * pcm_private_free - callback function to free private data allocated to pcm * @pcm: the PCM object @@ -375,7 +213,7 @@ int snd_avirt_audiopath_register(struct snd_avirt_audiopath *audiopath) return -EINVAL; } - audiopath_obj = create_snd_avirt_audiopath_obj(audiopath->uid); + audiopath_obj = snd_avirt_audiopath_create_obj(audiopath->uid); if (!audiopath_obj) { D_INFOK("Failed to alloc driver object"); return -ENOMEM; @@ -420,7 +258,7 @@ int snd_avirt_audiopath_deregister(struct snd_avirt_audiopath *audiopath) } list_del(&audiopath_obj->list); - destroy_snd_avirt_audiopath_obj(audiopath_obj); + snd_avirt_audiopath_destroy_obj(audiopath_obj); D_INFOK("Deregistered Audio Path %s", audiopath->uid); return 0; @@ -587,18 +425,15 @@ static int __init snd_avirt_core_init(void) return PTR_ERR(core.class); } - core.dev = device_create(core.class, NULL, 0, NULL, "avirtcore"); + core.dev = device_create(core.class, NULL, 0, NULL, "core"); if (IS_ERR(core.dev)) { err = PTR_ERR(core.dev); goto exit_class; } - snd_avirt_audiopath_kset = - kset_create_and_add("audiopaths", NULL, &core.dev->kobj); - if (!snd_avirt_audiopath_kset) { - err = -ENOMEM; + err = snd_avirt_sysfs_init(&core); + if (err < 0) goto exit_class_container; - } err = snd_card_register(core.card); if (err < 0) { @@ -608,12 +443,12 @@ static int __init snd_avirt_core_init(void) err = snd_avirt_configfs_init(&core); if (err < 0) - goto exit_kset; + goto exit_sysfs; return 0; -exit_kset: - kset_unregister(snd_avirt_audiopath_kset); +exit_sysfs: + snd_avirt_sysfs_exit(&core); exit_class_container: device_destroy(core.class, 0); exit_class: @@ -631,7 +466,7 @@ exit_platform_device: static void __exit snd_avirt_core_exit(void) { snd_avirt_configfs_exit(&core); - kset_unregister(snd_avirt_audiopath_kset); + snd_avirt_sysfs_exit(&core); device_destroy(core.class, 0); class_destroy(core.class); platform_device_unregister(core.plat_dev); diff --git a/core.h b/core.h index 6dc3333..e83631f 100644 --- a/core.h +++ b/core.h @@ -27,6 +27,12 @@ struct snd_avirt_core { bool streams_sealed; }; +struct snd_avirt_audiopath_obj { + struct kobject kobj; + struct list_head list; + struct snd_avirt_audiopath *path; +}; + /** * snd_avirt_configfs_init - Initialise the configfs system * @core: The snd_avirt_core pointer @@ -40,6 +46,34 @@ int __init snd_avirt_configfs_init(struct snd_avirt_core *core); */ void __exit snd_avirt_configfs_exit(struct snd_avirt_core *core); +/** + * snd_avirt_sysfs_init - Initialise the sysfs system + * @core: The snd_avirt_core pointer + * @return: 0 on success, negative ERRNO on failure + */ +int __init snd_avirt_sysfs_init(struct snd_avirt_core *core); + +/** + * snd_avirt_sysfs_exit - Clean up and exit the sysfs system + * @core: The snd_avirt_core pointer + */ +void __exit snd_avirt_sysfs_exit(struct snd_avirt_core *core); + +/** + * snd_avirt_audiopath_obj - creates an Audio Path object, assigns the kset + * and registers it with sysfs. + * @uid: Unique ID of the Audio Path + * @return: Pointer to the Audio Path object or NULL if it failed. + */ +struct snd_avirt_audiopath_obj *snd_avirt_audiopath_create_obj(const char *uid); + +/** + * snd_avirt_audiopath_destroy_obj - destroys an Audio Path object, deregisters + * it with sysfs + * @name: the Audio Path object + */ +void snd_avirt_audiopath_destroy_obj(struct snd_avirt_audiopath_obj *p); + /** * snd_avirt_streams_seal - Register the sound card to user space * @return: 0 on success, negative ERRNO on failure diff --git a/sysfs.c b/sysfs.c new file mode 100644 index 0000000..b7d28f5 --- /dev/null +++ b/sysfs.c @@ -0,0 +1,174 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * AVIRT - ALSA Virtual Soundcard + * + * Copyright (c) 2010-2019 Fiberdyne Systems Pty Ltd + * + * configfs.c - AVIRT sysfs support + */ + +#include + +#include "core.h" + +#define D_LOGNAME "sysfs" + +#define D_INFOK(fmt, args...) DINFO(D_LOGNAME, fmt, ##args) +#define D_PRINTK(fmt, args...) DDEBUG(D_LOGNAME, fmt, ##args) +#define D_ERRORK(fmt, args...) DERROR(D_LOGNAME, fmt, ##args) + +static struct kset *snd_avirt_audiopath_kset; +static struct kobject *kobj; + +#define to_audiopath_obj(d) \ + container_of(d, struct snd_avirt_audiopath_obj, kobj) +#define to_audiopath_attr(a) \ + container_of(a, struct snd_avirt_audiopath_attribute, attr) + +/** + * struct snd_avirt_audiopath_attribute - access the attributes of Audio Path + * @attr: attributes of an Audio Path + * @show: pointer to the show function + * @store: pointer to the store function + */ +struct snd_avirt_audiopath_attribute { + struct attribute attr; + ssize_t (*show)(struct snd_avirt_audiopath_obj *d, + struct snd_avirt_audiopath_attribute *attr, char *buf); + ssize_t (*store)(struct snd_avirt_audiopath_obj *d, + struct snd_avirt_audiopath_attribute *attr, + const char *buf, size_t count); +}; + +/** + * audiopath_attr_show - show function of an Audio Path + * @kobj: pointer to kobject + * @attr: pointer to attribute struct + * @buf: buffer + */ +static ssize_t audiopath_attr_show(struct kobject *kobj, struct attribute *attr, + char *buf) +{ + struct snd_avirt_audiopath_attribute *audiopath_attr; + struct snd_avirt_audiopath_obj *audiopath_obj; + + audiopath_attr = to_audiopath_attr(attr); + audiopath_obj = to_audiopath_obj(kobj); + + if (!audiopath_attr->show) + return -EIO; + + return audiopath_attr->show(audiopath_obj, audiopath_attr, buf); +} + +/** + * audiopath_attr_store - attribute store function of Audio Path object + * @kobj: pointer to kobject + * @attr: pointer to attribute struct + * @buf: buffer + * @len: length of buffer + */ +static ssize_t audiopath_attr_store(struct kobject *kobj, + struct attribute *attr, const char *buf, + size_t len) +{ + struct snd_avirt_audiopath_attribute *audiopath_attr; + struct snd_avirt_audiopath_obj *audiopath_obj; + + audiopath_attr = to_audiopath_attr(attr); + audiopath_obj = to_audiopath_obj(kobj); + + if (!audiopath_attr->store) + return -EIO; + return audiopath_attr->store(audiopath_obj, audiopath_attr, buf, len); +} + +static const struct sysfs_ops snd_avirt_audiopath_sysfs_ops = { + .show = audiopath_attr_show, + .store = audiopath_attr_store, +}; + +/** + * snd_avirt_audiopath_release - Audio Path release function + * @kobj: pointer to Audio Paths's kobject + */ +static void snd_avirt_audiopath_release(struct kobject *kobj) +{ + struct snd_avirt_audiopath_obj *audiopath_obj = to_audiopath_obj(kobj); + + kfree(audiopath_obj); +} + +static ssize_t name_show(struct snd_avirt_audiopath_obj *audiopath_obj, + struct snd_avirt_audiopath_attribute *attr, char *buf) +{ + return sprintf(buf, "%s\n", audiopath_obj->path->name); +} + +static ssize_t version_show(struct snd_avirt_audiopath_obj *audiopath_obj, + struct snd_avirt_audiopath_attribute *attr, + char *buf) +{ + struct snd_avirt_audiopath *audiopath = audiopath_obj->path; + + return sprintf(buf, "%d.%d.%d\n", audiopath->version[0], + audiopath->version[1], audiopath->version[2]); +} + + +static struct snd_avirt_audiopath_attribute snd_avirt_audiopath_attrs[] = { + __ATTR_RO(name), + __ATTR_RO(version), +}; + +static struct attribute *snd_avirt_audiopath_def_attrs[] = { + &snd_avirt_audiopath_attrs[0].attr, + &snd_avirt_audiopath_attrs[1].attr, + NULL, +}; + +static struct kobj_type snd_avirt_audiopath_ktype = { + .sysfs_ops = &snd_avirt_audiopath_sysfs_ops, + .release = snd_avirt_audiopath_release, + .default_attrs = snd_avirt_audiopath_def_attrs, +}; + +struct snd_avirt_audiopath_obj *snd_avirt_audiopath_create_obj(const char *uid) +{ + struct snd_avirt_audiopath_obj *snd_avirt_audiopath; + int retval; + + snd_avirt_audiopath = kzalloc(sizeof(*snd_avirt_audiopath), GFP_KERNEL); + if (!snd_avirt_audiopath) + return NULL; + snd_avirt_audiopath->kobj.kset = snd_avirt_audiopath_kset; + retval = kobject_init_and_add(&snd_avirt_audiopath->kobj, + &snd_avirt_audiopath_ktype, kobj, "%s", + uid); + if (retval) { + kobject_put(&snd_avirt_audiopath->kobj); + return NULL; + } + kobject_uevent(&snd_avirt_audiopath->kobj, KOBJ_ADD); + return snd_avirt_audiopath; +} + +void snd_avirt_audiopath_destroy_obj(struct snd_avirt_audiopath_obj *p) +{ + kobject_put(&p->kobj); +} + +int __init snd_avirt_sysfs_init(struct snd_avirt_core *core) +{ + snd_avirt_audiopath_kset = + kset_create_and_add("audiopaths", NULL, &core->dev->kobj); + if (!snd_avirt_audiopath_kset) + return -ENOMEM; + + return 0; +} + +void __exit snd_avirt_sysfs_exit(struct snd_avirt_core *core) +{ + kset_unregister(snd_avirt_audiopath_kset); +} -- cgit 1.2.3-korg From 8ce739b235362ca810a5e25fef58e7400ba679b4 Mon Sep 17 00:00:00 2001 From: Mark Farrugia Date: Fri, 1 Mar 2019 17:32:37 +1100 Subject: Add ability to route audio between audio paths Configfs interface for adding 'routes' is added, and allows two audio paths to chain together particular inputs/outputs, allowing for multistage audio driver handling. A particular use-case is that of an ADSP driver. We can feed in audio to it, and capture the processed, and/or mixed, audio at it's output, and direct to another audio path driver, such as the UNICENS audio driver. Signed-off-by: Mark Farrugia --- configfs.c | 254 +++++++++++++++++++++++++++++++++++++++++++++++++--------- core.c | 22 +++++ core.h | 21 +++++ sound/avirt.h | 21 ++++- sysfs.c | 27 +++++++ 5 files changed, 305 insertions(+), 40 deletions(-) (limited to 'core.c') diff --git a/configfs.c b/configfs.c index 4792f87..58e523f 100644 --- a/configfs.c +++ b/configfs.c @@ -17,18 +17,56 @@ #define D_PRINTK(fmt, args...) DDEBUG(D_LOGNAME, fmt, ##args) #define D_ERRORK(fmt, args...) DERROR(D_LOGNAME, fmt, ##args) -static ssize_t cfg_snd_avirt_stream_direction_show(struct config_item *item, - char *page) -{ - ssize_t count; - struct snd_avirt_stream *stream = - snd_avirt_stream_from_config_item(item); - - count = sprintf(page, "%d\n", stream->direction); - - return count; -} -CONFIGFS_ATTR_RO(cfg_snd_avirt_stream_, direction); +/** + * Defines the configfs direction 'show' callback for a stream or route + */ +#define CFG_SND_AVIRT_DIRECTION_RO(type) \ + static ssize_t cfg_snd_avirt_##type##_direction_show( \ + struct config_item *item, char *page) \ + { \ + ssize_t count; \ + struct snd_avirt_##type *s = \ + snd_avirt_##type##_from_config_item(item); \ + count = sprintf(page, "%d\n", s->direction); \ + return count; \ + } \ + CONFIGFS_ATTR_RO(cfg_snd_avirt_##type##_, direction) + +/** + * Defines the configfs channels 'show'/'store' callbacks for a stream or route + */ +#define CFG_SND_AVIRT_CHANNELS(type) \ + static ssize_t cfg_snd_avirt_##type##_channels_show( \ + struct config_item *item, char *page) \ + { \ + ssize_t count; \ + struct snd_avirt_##type *s = \ + snd_avirt_##type##_from_config_item(item); \ + count = sprintf(page, "%d\n", s->channels); \ + return count; \ + } \ + static ssize_t cfg_snd_avirt_##type##_channels_store( \ + struct config_item *item, const char *page, size_t count) \ + { \ + int err; \ + struct snd_avirt_##type *s = \ + snd_avirt_##type##_from_config_item(item); \ + unsigned long tmp; \ + char *p = (char *)page; \ + err = kstrtoul(p, 10, &tmp); \ + if (err < 0) \ + return err; \ + if ((tmp > INT_MAX) || (tmp == 0)) \ + return -ERANGE; \ + s->channels = tmp; \ + return count; \ + } \ + CONFIGFS_ATTR(cfg_snd_avirt_##type##_, channels); + +CFG_SND_AVIRT_DIRECTION_RO(stream); +CFG_SND_AVIRT_CHANNELS(stream); +CFG_SND_AVIRT_DIRECTION_RO(route); +CFG_SND_AVIRT_CHANNELS(route); static ssize_t cfg_snd_avirt_stream_map_show(struct config_item *item, char *page) @@ -53,64 +91,141 @@ static ssize_t cfg_snd_avirt_stream_map_store(struct config_item *item, } CONFIGFS_ATTR(cfg_snd_avirt_stream_, map); -static ssize_t cfg_snd_avirt_stream_channels_show(struct config_item *item, - char *page) +static struct configfs_attribute *cfg_snd_avirt_stream_attrs[] = { + &cfg_snd_avirt_stream_attr_channels, + &cfg_snd_avirt_stream_attr_map, + &cfg_snd_avirt_stream_attr_direction, + NULL, +}; + +static ssize_t cfg_snd_avirt_route_to_ap_show(struct config_item *item, + char *page) { - ssize_t count; - struct snd_avirt_stream *stream = - snd_avirt_stream_from_config_item(item); + struct snd_avirt_route *route = snd_avirt_route_from_config_item(item); + + if (route) + if (route->to_ap) + return sprintf(page, "%s\n", route->to_ap->uid); + + return sprintf(page, "\n"); +} + +static ssize_t cfg_snd_avirt_route_to_ap_store(struct config_item *item, + const char *page, size_t count) +{ + char *uid_ap; + struct snd_avirt_route *route = snd_avirt_route_from_config_item(item); + struct snd_avirt_audiopath *to_ap; + + uid_ap = strsep((char **)&page, "\n"); + to_ap = snd_avirt_audiopath_get(uid_ap); + if (!to_ap) { + D_ERRORK("Audio Path '%s' does not exist!", uid_ap); + D_ERRORK("Cannot set 'route'->'to_ap'"); + return count; + } - count = sprintf(page, "%d\n", stream->channels); + route->to_ap = to_ap; + if (route->from_ap) { + route->from_ap->route_to_ap = to_ap; + route->to_ap->route_from_ap = route->from_ap; + } return count; } +CONFIGFS_ATTR(cfg_snd_avirt_route_, to_ap); -static ssize_t cfg_snd_avirt_stream_channels_store(struct config_item *item, - const char *page, - size_t count) +static ssize_t cfg_snd_avirt_route_from_ap_show(struct config_item *item, + char *page) { - int err; - struct snd_avirt_stream *stream = - snd_avirt_stream_from_config_item(item); - unsigned long tmp; - char *p = (char *)page; + struct snd_avirt_route *route = snd_avirt_route_from_config_item(item); - err = kstrtoul(p, 10, &tmp); - if (err < 0) - return err; + if (route) + if (route->from_ap) + return sprintf(page, "%s\n", route->from_ap->uid); - if ((tmp > INT_MAX) || (tmp == 0)) - return -ERANGE; + return sprintf(page, "\n"); +} - stream->channels = tmp; +static ssize_t cfg_snd_avirt_route_from_ap_store(struct config_item *item, + const char *page, size_t count) +{ + char *uid_ap; + struct snd_avirt_route *route = snd_avirt_route_from_config_item(item); + struct snd_avirt_audiopath *from_ap; + + uid_ap = strsep((char **)&page, "\n"); + from_ap = snd_avirt_audiopath_get(uid_ap); + if (!from_ap) { + D_ERRORK("Audio Path '%s' does not exist!", uid_ap); + D_ERRORK("Cannot set 'route'->'from_ap'"); + return count; + } + + route->from_ap = from_ap; + if (route->to_ap) { + route->to_ap->route_from_ap = from_ap; + route->from_ap->route_to_ap = route->to_ap; + } return count; } -CONFIGFS_ATTR(cfg_snd_avirt_stream_, channels); +CONFIGFS_ATTR(cfg_snd_avirt_route_, from_ap); -static struct configfs_attribute *cfg_snd_avirt_stream_attrs[] = { - &cfg_snd_avirt_stream_attr_channels, - &cfg_snd_avirt_stream_attr_map, - &cfg_snd_avirt_stream_attr_direction, +static struct configfs_attribute *cfg_snd_avirt_route_attrs[] = { + &cfg_snd_avirt_route_attr_channels, + &cfg_snd_avirt_route_attr_direction, + &cfg_snd_avirt_route_attr_from_ap, + &cfg_snd_avirt_route_attr_to_ap, NULL, }; static void cfg_snd_avirt_stream_release(struct config_item *item) { - D_INFOK("item->name:%s", item->ci_namebuf); - kfree(snd_avirt_stream_from_config_item(item)); + struct snd_avirt_stream *stream = + snd_avirt_stream_from_config_item(item); + if (!stream) { + D_ERRORK("Cannot release stream!"); + return; + } + + D_INFOK("Release stream: %s", stream->name); + kfree(stream->pcm_ops); + kfree(stream); +} + +static void cfg_snd_avirt_route_release(struct config_item *item) +{ + struct snd_avirt_route *route = snd_avirt_route_from_config_item(item); + if (!route) { + D_ERRORK("Cannot release route!"); + return; + } + + D_INFOK("Release route: %s", route->name); + kfree(route); } static struct configfs_item_operations cfg_snd_avirt_stream_ops = { .release = cfg_snd_avirt_stream_release, }; +static struct configfs_item_operations cfg_snd_avirt_route_ops = { + .release = cfg_snd_avirt_route_release, +}; + static struct config_item_type cfg_snd_avirt_stream_type = { .ct_item_ops = &cfg_snd_avirt_stream_ops, .ct_attrs = cfg_snd_avirt_stream_attrs, .ct_owner = THIS_MODULE, }; +static struct config_item_type cfg_snd_avirt_route_type = { + .ct_item_ops = &cfg_snd_avirt_route_ops, + .ct_attrs = cfg_snd_avirt_route_attrs, + .ct_owner = THIS_MODULE, +}; + static struct config_item * cfg_snd_avirt_stream_make_item(struct config_group *group, const char *name) { @@ -144,9 +259,49 @@ cfg_snd_avirt_stream_make_item(struct config_group *group, const char *name) config_item_init_type_name(&stream->item, name, &cfg_snd_avirt_stream_type); + D_INFOK("Make stream: %s", stream->name); + return &stream->item; } +static struct config_item * +cfg_snd_avirt_route_make_item(struct config_group *group, const char *name) +{ + char *split; + int direction; + struct snd_avirt_route *route; + + // Get prefix (playback_ or capture_) + split = strsep((char **)&name, "_"); + if (!split) { + D_ERRORK("Route name: '%s' invalid!", split); + D_ERRORK("Must begin with playback_ * or capture_ *"); + return ERR_PTR(-EINVAL); + } + if (!strcmp(split, "playback")) { + direction = SNDRV_PCM_STREAM_PLAYBACK; + } else if (!strcmp(split, "capture")) { + direction = SNDRV_PCM_STREAM_CAPTURE; + } else { + D_ERRORK("Route name: '%s' invalid!", split); + D_ERRORK("Must begin with playback_ * or capture_ "); + return ERR_PTR(-EINVAL); + } + + // Get route name, and create route + split = strsep((char **)&name, "\n"); + route = snd_avirt_route_create(split, direction); + if (IS_ERR(route)) + return ERR_PTR(PTR_ERR(route)); + + config_item_init_type_name(&route->item, name, + &cfg_snd_avirt_route_type); + + D_INFOK("Make route: %s", route->name); + + return &route->item; +} + static ssize_t cfg_snd_avirt_stream_group_sealed_show(struct config_item *item, char *page) { @@ -182,12 +337,22 @@ static struct configfs_group_operations cfg_snd_avirt_stream_group_ops = { .make_item = cfg_snd_avirt_stream_make_item }; +static struct configfs_group_operations cfg_snd_avirt_route_group_ops = { + .make_item = cfg_snd_avirt_route_make_item +}; + static struct config_item_type cfg_stream_group_type = { .ct_group_ops = &cfg_snd_avirt_stream_group_ops, .ct_attrs = cfg_snd_avirt_stream_group_attrs, .ct_owner = THIS_MODULE }; +static struct config_item_type cfg_route_group_type = { + .ct_group_ops = &cfg_snd_avirt_route_group_ops, + .ct_attrs = NULL, + .ct_owner = THIS_MODULE +}; + static struct config_item_type cfg_avirt_group_type = { .ct_owner = THIS_MODULE, }; @@ -212,6 +377,8 @@ int __init snd_avirt_configfs_init(struct snd_avirt_core *core) D_ERRORK("Cannot register configfs subsys!"); return err; } + + /* Create streams default group */ core->stream_group = configfs_register_default_group( &cfg_subsys.su_group, "streams", &cfg_stream_group_type); if (IS_ERR(core->stream_group)) { @@ -220,6 +387,15 @@ int __init snd_avirt_configfs_init(struct snd_avirt_core *core) goto exit_configfs; } + /* Create routes default group */ + core->route_group = configfs_register_default_group( + &cfg_subsys.su_group, "routes", &cfg_route_group_type); + if (IS_ERR(core->route_group)) { + err = PTR_ERR(core->route_group); + D_ERRORK("Cannot register configfs default group 'routes'!"); + goto exit_configfs; + } + return 0; exit_configfs: diff --git a/core.c b/core.c index fd74b08..e1b056e 100644 --- a/core.c +++ b/core.c @@ -265,6 +265,28 @@ int snd_avirt_audiopath_deregister(struct snd_avirt_audiopath *audiopath) } EXPORT_SYMBOL_GPL(snd_avirt_audiopath_deregister); +/** + * snd_avirt_route_create - Create audio route + * @name: The name 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 *route; + + route = kzalloc(sizeof(*route), GFP_KERNEL); + if (!route) + return ERR_PTR(-ENOMEM); + + strcpy(route->name, name); + route->channels = 0; + route->direction = direction; + + return route; +} + /** * snd_avirt_stream_create - Create audio stream, including it's ALSA PCM device * @name: The name designated to the audio stream diff --git a/core.h b/core.h index e83631f..e4fb7a4 100644 --- a/core.h +++ b/core.h @@ -23,6 +23,7 @@ struct snd_avirt_core { struct class *class; struct platform_device *plat_dev; struct config_group *stream_group; + struct config_group *route_group; unsigned int stream_count; bool streams_sealed; }; @@ -117,6 +118,15 @@ int snd_avirt_stream_set_map(struct snd_avirt_stream *stream, const char *map); */ struct snd_avirt_audiopath *snd_avirt_audiopath_get(const char *uid); +/** + * snd_avirt_route_create - Create audio route + * @name: The name 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); + /** * snd_avirt_stream_from_config_item - Convert config_item to a snd_avirt_stream * @item: The config_item to convert from @@ -128,4 +138,15 @@ snd_avirt_stream_from_config_item(struct config_item *item) return item ? container_of(item, struct snd_avirt_stream, item) : NULL; } +/** + * snd_avirt_route_from_config_item - Convert config_item to a snd_avirt_route + * @item: The config_item to convert from + * @return: The item's snd_avirt_stream if successful, NULL otherwise + */ +static inline struct snd_avirt_route * +snd_avirt_route_from_config_item(struct config_item *item) +{ + return item ? container_of(item, struct snd_avirt_route, item) : NULL; +} + #endif /* __SOUND_AVIRT_CORE_H */ diff --git a/sound/avirt.h b/sound/avirt.h index cb9a61e..138d0ca 100644 --- a/sound/avirt.h +++ b/sound/avirt.h @@ -36,9 +36,12 @@ struct snd_avirt_stream_array; /* Forward declaration */ typedef int (*snd_avirt_audiopath_configure)( struct snd_card *card, struct snd_avirt_stream_array *stream_array); +typedef void (*snd_avirt_pcm_exttrigger)(void); + /** * 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 */ @@ -47,8 +50,24 @@ struct snd_avirt_audiopath { const struct snd_pcm_ops *pcm_playback_ops; /* ALSA PCM playback ops */ const struct snd_pcm_ops *pcm_capture_ops; /* ALSA PCM capture ops */ snd_avirt_audiopath_configure configure; /* Config callback function */ - + snd_avirt_pcm_exttrigger pcm_exttrigger; void *context; + + // MUST be at the end + struct snd_avirt_audiopath *route_from_ap; + struct snd_avirt_audiopath *route_to_ap; +}; + +/** + * Audio routing + */ +struct snd_avirt_route { + char name[MAX_NAME_LEN]; + unsigned int channels; + unsigned int direction; + struct snd_avirt_audiopath *from_ap; + struct snd_avirt_audiopath *to_ap; + struct config_item item; }; /** diff --git a/sysfs.c b/sysfs.c index b7d28f5..29f9b5d 100644 --- a/sysfs.c +++ b/sysfs.c @@ -115,15 +115,42 @@ static ssize_t version_show(struct snd_avirt_audiopath_obj *audiopath_obj, audiopath->version[1], audiopath->version[2]); } +static ssize_t route_from_ap_show(struct snd_avirt_audiopath_obj *audiopath_obj, + struct snd_avirt_audiopath_attribute *attr, + char *buf) +{ + struct snd_avirt_audiopath *audiopath = audiopath_obj->path; + + if (audiopath->route_from_ap) + return sprintf(buf, "%s\n", audiopath->route_from_ap->uid); + + return 0; +} + +static ssize_t route_to_ap_show(struct snd_avirt_audiopath_obj *audiopath_obj, + struct snd_avirt_audiopath_attribute *attr, + char *buf) +{ + struct snd_avirt_audiopath *audiopath = audiopath_obj->path; + + if (audiopath->route_to_ap) + return sprintf(buf, "%s\n", audiopath->route_to_ap->uid); + + return 0; +} static struct snd_avirt_audiopath_attribute snd_avirt_audiopath_attrs[] = { __ATTR_RO(name), __ATTR_RO(version), + __ATTR_RO(route_from_ap), + __ATTR_RO(route_to_ap), }; static struct attribute *snd_avirt_audiopath_def_attrs[] = { &snd_avirt_audiopath_attrs[0].attr, &snd_avirt_audiopath_attrs[1].attr, + &snd_avirt_audiopath_attrs[2].attr, + &snd_avirt_audiopath_attrs[3].attr, NULL, }; -- cgit 1.2.3-korg From 9f68614f664506535c873699dcfbca6e2515a24c Mon Sep 17 00:00:00 2001 From: Mark Farrugia Date: Fri, 1 Mar 2019 17:34:08 +1100 Subject: Restructure streams_seal algorithm, formatting fixes. Signed-off-by: Mark Farrugia --- .vscode/settings.json | 13 +++++++------ configfs.c | 6 ++++-- core.c | 22 +++++++++++----------- 3 files changed, 22 insertions(+), 19 deletions(-) (limited to 'core.c') diff --git a/.vscode/settings.json b/.vscode/settings.json index 4dc08b0..0839b43 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,8 +1,9 @@ { - "editor.tabSize": 8, - "editor.insertSpaces": false, - "editor.rulers": [ - 80 - ], - "editor.formatOnSave": true, + "editor.tabSize": 8, + "editor.insertSpaces": false, + "editor.rulers": [ + 80 + ], + "editor.formatOnSave": true, + "editor.trimAutoWhitespace": true } \ No newline at end of file diff --git a/configfs.c b/configfs.c index 58e523f..5668cb3 100644 --- a/configfs.c +++ b/configfs.c @@ -358,8 +358,10 @@ static struct config_item_type cfg_avirt_group_type = { }; static struct configfs_subsystem cfg_subsys = { - .su_group = { - .cg_item = { + .su_group = + { + .cg_item = + { .ci_namebuf = "snd-avirt", .ci_type = &cfg_avirt_group_type, }, diff --git a/core.c b/core.c index e1b056e..a618bfe 100644 --- a/core.c +++ b/core.c @@ -187,9 +187,7 @@ struct snd_avirt_audiopath *snd_avirt_audiopath_get(const char *uid) { struct snd_avirt_audiopath_obj *ap_obj; - list_for_each_entry(ap_obj, &audiopath_list, list) { - //D_INFOK("snd_avirt_audiopath_get, map:%s", uid); - + list_for_each_entry (ap_obj, &audiopath_list, list) { if (!strcmp(ap_obj->path->uid, uid)) return ap_obj->path; } @@ -339,16 +337,18 @@ int snd_avirt_streams_seal(void) return -1; } - list_for_each_entry(ap_obj, &audiopath_list, list) { + list_for_each_entry (ap_obj, &audiopath_list, list) { for (i = 0; i < MAX_STREAMS; i++) stream_array.streams[i] = NULL; stream_array.count = 0; - if (snd_avirt_streams_get(ap_obj->path->uid, &stream_array) > - 0) { - D_INFOK("Do configure for AP: %s streams:%d", - ap_obj->path->uid, stream_array.count); - ap_obj->path->configure(core.card, &stream_array); - } + if (snd_avirt_streams_get(ap_obj->path->uid, &stream_array) <= + 0) + continue; + + D_INFOK("Do configure for AP: %s streams:%d cb:%p", + ap_obj->path->uid, stream_array.count, + ap_obj->path->configure); + ap_obj->path->configure(core.card, &stream_array); } core.streams_sealed = true; @@ -372,7 +372,7 @@ struct snd_avirt_stream *snd_avirt_stream_find_by_device(unsigned int device) return ERR_PTR(-EINVAL); } - list_for_each(entry, &core.stream_group->cg_children) { + list_for_each (entry, &core.stream_group->cg_children) { item = container_of(entry, struct config_item, ci_entry); stream = snd_avirt_stream_from_config_item(item); if (!stream) -- cgit 1.2.3-korg