diff options
Diffstat (limited to 'configfs.c')
-rw-r--r-- | configfs.c | 224 |
1 files changed, 163 insertions, 61 deletions
@@ -59,6 +59,7 @@ if ((tmp > INT_MAX) || (tmp == 0)) \ return -ERANGE; \ s->channels = tmp; \ + snd_avirt_##type##_try_complete(s); \ return count; \ } \ CONFIGFS_ATTR(cfg_snd_avirt_##type##_, channels); @@ -68,6 +69,66 @@ CFG_SND_AVIRT_CHANNELS(stream); CFG_SND_AVIRT_DIRECTION_RO(route); CFG_SND_AVIRT_CHANNELS(route); +/* + * Check PCM hw params between a source and a sink + */ +#define CHK_ROUTE_ERR(source_hw, sink_hw, field) \ + do { \ + if (source_hw->field != sink_hw->field) { \ + D_ERRORK( \ + "Route HW mismatch: ##field (src:%d, sink:%d)", \ + source_hw->field, sink_hw->field); \ + return -1; \ + } \ + } while (0) + +/* + * Check the a route's source and sink Audio Path's hardware params, to ensure + * compatibility + */ +static int cfg_snd_avirt_route_verify_hw(struct snd_avirt_audiopath *source_ap, + struct snd_avirt_audiopath *sink_ap) +{ + const struct snd_pcm_hardware *source_hw = source_ap->hw; + const struct snd_pcm_hardware *sink_hw = sink_ap->hw; + + CHK_ROUTE_ERR(source_hw, sink_hw, channels_max); + CHK_ROUTE_ERR(source_hw, sink_hw, channels_min); + CHK_ROUTE_ERR(source_hw, sink_hw, rate_max); + CHK_ROUTE_ERR(source_hw, sink_hw, rate_min); + CHK_ROUTE_ERR(source_hw, sink_hw, rates); + + return 0; +} + +/* + * Store the Audio Path endpoint (source or sink), and check compatiblity + */ +static int cfg_snd_avirt_route_ap_store(struct snd_avirt_route *route, + struct snd_avirt_audiopath *ap, + snd_avirt_route_endpoint endpoint) +{ + /* If other endpoint is set, we want to check that the two Audio Path + * endpoints are compatible before we set this endpoint */ + if (route->endpoint_ap[!endpoint]) { + if (!cfg_snd_avirt_route_verify_hw( + route->endpoint_ap[!endpoint], ap)) { + route->endpoint_ap[endpoint] = ap; + D_INFOK("Route successfully created: '%s' [%s -> %s]", + route->uid, ap->uid, + route->endpoint_ap[!endpoint]->uid); + return 0; + } else { + D_ERRORK("Route could not be created: %s", route->uid); + return -1; + } + } else { + route->endpoint_ap[endpoint] = ap; + } + + return 0; +} + static ssize_t cfg_snd_avirt_stream_map_show(struct config_item *item, char *page) { @@ -80,12 +141,31 @@ static ssize_t cfg_snd_avirt_stream_map_show(struct config_item *item, static ssize_t cfg_snd_avirt_stream_map_store(struct config_item *item, const char *page, size_t count) { - char *split; + char *map; + struct snd_avirt_audiopath *audiopath; struct snd_avirt_stream *stream = snd_avirt_stream_from_config_item(item); - split = strsep((char **)&page, "\n"); - snd_avirt_stream_set_map(stream, split); + map = strsep((char **)&page, "\n"); + + /* If already configured, we cannot create the stream */ + if (snd_avirt_streams_configured()) { + D_ERRORK("Streams already configured. Cannot set map: '%s'", + map); + return -EPERM; + } + + if (!strcmp(stream->map, map)) + return -1; + + audiopath = snd_avirt_audiopath_get(map); + if (!audiopath) { + D_ERRORK("Cannot find Audio Path uid: '%s'!", stream->map); + } + + memcpy(stream->map, (char *)map, strlen(map)); + + snd_avirt_stream_try_complete(stream); return count; } @@ -98,85 +178,100 @@ static struct configfs_attribute *cfg_snd_avirt_stream_attrs[] = { NULL, }; -static ssize_t cfg_snd_avirt_route_to_ap_show(struct config_item *item, - char *page) +static ssize_t cfg_snd_avirt_route_sink_ap_show(struct config_item *item, + char *page) { struct snd_avirt_route *route = snd_avirt_route_from_config_item(item); + if (!route) { + D_ERRORK("Cannot get route!"); + goto exit_err; + } - if (route) - if (route->to_ap) - return sprintf(page, "%s\n", route->to_ap->uid); + if (route->endpoint_ap[SND_AVIRT_ROUTE_SINK]) + return sprintf(page, "%s\n", + route->endpoint_ap[SND_AVIRT_ROUTE_SINK]->uid); +exit_err: return sprintf(page, "\n"); } -static ssize_t cfg_snd_avirt_route_to_ap_store(struct config_item *item, - const char *page, size_t count) +static ssize_t cfg_snd_avirt_route_sink_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; + struct snd_avirt_audiopath *sink_ap; + + if (!route) { + D_ERRORK("Cannot get route!"); + goto exit; + } uid_ap = strsep((char **)&page, "\n"); - to_ap = snd_avirt_audiopath_get(uid_ap); - if (!to_ap) { + sink_ap = snd_avirt_audiopath_get(uid_ap); + if (!sink_ap) { D_ERRORK("Audio Path '%s' does not exist!", uid_ap); - D_ERRORK("Cannot set 'route'->'to_ap'"); - return count; + D_ERRORK("Cannot set 'route'->'sink_ap'"); + goto exit; } - 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; - } + cfg_snd_avirt_route_ap_store(route, sink_ap, SND_AVIRT_ROUTE_SINK); +exit: return count; } -CONFIGFS_ATTR(cfg_snd_avirt_route_, to_ap); +CONFIGFS_ATTR(cfg_snd_avirt_route_, sink_ap); -static ssize_t cfg_snd_avirt_route_from_ap_show(struct config_item *item, - char *page) +static ssize_t cfg_snd_avirt_route_source_ap_show(struct config_item *item, + char *page) { struct snd_avirt_route *route = snd_avirt_route_from_config_item(item); + if (!route) { + D_ERRORK("Cannot get route!"); + goto exit_err; + } - if (route) - if (route->from_ap) - return sprintf(page, "%s\n", route->from_ap->uid); + if (route->endpoint_ap[SND_AVIRT_ROUTE_SOURCE]) + return sprintf(page, "%s\n", + route->endpoint_ap[SND_AVIRT_ROUTE_SOURCE]->uid); +exit_err: return sprintf(page, "\n"); } -static ssize_t cfg_snd_avirt_route_from_ap_store(struct config_item *item, - const char *page, size_t count) +static ssize_t cfg_snd_avirt_route_source_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; + struct snd_avirt_audiopath *source_ap; + + if (!route) { + D_ERRORK("Cannot get route!"); + goto exit; + } uid_ap = strsep((char **)&page, "\n"); - from_ap = snd_avirt_audiopath_get(uid_ap); - if (!from_ap) { + source_ap = snd_avirt_audiopath_get(uid_ap); + if (!source_ap) { D_ERRORK("Audio Path '%s' does not exist!", uid_ap); - D_ERRORK("Cannot set 'route'->'from_ap'"); + D_ERRORK("Cannot set 'route'->'source_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; - } + cfg_snd_avirt_route_ap_store(route, source_ap, SND_AVIRT_ROUTE_SOURCE); +exit: return count; } -CONFIGFS_ATTR(cfg_snd_avirt_route_, from_ap); +CONFIGFS_ATTR(cfg_snd_avirt_route_, source_ap); 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, + &cfg_snd_avirt_route_attr_source_ap, + &cfg_snd_avirt_route_attr_sink_ap, NULL, }; @@ -190,8 +285,7 @@ static void cfg_snd_avirt_stream_release(struct config_item *item) } D_INFOK("Release stream: %s", stream->name); - kfree(stream->pcm_ops); - kfree(stream); + snd_avirt_stream_try_destroy(stream); } static void cfg_snd_avirt_route_release(struct config_item *item) @@ -202,8 +296,9 @@ static void cfg_snd_avirt_route_release(struct config_item *item) return; } - D_INFOK("Release route: %s", route->name); kfree(route); + + D_INFOK("Release route: %s", route->uid); } static struct configfs_item_operations cfg_snd_avirt_stream_ops = { @@ -229,7 +324,8 @@ static struct config_item_type cfg_snd_avirt_route_type = { static struct config_item * cfg_snd_avirt_stream_make_item(struct config_group *group, const char *name) { - char *split; + char *split, *stream_name; + bool internal = false; int direction; struct snd_avirt_stream *stream; @@ -251,16 +347,23 @@ cfg_snd_avirt_stream_make_item(struct config_group *group, const char *name) } // Get stream name, and create PCM for stream - split = strsep((char **)&name, "\n"); - stream = snd_avirt_stream_create(split, direction); + stream_name = strsep((char **)&name, "\n"); + + // If internal, get internal + split = strstr(stream_name, "__internal"); + if (split) { + stream_name = strsep((char **)&stream_name, "__"); + internal = true; + } + + // Finally, create stream + stream = snd_avirt_stream_create(stream_name, direction, internal); if (IS_ERR(stream)) return ERR_PTR(PTR_ERR(stream)); config_item_init_type_name(&stream->item, name, &cfg_snd_avirt_stream_type); - D_INFOK("Make stream: %s", stream->name); - return &stream->item; } @@ -297,39 +400,38 @@ cfg_snd_avirt_route_make_item(struct config_group *group, const char *name) 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) +static ssize_t +cfg_snd_avirt_stream_group_configured_show(struct config_item *item, char *page) { - return snprintf(page, PAGE_SIZE, "%d\n", snd_avirt_streams_sealed()); + return snprintf(page, PAGE_SIZE, "%d\n", + snd_avirt_streams_configured()); } -static ssize_t cfg_snd_avirt_stream_group_sealed_store(struct config_item *item, - const char *page, - size_t count) +static ssize_t +cfg_snd_avirt_stream_group_configured_store(struct config_item *item, + const char *page, size_t count) { unsigned long tmp; char *p = (char *)page; CHK_ERR(kstrtoul(p, 10, &tmp)); - if (tmp != 1) { - D_ERRORK("streams can only be sealed, not unsealed!"); + if (tmp > 1) { + D_ERRORK("Configure streams must be 0 or 1!"); return -ERANGE; } - snd_avirt_streams_seal(); + (tmp) ? snd_avirt_streams_configure() : snd_avirt_streams_unconfigure(); return count; } -CONFIGFS_ATTR(cfg_snd_avirt_stream_group_, sealed); +CONFIGFS_ATTR(cfg_snd_avirt_stream_group_, configured); static struct configfs_attribute *cfg_snd_avirt_stream_group_attrs[] = { - &cfg_snd_avirt_stream_group_attr_sealed, + &cfg_snd_avirt_stream_group_attr_configured, NULL, }; |