mirror of
https://github.com/alsa-project/alsa-lib.git
synced 2026-03-27 07:58:02 -04:00
hw: add option to specify dynamic DAPM routing to apply when the plugin is opened
Signed-off-by: Benedek Kupper <benedek.kupper@streamunlimited.com>
This commit is contained in:
parent
7ffe3d4162
commit
b6d31eaf7b
2 changed files with 174 additions and 4 deletions
|
|
@ -62,14 +62,14 @@ typedef int snd_pcm_route_ttable_entry_t;
|
||||||
#define SND_PCM_PLUGIN_ROUTE_HALF (SND_PCM_PLUGIN_ROUTE_RESOLUTION / 2) /**< half value */
|
#define SND_PCM_PLUGIN_ROUTE_HALF (SND_PCM_PLUGIN_ROUTE_RESOLUTION / 2) /**< half value */
|
||||||
#define SND_PCM_PLUGIN_ROUTE_FULL SND_PCM_PLUGIN_ROUTE_RESOLUTION /**< full value */
|
#define SND_PCM_PLUGIN_ROUTE_FULL SND_PCM_PLUGIN_ROUTE_RESOLUTION /**< full value */
|
||||||
#endif
|
#endif
|
||||||
|
struct snd_pcm_ctl_route;
|
||||||
/*
|
/*
|
||||||
* Hardware plugin
|
* Hardware plugin
|
||||||
*/
|
*/
|
||||||
int snd_pcm_hw_open(snd_pcm_t **pcmp, const char *name,
|
int snd_pcm_hw_open(snd_pcm_t **pcmp, const char *name,
|
||||||
int card, int device, int subdevice,
|
int card, int device, int subdevice,
|
||||||
snd_pcm_stream_t stream, int mode,
|
snd_pcm_stream_t stream, int mode,
|
||||||
int mmap_emulation, int sync_ptr_ioctl);
|
int mmap_emulation, int sync_ptr_ioctl, struct snd_pcm_ctl_route *ctl_routes);
|
||||||
int _snd_pcm_hw_open(snd_pcm_t **pcmp, const char *name,
|
int _snd_pcm_hw_open(snd_pcm_t **pcmp, const char *name,
|
||||||
snd_config_t *root ATTRIBUTE_UNUSED, snd_config_t *conf,
|
snd_config_t *root ATTRIBUTE_UNUSED, snd_config_t *conf,
|
||||||
snd_pcm_stream_t stream, int mode);
|
snd_pcm_stream_t stream, int mode);
|
||||||
|
|
|
||||||
174
src/pcm/pcm_hw.c
174
src/pcm/pcm_hw.c
|
|
@ -87,6 +87,11 @@ static const snd_pcm_fast_ops_t snd_pcm_hw_fast_ops_timer;
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
typedef struct snd_pcm_ctl_route {
|
||||||
|
const char *elem;
|
||||||
|
const char *value;
|
||||||
|
} snd_pcm_ctl_route_t;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
int version;
|
int version;
|
||||||
int fd;
|
int fd;
|
||||||
|
|
@ -1415,6 +1420,152 @@ static int snd_pcm_hw_set_chmap(snd_pcm_t *pcm, const snd_pcm_chmap_t *map)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static snd_pcm_ctl_route_t *
|
||||||
|
snd_pcm_hw_parse_ctl_routes(snd_config_t *conf)
|
||||||
|
{
|
||||||
|
snd_pcm_ctl_route_t *route_list;
|
||||||
|
snd_config_iterator_t i, next;
|
||||||
|
const char *str;
|
||||||
|
int nums, err;
|
||||||
|
|
||||||
|
if (snd_config_get_type(conf) != SND_CONFIG_TYPE_COMPOUND) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
nums = 0;
|
||||||
|
snd_config_for_each(i, next, conf) {
|
||||||
|
nums++;
|
||||||
|
}
|
||||||
|
|
||||||
|
route_list = calloc(nums + 1, sizeof(*route_list));
|
||||||
|
if (!route_list) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
nums = 0;
|
||||||
|
snd_config_for_each(i, next, conf) {
|
||||||
|
snd_config_t *n = snd_config_iterator_entry(i);
|
||||||
|
if (snd_config_get_id(n, &route_list[nums].elem) < 0) {
|
||||||
|
SNDERR("Missing route ID #%d\n", nums);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = snd_config_get_string(n, &route_list[nums].value);
|
||||||
|
if (err < 0) {
|
||||||
|
SNDERR("Missing route value #%d\n", nums);
|
||||||
|
goto _err;
|
||||||
|
}
|
||||||
|
nums++;
|
||||||
|
}
|
||||||
|
return route_list;
|
||||||
|
|
||||||
|
_err:
|
||||||
|
free(route_list);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int snd_pcm_hw_get_enum_item_index(snd_ctl_t *ctl, snd_ctl_elem_id_t *id, const char *name)
|
||||||
|
{
|
||||||
|
snd_ctl_elem_info_t info = {};
|
||||||
|
int err;
|
||||||
|
unsigned int i, items;
|
||||||
|
|
||||||
|
snd_ctl_elem_info_set_id(&info, id);
|
||||||
|
|
||||||
|
if ((err = snd_ctl_elem_info(ctl, &info)) < 0) {
|
||||||
|
SNDERR("snd_ctl_elem_info failure: %s\n", snd_strerror(err));
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
if (SND_CTL_ELEM_TYPE_ENUMERATED != snd_ctl_elem_info_get_type(&info)) {
|
||||||
|
SNDERR("\"%s\" control isn't enum type (%s)\n", name,
|
||||||
|
snd_ctl_elem_type_name(snd_ctl_elem_info_get_type(&info)));
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
items = snd_ctl_elem_info_get_items(&info);
|
||||||
|
for (i = 0; i < items; i++) {
|
||||||
|
const char *itemname;
|
||||||
|
|
||||||
|
/* need to reload the info to get the selected name copied into info */
|
||||||
|
snd_ctl_elem_info_set_item(&info, i);
|
||||||
|
if ((err = snd_ctl_elem_info(ctl, &info)) < 0) {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
itemname = snd_ctl_elem_info_get_item_name(&info);
|
||||||
|
if (strcmp(name, itemname) == 0) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int snd_pcm_hw_set_enum_ctl(snd_ctl_t *ctl, snd_ctl_elem_id_t *ctl_id, const char* enum_value)
|
||||||
|
{
|
||||||
|
snd_ctl_elem_value_t ctl_value = {};
|
||||||
|
int enum_index;
|
||||||
|
|
||||||
|
/* lookup enum value by name */
|
||||||
|
enum_index = snd_pcm_hw_get_enum_item_index(ctl, ctl_id, enum_value);
|
||||||
|
if (enum_index < 0) {
|
||||||
|
SNDERR("enum value \"%s\" for control \"%s\" not found: %d\n",
|
||||||
|
enum_value, snd_ctl_elem_id_get_name(ctl_id), enum_index);
|
||||||
|
return enum_index;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* apply value */
|
||||||
|
snd_ctl_elem_value_set_id(&ctl_value, ctl_id);
|
||||||
|
snd_ctl_elem_value_set_enumerated(&ctl_value, 0, enum_index);
|
||||||
|
return snd_ctl_elem_write(ctl, &ctl_value);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void snd_pcm_hw_set_routes(snd_ctl_t *ctl, snd_pcm_ctl_route_t *routes)
|
||||||
|
{
|
||||||
|
snd_ctl_elem_list_t clist = {};
|
||||||
|
snd_pcm_ctl_route_t *route;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
/* inspired by snd_hctl_load, need to reallocate the list
|
||||||
|
* until all elem IDs are placed on it */
|
||||||
|
if ((err = snd_ctl_elem_list(ctl, &clist)) < 0) {
|
||||||
|
goto _err;
|
||||||
|
}
|
||||||
|
while (clist.count != clist.used) {
|
||||||
|
if ((err = snd_ctl_elem_list_alloc_space(&clist, clist.count)) < 0) {
|
||||||
|
goto _err;
|
||||||
|
}
|
||||||
|
if ((err = snd_ctl_elem_list(ctl, &clist)) < 0) {
|
||||||
|
goto _err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* lookup each route in the elem list */
|
||||||
|
for (route = routes; (route->elem != NULL) && (route->value != NULL); route++) {
|
||||||
|
snd_ctl_elem_id_t ctl_id = {};
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < clist.count; i++) {
|
||||||
|
const char *name;
|
||||||
|
|
||||||
|
snd_ctl_elem_list_get_id(&clist, i, &ctl_id);
|
||||||
|
name = snd_ctl_elem_id_get_name(&ctl_id);
|
||||||
|
|
||||||
|
if (strcmp(name, route->elem) == 0) {
|
||||||
|
/* elem found, set the requested value */
|
||||||
|
snd_pcm_hw_set_enum_ctl(ctl, &ctl_id, route->value);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (i == clist.count) {
|
||||||
|
SNDERR("Cannot find control \"%s\"\n", route->elem);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_err:
|
||||||
|
/* don't forget to clean up the elem list buffer */
|
||||||
|
snd_ctl_elem_list_free_space(&clist);
|
||||||
|
}
|
||||||
|
|
||||||
static void snd_pcm_hw_dump(snd_pcm_t *pcm, snd_output_t *out)
|
static void snd_pcm_hw_dump(snd_pcm_t *pcm, snd_output_t *out)
|
||||||
{
|
{
|
||||||
snd_pcm_hw_t *hw = pcm->private_data;
|
snd_pcm_hw_t *hw = pcm->private_data;
|
||||||
|
|
@ -1667,7 +1818,7 @@ int snd_pcm_hw_open(snd_pcm_t **pcmp, const char *name,
|
||||||
int card, int device, int subdevice,
|
int card, int device, int subdevice,
|
||||||
snd_pcm_stream_t stream, int mode,
|
snd_pcm_stream_t stream, int mode,
|
||||||
int mmap_emulation ATTRIBUTE_UNUSED,
|
int mmap_emulation ATTRIBUTE_UNUSED,
|
||||||
int sync_ptr_ioctl)
|
int sync_ptr_ioctl, snd_pcm_ctl_route_t *ctl_routes)
|
||||||
{
|
{
|
||||||
char filename[sizeof(SNDRV_FILE_PCM_STREAM_PLAYBACK) + 20];
|
char filename[sizeof(SNDRV_FILE_PCM_STREAM_PLAYBACK) + 20];
|
||||||
const char *filefmt;
|
const char *filefmt;
|
||||||
|
|
@ -1695,6 +1846,9 @@ int snd_pcm_hw_open(snd_pcm_t **pcmp, const char *name,
|
||||||
}
|
}
|
||||||
sprintf(filename, filefmt, card, device);
|
sprintf(filename, filefmt, card, device);
|
||||||
|
|
||||||
|
if (ctl_routes)
|
||||||
|
snd_pcm_hw_set_routes(ctl, ctl_routes);
|
||||||
|
|
||||||
__again:
|
__again:
|
||||||
if (attempt++ > 3) {
|
if (attempt++ > 3) {
|
||||||
ret = -EBUSY;
|
ret = -EBUSY;
|
||||||
|
|
@ -1764,6 +1918,9 @@ pcm.name {
|
||||||
[channels INT] # Restrict only to the given channels
|
[channels INT] # Restrict only to the given channels
|
||||||
[rate INT] # Restrict only to the given rate
|
[rate INT] # Restrict only to the given rate
|
||||||
[chmap MAP] # Override channel maps; MAP is a string array
|
[chmap MAP] # Override channel maps; MAP is a string array
|
||||||
|
[routing {
|
||||||
|
"Mux" "IN1"
|
||||||
|
}] # DAPM dynamic routing list to configure when opening the plugin
|
||||||
}
|
}
|
||||||
\endcode
|
\endcode
|
||||||
|
|
||||||
|
|
@ -1801,6 +1958,7 @@ int _snd_pcm_hw_open(snd_pcm_t **pcmp, const char *name,
|
||||||
snd_config_t *n;
|
snd_config_t *n;
|
||||||
int nonblock = 1; /* non-block per default */
|
int nonblock = 1; /* non-block per default */
|
||||||
snd_pcm_chmap_query_t **chmap = NULL;
|
snd_pcm_chmap_query_t **chmap = NULL;
|
||||||
|
snd_pcm_ctl_route_t *route_list = NULL;
|
||||||
snd_pcm_hw_t *hw;
|
snd_pcm_hw_t *hw;
|
||||||
|
|
||||||
/* look for defaults.pcm.nonblock definition */
|
/* look for defaults.pcm.nonblock definition */
|
||||||
|
|
@ -1892,6 +2050,16 @@ int _snd_pcm_hw_open(snd_pcm_t **pcmp, const char *name,
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (strcmp(id, "routing") == 0) {
|
||||||
|
free(route_list);
|
||||||
|
route_list = snd_pcm_hw_parse_ctl_routes(n);
|
||||||
|
if (!route_list) {
|
||||||
|
SNDERR("Invalid routing for %s", id);
|
||||||
|
err = -EINVAL;
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
SNDERR("Unknown field %s", id);
|
SNDERR("Unknown field %s", id);
|
||||||
err = -EINVAL;
|
err = -EINVAL;
|
||||||
goto fail;
|
goto fail;
|
||||||
|
|
@ -1903,7 +2071,8 @@ int _snd_pcm_hw_open(snd_pcm_t **pcmp, const char *name,
|
||||||
}
|
}
|
||||||
err = snd_pcm_hw_open(pcmp, name, card, device, subdevice, stream,
|
err = snd_pcm_hw_open(pcmp, name, card, device, subdevice, stream,
|
||||||
mode | (nonblock ? SND_PCM_NONBLOCK : 0),
|
mode | (nonblock ? SND_PCM_NONBLOCK : 0),
|
||||||
0, sync_ptr_ioctl);
|
0, sync_ptr_ioctl, route_list);
|
||||||
|
free(route_list);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
goto fail;
|
goto fail;
|
||||||
if (nonblock && ! (mode & SND_PCM_NONBLOCK)) {
|
if (nonblock && ! (mode & SND_PCM_NONBLOCK)) {
|
||||||
|
|
@ -1932,6 +2101,7 @@ int _snd_pcm_hw_open(snd_pcm_t **pcmp, const char *name,
|
||||||
|
|
||||||
fail:
|
fail:
|
||||||
snd_pcm_free_chmaps(chmap);
|
snd_pcm_free_chmaps(chmap);
|
||||||
|
free(route_list);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue