diff options
Diffstat (limited to 'firmware/target/hosted/alsa-controls.c')
-rw-r--r-- | firmware/target/hosted/alsa-controls.c | 167 |
1 files changed, 103 insertions, 64 deletions
diff --git a/firmware/target/hosted/alsa-controls.c b/firmware/target/hosted/alsa-controls.c index f4914aa216..8d8c46decd 100644 --- a/firmware/target/hosted/alsa-controls.c +++ b/firmware/target/hosted/alsa-controls.c | |||
@@ -19,33 +19,108 @@ | |||
19 | ****************************************************************************/ | 19 | ****************************************************************************/ |
20 | #include "alsa-controls.h" | 20 | #include "alsa-controls.h" |
21 | #include "panic.h" | 21 | #include "panic.h" |
22 | #include "debug.h" | ||
22 | #include <stdlib.h> | 23 | #include <stdlib.h> |
23 | 24 | ||
24 | /* alsa control handle, we keep it open at all times */ | 25 | /* alsa control handle, we keep it open at all times */ |
25 | static snd_ctl_t *alsa_ctl; | 26 | static snd_ctl_t *alsa_ctl; |
26 | /* list of all controls, we allocate and fill it once, so we can easily lookup */ | 27 | /* list of all controls, we allocate and fill it once, so we can easily lookup */ |
27 | static snd_ctl_elem_list_t *alsa_ctl_list; | 28 | static snd_ctl_elem_list_t *alsa_ctl_list; |
29 | static unsigned alsa_ctl_count; | ||
30 | /* for each control, we store some info to avoid constantly using ALSA's horrible lookup functions */ | ||
31 | static struct ctl_t | ||
32 | { | ||
33 | snd_ctl_elem_id_t *id; /* ID associated with the control */ | ||
34 | const char *name; /* name of the control */ | ||
35 | snd_ctl_elem_type_t type; /* control type (boolean, enum, ...) */ | ||
36 | unsigned count; /* dimension of the control */ | ||
37 | unsigned items; /* number of items (for enums) */ | ||
38 | const char **enum_name; /* names of the enum, indexed by ALSA index */ | ||
39 | } *alsa_ctl_info; | ||
28 | 40 | ||
29 | void alsa_controls_init(void) | 41 | #if defined(DEBUG) |
42 | static const char *alsa_ctl_type_name(snd_ctl_elem_type_t type) | ||
43 | { | ||
44 | switch(type) | ||
45 | { | ||
46 | case SND_CTL_ELEM_TYPE_BOOLEAN: return "BOOLEAN"; | ||
47 | case SND_CTL_ELEM_TYPE_INTEGER: return "INTEGER"; | ||
48 | case SND_CTL_ELEM_TYPE_ENUMERATED: return "ENUMERATED"; | ||
49 | default: return "???"; | ||
50 | } | ||
51 | } | ||
52 | #endif | ||
53 | |||
54 | void alsa_controls_init(const char *name) | ||
30 | { | 55 | { |
31 | snd_ctl_elem_info_t *info; | ||
32 | /* allocate list on heap because it is used it is used in other functions */ | 56 | /* allocate list on heap because it is used it is used in other functions */ |
33 | snd_ctl_elem_list_malloc(&alsa_ctl_list); | 57 | snd_ctl_elem_list_malloc(&alsa_ctl_list); |
34 | /* allocate info on stack so there is no need to free them */ | ||
35 | snd_ctl_elem_info_alloca(&info); | ||
36 | /* there is only one card and "default" always works */ | 58 | /* there is only one card and "default" always works */ |
37 | if(snd_ctl_open(&alsa_ctl, "default", 0) < 0) | 59 | if(snd_ctl_open(&alsa_ctl, name, 0) < 0) |
38 | panicf("Cannot open ctl\n"); | 60 | panicf("Cannot open ctl\n"); |
39 | /* ALSA is braindead: first "get" the list -> only retrieve count */ | 61 | /* ALSA is braindead: first "get" the list -> only retrieve count */ |
40 | if(snd_ctl_elem_list(alsa_ctl, alsa_ctl_list) < 0) | 62 | if(snd_ctl_elem_list(alsa_ctl, alsa_ctl_list) < 0) |
41 | panicf("Cannot get element list\n"); | 63 | panicf("Cannot get element list\n"); |
42 | /* now we can allocate the list since the know the size */ | 64 | /* now we can allocate the list since the know the size */ |
43 | int count = snd_ctl_elem_list_get_count(alsa_ctl_list); | 65 | alsa_ctl_count = snd_ctl_elem_list_get_count(alsa_ctl_list); |
44 | if(snd_ctl_elem_list_alloc_space(alsa_ctl_list, count) < 0) | 66 | if(snd_ctl_elem_list_alloc_space(alsa_ctl_list, alsa_ctl_count) < 0) |
45 | panicf("Cannot allocate space for element list\n"); | 67 | panicf("Cannot allocate space for element list\n"); |
46 | /* ... and get the list again! */ | 68 | /* ... and get the list again! */ |
47 | if(snd_ctl_elem_list(alsa_ctl, alsa_ctl_list) < 0) | 69 | if(snd_ctl_elem_list(alsa_ctl, alsa_ctl_list) < 0) |
48 | panicf("Cannot get element list\n"); | 70 | panicf("Cannot get element list\n"); |
71 | /* now cache the info */ | ||
72 | alsa_ctl_info = malloc(sizeof(struct ctl_t) * alsa_ctl_count); | ||
73 | snd_ctl_elem_info_t *info; | ||
74 | snd_ctl_elem_info_malloc(&info); | ||
75 | for(unsigned i = 0; i < alsa_ctl_count; i++) | ||
76 | { | ||
77 | /* allocate the ID and retrieve it */ | ||
78 | snd_ctl_elem_id_malloc(&alsa_ctl_info[i].id); | ||
79 | snd_ctl_elem_list_get_id(alsa_ctl_list, i, alsa_ctl_info[i].id); | ||
80 | /* NOTE: name is valid as long as the ID lives; since we keep both, no need to copy */ | ||
81 | alsa_ctl_info[i].name = snd_ctl_elem_id_get_name(alsa_ctl_info[i].id); | ||
82 | /* get info */ | ||
83 | snd_ctl_elem_info_set_id(info, alsa_ctl_info[i].id); | ||
84 | if(snd_ctl_elem_info(alsa_ctl, info) < 0) | ||
85 | panicf("Cannot get control '%s' info", alsa_ctl_info[i].name); | ||
86 | alsa_ctl_info[i].type = snd_ctl_elem_info_get_type(info); | ||
87 | alsa_ctl_info[i].count = snd_ctl_elem_info_get_count(info); | ||
88 | if(alsa_ctl_info[i].type == SND_CTL_ELEM_TYPE_ENUMERATED) | ||
89 | { | ||
90 | alsa_ctl_info[i].items = snd_ctl_elem_info_get_items(info); | ||
91 | alsa_ctl_info[i].enum_name = malloc(sizeof(char *) * alsa_ctl_info[i].items); | ||
92 | for(unsigned j = 0; j < alsa_ctl_info[i].items; j++) | ||
93 | { | ||
94 | snd_ctl_elem_info_set_item(info, j); | ||
95 | if(snd_ctl_elem_info(alsa_ctl, info) < 0) | ||
96 | panicf("Cannot get control '%s' info for item %u", alsa_ctl_info[i].name, j); | ||
97 | /* NOTE: copy string because it becomes invalid after info is freed */ | ||
98 | alsa_ctl_info[i].enum_name[j] = strdup(snd_ctl_elem_info_get_item_name(info)); | ||
99 | } | ||
100 | } | ||
101 | else | ||
102 | alsa_ctl_info[i].items = 0; | ||
103 | } | ||
104 | snd_ctl_elem_info_free(info); | ||
105 | |||
106 | DEBUGF("ALSA controls:\n"); | ||
107 | for(unsigned i = 0; i < alsa_ctl_count; i++) | ||
108 | { | ||
109 | DEBUGF("- name='%s', type=%s, count=%u\n", alsa_ctl_info[i].name, | ||
110 | alsa_ctl_type_name(alsa_ctl_info[i].type), alsa_ctl_info[i].count); | ||
111 | if(alsa_ctl_info[i].type == SND_CTL_ELEM_TYPE_ENUMERATED) | ||
112 | { | ||
113 | DEBUGF(" items:"); | ||
114 | for(unsigned j = 0; j < alsa_ctl_info[i].items; j++) | ||
115 | { | ||
116 | if(j != 0) | ||
117 | DEBUGF(","); | ||
118 | DEBUGF(" '%s'", alsa_ctl_info[i].enum_name[j]); | ||
119 | } | ||
120 | DEBUGF("\n"); | ||
121 | } | ||
122 | } | ||
123 | |||
49 | } | 124 | } |
50 | 125 | ||
51 | void alsa_controls_close(void) | 126 | void alsa_controls_close(void) |
@@ -53,56 +128,33 @@ void alsa_controls_close(void) | |||
53 | snd_ctl_close(alsa_ctl); | 128 | snd_ctl_close(alsa_ctl); |
54 | } | 129 | } |
55 | 130 | ||
56 | /* find a control element ID by name, return false of not found, the id needs | 131 | /* find a control element ID by name, return -1 of not found or index into array */ |
57 | * to be allocated */ | 132 | int alsa_controls_find(const char *name) |
58 | bool alsa_controls_find(snd_ctl_elem_id_t *id, const char *name) | ||
59 | { | 133 | { |
60 | /* ALSA identifies controls by "id"s, it almost makes sense, except ids | ||
61 | * are a horrible opaque structure */ | ||
62 | int count = snd_ctl_elem_list_get_count(alsa_ctl_list); | ||
63 | /* enumerate controls */ | 134 | /* enumerate controls */ |
64 | for(int i = 0; i < count; i++) | 135 | for(unsigned i = 0; i < alsa_ctl_count; i++) |
65 | { | 136 | if(strcmp(alsa_ctl_info[i].name, name) == 0) |
66 | snd_ctl_elem_list_get_id(alsa_ctl_list, i, id); | 137 | return i; |
67 | |||
68 | if(strcmp(snd_ctl_elem_id_get_name(id), name) == 0) | ||
69 | return true; | ||
70 | } | ||
71 | /* not found */ | 138 | /* not found */ |
72 | return false; | 139 | return -1; |
73 | } | 140 | } |
74 | 141 | ||
75 | bool alsa_has_control(const char *name) | 142 | bool alsa_has_control(const char *name) |
76 | { | 143 | { |
77 | snd_ctl_elem_id_t *id; | 144 | return alsa_controls_find(name) >= 0; |
78 | /* allocate things on stack */ | ||
79 | snd_ctl_elem_id_alloca(&id); | ||
80 | /* find control */ | ||
81 | return alsa_controls_find(id, name); | ||
82 | } | 145 | } |
83 | 146 | ||
84 | /* find a control element enum index by name, return -1 if not found */ | 147 | /* find a control element enum index by name, return -1 if not found */ |
85 | int alsa_controls_find_enum(const char *name, const char *enum_name) | 148 | int alsa_controls_find_enum(const char *name, const char *enum_name) |
86 | { | 149 | { |
87 | snd_ctl_elem_id_t *id; | ||
88 | snd_ctl_elem_info_t *info; | ||
89 | /* allocate things on stack to speedup */ | ||
90 | snd_ctl_elem_id_alloca(&id); | ||
91 | snd_ctl_elem_info_alloca(&info); | ||
92 | /* find control */ | 150 | /* find control */ |
93 | if(!alsa_controls_find(id, name)) | 151 | int idx = alsa_controls_find(name); |
152 | if(idx < 0) | ||
94 | panicf("Cannot find control '%s'", name); | 153 | panicf("Cannot find control '%s'", name); |
95 | snd_ctl_elem_info_set_id(info, id); | ||
96 | if(snd_ctl_elem_info(alsa_ctl, info) < 0) | ||
97 | panicf("Cannot get control '%s' info", name); | ||
98 | /* list items */ | 154 | /* list items */ |
99 | unsigned count = snd_ctl_elem_info_get_items(info); | 155 | for(unsigned i = 0; i < alsa_ctl_info[idx].items; i++) |
100 | for(unsigned i = 0; i < count; i++) | ||
101 | { | 156 | { |
102 | snd_ctl_elem_info_set_item(info, i); | 157 | if(strcmp(alsa_ctl_info[idx].enum_name[i], enum_name) == 0) |
103 | if(snd_ctl_elem_info(alsa_ctl, info) < 0) | ||
104 | panicf("Cannot get control '%s' info for item %u", name, i); | ||
105 | if(strcmp(snd_ctl_elem_info_get_item_name(info), enum_name) == 0) | ||
106 | return i; | 158 | return i; |
107 | } | 159 | } |
108 | return -1; | 160 | return -1; |
@@ -112,27 +164,21 @@ int alsa_controls_find_enum(const char *name, const char *enum_name) | |||
112 | void alsa_controls_get_set(bool get, const char *name, snd_ctl_elem_type_t type, | 164 | void alsa_controls_get_set(bool get, const char *name, snd_ctl_elem_type_t type, |
113 | unsigned nr_values, long *val) | 165 | unsigned nr_values, long *val) |
114 | { | 166 | { |
115 | snd_ctl_elem_id_t *id; | ||
116 | snd_ctl_elem_info_t *info; | ||
117 | snd_ctl_elem_value_t *value; | 167 | snd_ctl_elem_value_t *value; |
118 | /* allocate things on stack to speedup */ | 168 | /* allocate things on stack to speedup */ |
119 | snd_ctl_elem_id_alloca(&id); | ||
120 | snd_ctl_elem_info_alloca(&info); | ||
121 | snd_ctl_elem_value_alloca(&value); | 169 | snd_ctl_elem_value_alloca(&value); |
122 | /* find control */ | 170 | /* find control */ |
123 | if(!alsa_controls_find(id, name)) | 171 | int idx = alsa_controls_find(name); |
172 | if(idx < 0) | ||
124 | panicf("Cannot find control '%s'", name); | 173 | panicf("Cannot find control '%s'", name); |
125 | /* check the type of the control */ | 174 | /* check the type of the control */ |
126 | snd_ctl_elem_info_set_id(info, id); | 175 | if(alsa_ctl_info[idx].type != type) |
127 | if(snd_ctl_elem_info(alsa_ctl, info) < 0) | ||
128 | panicf("Cannot get control '%s' info", name); | ||
129 | if(snd_ctl_elem_info_get_type(info) != type) | ||
130 | panicf("Control '%s' has wrong type (got %d, expected %d", name, | 176 | panicf("Control '%s' has wrong type (got %d, expected %d", name, |
131 | snd_ctl_elem_info_get_type(info), type); | 177 | alsa_ctl_info[idx].type, type); |
132 | if(snd_ctl_elem_info_get_count(info) != nr_values) | 178 | if(alsa_ctl_info[idx].count != nr_values) |
133 | panicf("Control '%s' has wrong count (got %u, expected %u)", | 179 | panicf("Control '%s' has wrong count (got %u, expected %u)", |
134 | name, snd_ctl_elem_info_get_count(info), nr_values); | 180 | name, alsa_ctl_info[idx].count, nr_values); |
135 | snd_ctl_elem_value_set_id(value, id); | 181 | snd_ctl_elem_value_set_id(value, alsa_ctl_info[idx].id); |
136 | /* set value */ | 182 | /* set value */ |
137 | if(get) | 183 | if(get) |
138 | { | 184 | { |
@@ -185,19 +231,12 @@ void alsa_controls_get(const char *name, snd_ctl_elem_type_t type, | |||
185 | /* get control information */ | 231 | /* get control information */ |
186 | void alsa_controls_get_info(const char *name, int *out_count) | 232 | void alsa_controls_get_info(const char *name, int *out_count) |
187 | { | 233 | { |
188 | snd_ctl_elem_id_t *id; | ||
189 | snd_ctl_elem_info_t *info; | ||
190 | /* allocate things on stack to speedup */ | ||
191 | snd_ctl_elem_id_alloca(&id); | ||
192 | snd_ctl_elem_info_alloca(&info); | ||
193 | /* find control */ | 234 | /* find control */ |
194 | if(!alsa_controls_find(id, name)) | 235 | int idx = alsa_controls_find(name); |
236 | if(idx < 0) | ||
195 | panicf("Cannot find control '%s'", name); | 237 | panicf("Cannot find control '%s'", name); |
196 | /* get info */ | 238 | /* get info */ |
197 | snd_ctl_elem_info_set_id(info, id); | 239 | *out_count = alsa_ctl_info[idx].count; |
198 | if(snd_ctl_elem_info(alsa_ctl, info) < 0) | ||
199 | panicf("Cannot get control '%s' info", name); | ||
200 | *out_count = snd_ctl_elem_info_get_count(info); | ||
201 | } | 240 | } |
202 | 241 | ||
203 | /* helper function: set a control with a single boolean value */ | 242 | /* helper function: set a control with a single boolean value */ |