diff options
author | Mark Farrugia <mark.farrugia@fiberdyne.com.au> | 2019-03-01 17:32:37 +1100 |
---|---|---|
committer | Mark Farrugia <mark.farrugia@fiberdyne.com.au> | 2019-03-01 17:32:37 +1100 |
commit | 8ce739b235362ca810a5e25fef58e7400ba679b4 (patch) | |
tree | cc3e83ec9f9055ce1a13142a2f44ad25aed022ab /configfs.c | |
parent | 763bbd2abc251d351746bfddfbac9d39a74e4492 (diff) |
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 <mark.farrugia@fiberdyne.com.au>
Diffstat (limited to 'configfs.c')
-rw-r--r-- | configfs.c | 254 |
1 files changed, 215 insertions, 39 deletions
@@ -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: |