1 | // SPDX-License-Identifier: GPL-2.0 |
2 | // |
3 | // kselftest for the ALSA mixer API |
4 | // |
5 | // Original author: Mark Brown <broonie@kernel.org> |
6 | // Copyright (c) 2021-2 Arm Limited |
7 | |
8 | // This test will iterate over all cards detected in the system, exercising |
9 | // every mixer control it can find. This may conflict with other system |
10 | // software if there is audio activity so is best run on a system with a |
11 | // minimal active userspace. |
12 | |
13 | #include <stdio.h> |
14 | #include <stdlib.h> |
15 | #include <stdbool.h> |
16 | #include <limits.h> |
17 | #include <string.h> |
18 | #include <getopt.h> |
19 | #include <stdarg.h> |
20 | #include <ctype.h> |
21 | #include <math.h> |
22 | #include <errno.h> |
23 | #include <assert.h> |
24 | #include <alsa/asoundlib.h> |
25 | #include <poll.h> |
26 | #include <stdint.h> |
27 | |
28 | #include "../kselftest.h" |
29 | #include "alsa-local.h" |
30 | |
31 | #define TESTS_PER_CONTROL 7 |
32 | |
33 | struct card_data { |
34 | snd_ctl_t *handle; |
35 | int card; |
36 | struct pollfd pollfd; |
37 | int num_ctls; |
38 | snd_ctl_elem_list_t *ctls; |
39 | struct card_data *next; |
40 | }; |
41 | |
42 | struct ctl_data { |
43 | const char *name; |
44 | snd_ctl_elem_id_t *id; |
45 | snd_ctl_elem_info_t *info; |
46 | snd_ctl_elem_value_t *def_val; |
47 | int elem; |
48 | int event_missing; |
49 | int event_spurious; |
50 | struct card_data *card; |
51 | struct ctl_data *next; |
52 | }; |
53 | |
54 | int num_cards = 0; |
55 | int num_controls = 0; |
56 | struct card_data *card_list = NULL; |
57 | struct ctl_data *ctl_list = NULL; |
58 | |
59 | static void find_controls(void) |
60 | { |
61 | char name[32]; |
62 | int card, ctl, err; |
63 | struct card_data *card_data; |
64 | struct ctl_data *ctl_data; |
65 | snd_config_t *config; |
66 | char *card_name, *card_longname; |
67 | |
68 | card = -1; |
69 | if (snd_card_next(&card) < 0 || card < 0) |
70 | return; |
71 | |
72 | config = get_alsalib_config(); |
73 | |
74 | while (card >= 0) { |
75 | sprintf(name, "hw:%d" , card); |
76 | |
77 | card_data = malloc(sizeof(*card_data)); |
78 | if (!card_data) |
79 | ksft_exit_fail_msg(msg: "Out of memory\n" ); |
80 | |
81 | err = snd_ctl_open_lconf(&card_data->handle, name, 0, config); |
82 | if (err < 0) { |
83 | ksft_print_msg(msg: "Failed to get hctl for card %d: %s\n" , |
84 | card, snd_strerror(err)); |
85 | goto next_card; |
86 | } |
87 | |
88 | err = snd_card_get_name(card, &card_name); |
89 | if (err != 0) |
90 | card_name = "Unknown" ; |
91 | err = snd_card_get_longname(card, &card_longname); |
92 | if (err != 0) |
93 | card_longname = "Unknown" ; |
94 | ksft_print_msg(msg: "Card %d - %s (%s)\n" , card, |
95 | card_name, card_longname); |
96 | |
97 | /* Count controls */ |
98 | snd_ctl_elem_list_malloc(&card_data->ctls); |
99 | snd_ctl_elem_list(card_data->handle, card_data->ctls); |
100 | card_data->num_ctls = snd_ctl_elem_list_get_count(card_data->ctls); |
101 | |
102 | /* Enumerate control information */ |
103 | snd_ctl_elem_list_alloc_space(card_data->ctls, card_data->num_ctls); |
104 | snd_ctl_elem_list(card_data->handle, card_data->ctls); |
105 | |
106 | card_data->card = num_cards++; |
107 | card_data->next = card_list; |
108 | card_list = card_data; |
109 | |
110 | num_controls += card_data->num_ctls; |
111 | |
112 | for (ctl = 0; ctl < card_data->num_ctls; ctl++) { |
113 | ctl_data = malloc(sizeof(*ctl_data)); |
114 | if (!ctl_data) |
115 | ksft_exit_fail_msg(msg: "Out of memory\n" ); |
116 | |
117 | memset(ctl_data, 0, sizeof(*ctl_data)); |
118 | ctl_data->card = card_data; |
119 | ctl_data->elem = ctl; |
120 | ctl_data->name = snd_ctl_elem_list_get_name(card_data->ctls, |
121 | ctl); |
122 | |
123 | err = snd_ctl_elem_id_malloc(&ctl_data->id); |
124 | if (err < 0) |
125 | ksft_exit_fail_msg(msg: "Out of memory\n" ); |
126 | |
127 | err = snd_ctl_elem_info_malloc(&ctl_data->info); |
128 | if (err < 0) |
129 | ksft_exit_fail_msg(msg: "Out of memory\n" ); |
130 | |
131 | err = snd_ctl_elem_value_malloc(&ctl_data->def_val); |
132 | if (err < 0) |
133 | ksft_exit_fail_msg(msg: "Out of memory\n" ); |
134 | |
135 | snd_ctl_elem_list_get_id(card_data->ctls, ctl, |
136 | ctl_data->id); |
137 | snd_ctl_elem_info_set_id(ctl_data->info, ctl_data->id); |
138 | err = snd_ctl_elem_info(card_data->handle, |
139 | ctl_data->info); |
140 | if (err < 0) { |
141 | ksft_print_msg(msg: "%s getting info for %s\n" , |
142 | snd_strerror(err), |
143 | ctl_data->name); |
144 | } |
145 | |
146 | snd_ctl_elem_value_set_id(ctl_data->def_val, |
147 | ctl_data->id); |
148 | |
149 | ctl_data->next = ctl_list; |
150 | ctl_list = ctl_data; |
151 | } |
152 | |
153 | /* Set up for events */ |
154 | err = snd_ctl_subscribe_events(card_data->handle, true); |
155 | if (err < 0) { |
156 | ksft_exit_fail_msg(msg: "snd_ctl_subscribe_events() failed for card %d: %d\n" , |
157 | card, err); |
158 | } |
159 | |
160 | err = snd_ctl_poll_descriptors_count(card_data->handle); |
161 | if (err != 1) { |
162 | ksft_exit_fail_msg(msg: "Unexpected descriptor count %d for card %d\n" , |
163 | err, card); |
164 | } |
165 | |
166 | err = snd_ctl_poll_descriptors(card_data->handle, |
167 | &card_data->pollfd, 1); |
168 | if (err != 1) { |
169 | ksft_exit_fail_msg(msg: "snd_ctl_poll_descriptors() failed for card %d: %d\n" , |
170 | card, err); |
171 | } |
172 | |
173 | next_card: |
174 | if (snd_card_next(&card) < 0) { |
175 | ksft_print_msg(msg: "snd_card_next" ); |
176 | break; |
177 | } |
178 | } |
179 | |
180 | snd_config_delete(config); |
181 | } |
182 | |
183 | /* |
184 | * Block for up to timeout ms for an event, returns a negative value |
185 | * on error, 0 for no event and 1 for an event. |
186 | */ |
187 | static int wait_for_event(struct ctl_data *ctl, int timeout) |
188 | { |
189 | unsigned short revents; |
190 | snd_ctl_event_t *event; |
191 | int err; |
192 | unsigned int mask = 0; |
193 | unsigned int ev_id; |
194 | |
195 | snd_ctl_event_alloca(&event); |
196 | |
197 | do { |
198 | err = poll(&(ctl->card->pollfd), 1, timeout); |
199 | if (err < 0) { |
200 | ksft_print_msg("poll() failed for %s: %s (%d)\n" , |
201 | ctl->name, strerror(errno), errno); |
202 | return -1; |
203 | } |
204 | /* Timeout */ |
205 | if (err == 0) |
206 | return 0; |
207 | |
208 | err = snd_ctl_poll_descriptors_revents(ctl->card->handle, |
209 | &(ctl->card->pollfd), |
210 | 1, &revents); |
211 | if (err < 0) { |
212 | ksft_print_msg(msg: "snd_ctl_poll_descriptors_revents() failed for %s: %d\n" , |
213 | ctl->name, err); |
214 | return err; |
215 | } |
216 | if (revents & POLLERR) { |
217 | ksft_print_msg(msg: "snd_ctl_poll_descriptors_revents() reported POLLERR for %s\n" , |
218 | ctl->name); |
219 | return -1; |
220 | } |
221 | /* No read events */ |
222 | if (!(revents & POLLIN)) { |
223 | ksft_print_msg(msg: "No POLLIN\n" ); |
224 | continue; |
225 | } |
226 | |
227 | err = snd_ctl_read(ctl->card->handle, event); |
228 | if (err < 0) { |
229 | ksft_print_msg(msg: "snd_ctl_read() failed for %s: %d\n" , |
230 | ctl->name, err); |
231 | return err; |
232 | } |
233 | |
234 | if (snd_ctl_event_get_type(event) != SND_CTL_EVENT_ELEM) |
235 | continue; |
236 | |
237 | /* The ID returned from the event is 1 less than numid */ |
238 | mask = snd_ctl_event_elem_get_mask(event); |
239 | ev_id = snd_ctl_event_elem_get_numid(event); |
240 | if (ev_id != snd_ctl_elem_info_get_numid(ctl->info)) { |
241 | ksft_print_msg("Event for unexpected ctl %s\n" , |
242 | snd_ctl_event_elem_get_name(event)); |
243 | continue; |
244 | } |
245 | |
246 | if ((mask & SND_CTL_EVENT_MASK_REMOVE) == SND_CTL_EVENT_MASK_REMOVE) { |
247 | ksft_print_msg(msg: "Removal event for %s\n" , |
248 | ctl->name); |
249 | return -1; |
250 | } |
251 | } while ((mask & SND_CTL_EVENT_MASK_VALUE) != SND_CTL_EVENT_MASK_VALUE); |
252 | |
253 | return 1; |
254 | } |
255 | |
256 | static bool ctl_value_index_valid(struct ctl_data *ctl, |
257 | snd_ctl_elem_value_t *val, |
258 | int index) |
259 | { |
260 | long int_val; |
261 | long long int64_val; |
262 | |
263 | switch (snd_ctl_elem_info_get_type(ctl->info)) { |
264 | case SND_CTL_ELEM_TYPE_NONE: |
265 | ksft_print_msg(msg: "%s.%d Invalid control type NONE\n" , |
266 | ctl->name, index); |
267 | return false; |
268 | |
269 | case SND_CTL_ELEM_TYPE_BOOLEAN: |
270 | int_val = snd_ctl_elem_value_get_boolean(val, index); |
271 | switch (int_val) { |
272 | case 0: |
273 | case 1: |
274 | break; |
275 | default: |
276 | ksft_print_msg(msg: "%s.%d Invalid boolean value %ld\n" , |
277 | ctl->name, index, int_val); |
278 | return false; |
279 | } |
280 | break; |
281 | |
282 | case SND_CTL_ELEM_TYPE_INTEGER: |
283 | int_val = snd_ctl_elem_value_get_integer(val, index); |
284 | |
285 | if (int_val < snd_ctl_elem_info_get_min(ctl->info)) { |
286 | ksft_print_msg(msg: "%s.%d value %ld less than minimum %ld\n" , |
287 | ctl->name, index, int_val, |
288 | snd_ctl_elem_info_get_min(ctl->info)); |
289 | return false; |
290 | } |
291 | |
292 | if (int_val > snd_ctl_elem_info_get_max(ctl->info)) { |
293 | ksft_print_msg(msg: "%s.%d value %ld more than maximum %ld\n" , |
294 | ctl->name, index, int_val, |
295 | snd_ctl_elem_info_get_max(ctl->info)); |
296 | return false; |
297 | } |
298 | |
299 | /* Only check step size if there is one and we're in bounds */ |
300 | if (snd_ctl_elem_info_get_step(ctl->info) && |
301 | (int_val - snd_ctl_elem_info_get_min(ctl->info) % |
302 | snd_ctl_elem_info_get_step(ctl->info))) { |
303 | ksft_print_msg(msg: "%s.%d value %ld invalid for step %ld minimum %ld\n" , |
304 | ctl->name, index, int_val, |
305 | snd_ctl_elem_info_get_step(ctl->info), |
306 | snd_ctl_elem_info_get_min(ctl->info)); |
307 | return false; |
308 | } |
309 | break; |
310 | |
311 | case SND_CTL_ELEM_TYPE_INTEGER64: |
312 | int64_val = snd_ctl_elem_value_get_integer64(val, index); |
313 | |
314 | if (int64_val < snd_ctl_elem_info_get_min64(ctl->info)) { |
315 | ksft_print_msg(msg: "%s.%d value %lld less than minimum %lld\n" , |
316 | ctl->name, index, int64_val, |
317 | snd_ctl_elem_info_get_min64(ctl->info)); |
318 | return false; |
319 | } |
320 | |
321 | if (int64_val > snd_ctl_elem_info_get_max64(ctl->info)) { |
322 | ksft_print_msg(msg: "%s.%d value %lld more than maximum %ld\n" , |
323 | ctl->name, index, int64_val, |
324 | snd_ctl_elem_info_get_max(ctl->info)); |
325 | return false; |
326 | } |
327 | |
328 | /* Only check step size if there is one and we're in bounds */ |
329 | if (snd_ctl_elem_info_get_step64(ctl->info) && |
330 | (int64_val - snd_ctl_elem_info_get_min64(ctl->info)) % |
331 | snd_ctl_elem_info_get_step64(ctl->info)) { |
332 | ksft_print_msg(msg: "%s.%d value %lld invalid for step %lld minimum %lld\n" , |
333 | ctl->name, index, int64_val, |
334 | snd_ctl_elem_info_get_step64(ctl->info), |
335 | snd_ctl_elem_info_get_min64(ctl->info)); |
336 | return false; |
337 | } |
338 | break; |
339 | |
340 | case SND_CTL_ELEM_TYPE_ENUMERATED: |
341 | int_val = snd_ctl_elem_value_get_enumerated(val, index); |
342 | |
343 | if (int_val < 0) { |
344 | ksft_print_msg(msg: "%s.%d negative value %ld for enumeration\n" , |
345 | ctl->name, index, int_val); |
346 | return false; |
347 | } |
348 | |
349 | if (int_val >= snd_ctl_elem_info_get_items(ctl->info)) { |
350 | ksft_print_msg(msg: "%s.%d value %ld more than item count %u\n" , |
351 | ctl->name, index, int_val, |
352 | snd_ctl_elem_info_get_items(ctl->info)); |
353 | return false; |
354 | } |
355 | break; |
356 | |
357 | default: |
358 | /* No tests for other types */ |
359 | break; |
360 | } |
361 | |
362 | return true; |
363 | } |
364 | |
365 | /* |
366 | * Check that the provided value meets the constraints for the |
367 | * provided control. |
368 | */ |
369 | static bool ctl_value_valid(struct ctl_data *ctl, snd_ctl_elem_value_t *val) |
370 | { |
371 | int i; |
372 | bool valid = true; |
373 | |
374 | for (i = 0; i < snd_ctl_elem_info_get_count(ctl->info); i++) |
375 | if (!ctl_value_index_valid(ctl, val, i)) |
376 | valid = false; |
377 | |
378 | return valid; |
379 | } |
380 | |
381 | /* |
382 | * Check that we can read the default value and it is valid. Write |
383 | * tests use the read value to restore the default. |
384 | */ |
385 | static void test_ctl_get_value(struct ctl_data *ctl) |
386 | { |
387 | int err; |
388 | |
389 | /* If the control is turned off let's be polite */ |
390 | if (snd_ctl_elem_info_is_inactive(ctl->info)) { |
391 | ksft_print_msg(msg: "%s is inactive\n" , ctl->name); |
392 | ksft_test_result_skip(msg: "get_value.%d.%d\n" , |
393 | ctl->card->card, ctl->elem); |
394 | return; |
395 | } |
396 | |
397 | /* Can't test reading on an unreadable control */ |
398 | if (!snd_ctl_elem_info_is_readable(ctl->info)) { |
399 | ksft_print_msg(msg: "%s is not readable\n" , ctl->name); |
400 | ksft_test_result_skip(msg: "get_value.%d.%d\n" , |
401 | ctl->card->card, ctl->elem); |
402 | return; |
403 | } |
404 | |
405 | err = snd_ctl_elem_read(ctl->card->handle, ctl->def_val); |
406 | if (err < 0) { |
407 | ksft_print_msg(msg: "snd_ctl_elem_read() failed: %s\n" , |
408 | snd_strerror(err)); |
409 | goto out; |
410 | } |
411 | |
412 | if (!ctl_value_valid(ctl, ctl->def_val)) |
413 | err = -EINVAL; |
414 | |
415 | out: |
416 | ksft_test_result(err >= 0, "get_value.%d.%d\n" , |
417 | ctl->card->card, ctl->elem); |
418 | } |
419 | |
420 | static bool strend(const char *haystack, const char *needle) |
421 | { |
422 | size_t haystack_len = strlen(haystack); |
423 | size_t needle_len = strlen(needle); |
424 | |
425 | if (needle_len > haystack_len) |
426 | return false; |
427 | return strcmp(haystack + haystack_len - needle_len, needle) == 0; |
428 | } |
429 | |
430 | static void test_ctl_name(struct ctl_data *ctl) |
431 | { |
432 | bool name_ok = true; |
433 | |
434 | ksft_print_msg(msg: "%d.%d %s\n" , ctl->card->card, ctl->elem, |
435 | ctl->name); |
436 | |
437 | /* Only boolean controls should end in Switch */ |
438 | if (strend(ctl->name, " Switch" )) { |
439 | if (snd_ctl_elem_info_get_type(ctl->info) != SND_CTL_ELEM_TYPE_BOOLEAN) { |
440 | ksft_print_msg(msg: "%d.%d %s ends in Switch but is not boolean\n" , |
441 | ctl->card->card, ctl->elem, ctl->name); |
442 | name_ok = false; |
443 | } |
444 | } |
445 | |
446 | /* Writeable boolean controls should end in Switch */ |
447 | if (snd_ctl_elem_info_get_type(ctl->info) == SND_CTL_ELEM_TYPE_BOOLEAN && |
448 | snd_ctl_elem_info_is_writable(ctl->info)) { |
449 | if (!strend(ctl->name, " Switch" )) { |
450 | ksft_print_msg(msg: "%d.%d %s is a writeable boolean but not a Switch\n" , |
451 | ctl->card->card, ctl->elem, ctl->name); |
452 | name_ok = false; |
453 | } |
454 | } |
455 | |
456 | ksft_test_result(name_ok, "name.%d.%d\n" , |
457 | ctl->card->card, ctl->elem); |
458 | } |
459 | |
460 | static void show_values(struct ctl_data *ctl, snd_ctl_elem_value_t *orig_val, |
461 | snd_ctl_elem_value_t *read_val) |
462 | { |
463 | long long orig_int, read_int; |
464 | int i; |
465 | |
466 | for (i = 0; i < snd_ctl_elem_info_get_count(ctl->info); i++) { |
467 | switch (snd_ctl_elem_info_get_type(ctl->info)) { |
468 | case SND_CTL_ELEM_TYPE_BOOLEAN: |
469 | orig_int = snd_ctl_elem_value_get_boolean(orig_val, i); |
470 | read_int = snd_ctl_elem_value_get_boolean(read_val, i); |
471 | break; |
472 | |
473 | case SND_CTL_ELEM_TYPE_INTEGER: |
474 | orig_int = snd_ctl_elem_value_get_integer(orig_val, i); |
475 | read_int = snd_ctl_elem_value_get_integer(read_val, i); |
476 | break; |
477 | |
478 | case SND_CTL_ELEM_TYPE_INTEGER64: |
479 | orig_int = snd_ctl_elem_value_get_integer64(orig_val, |
480 | i); |
481 | read_int = snd_ctl_elem_value_get_integer64(read_val, |
482 | i); |
483 | break; |
484 | |
485 | case SND_CTL_ELEM_TYPE_ENUMERATED: |
486 | orig_int = snd_ctl_elem_value_get_enumerated(orig_val, |
487 | i); |
488 | read_int = snd_ctl_elem_value_get_enumerated(read_val, |
489 | i); |
490 | break; |
491 | |
492 | default: |
493 | return; |
494 | } |
495 | |
496 | ksft_print_msg(msg: "%s.%d orig %lld read %lld, is_volatile %d\n" , |
497 | ctl->name, i, orig_int, read_int, |
498 | snd_ctl_elem_info_is_volatile(ctl->info)); |
499 | } |
500 | } |
501 | |
502 | static bool show_mismatch(struct ctl_data *ctl, int index, |
503 | snd_ctl_elem_value_t *read_val, |
504 | snd_ctl_elem_value_t *expected_val) |
505 | { |
506 | long long expected_int, read_int; |
507 | |
508 | /* |
509 | * We factor out the code to compare values representable as |
510 | * integers, ensure that check doesn't log otherwise. |
511 | */ |
512 | expected_int = 0; |
513 | read_int = 0; |
514 | |
515 | switch (snd_ctl_elem_info_get_type(ctl->info)) { |
516 | case SND_CTL_ELEM_TYPE_BOOLEAN: |
517 | expected_int = snd_ctl_elem_value_get_boolean(expected_val, |
518 | index); |
519 | read_int = snd_ctl_elem_value_get_boolean(read_val, index); |
520 | break; |
521 | |
522 | case SND_CTL_ELEM_TYPE_INTEGER: |
523 | expected_int = snd_ctl_elem_value_get_integer(expected_val, |
524 | index); |
525 | read_int = snd_ctl_elem_value_get_integer(read_val, index); |
526 | break; |
527 | |
528 | case SND_CTL_ELEM_TYPE_INTEGER64: |
529 | expected_int = snd_ctl_elem_value_get_integer64(expected_val, |
530 | index); |
531 | read_int = snd_ctl_elem_value_get_integer64(read_val, |
532 | index); |
533 | break; |
534 | |
535 | case SND_CTL_ELEM_TYPE_ENUMERATED: |
536 | expected_int = snd_ctl_elem_value_get_enumerated(expected_val, |
537 | index); |
538 | read_int = snd_ctl_elem_value_get_enumerated(read_val, |
539 | index); |
540 | break; |
541 | |
542 | default: |
543 | break; |
544 | } |
545 | |
546 | if (expected_int != read_int) { |
547 | /* |
548 | * NOTE: The volatile attribute means that the hardware |
549 | * can voluntarily change the state of control element |
550 | * independent of any operation by software. |
551 | */ |
552 | bool is_volatile = snd_ctl_elem_info_is_volatile(ctl->info); |
553 | ksft_print_msg("%s.%d expected %lld but read %lld, is_volatile %d\n" , |
554 | ctl->name, index, expected_int, read_int, is_volatile); |
555 | return !is_volatile; |
556 | } else { |
557 | return false; |
558 | } |
559 | } |
560 | |
561 | /* |
562 | * Write a value then if possible verify that we get the expected |
563 | * result. An optional expected value can be provided if we expect |
564 | * the write to fail, for verifying that invalid writes don't corrupt |
565 | * anything. |
566 | */ |
567 | static int write_and_verify(struct ctl_data *ctl, |
568 | snd_ctl_elem_value_t *write_val, |
569 | snd_ctl_elem_value_t *expected_val) |
570 | { |
571 | int err, i; |
572 | bool error_expected, mismatch_shown; |
573 | snd_ctl_elem_value_t *initial_val, *read_val, *w_val; |
574 | snd_ctl_elem_value_alloca(&initial_val); |
575 | snd_ctl_elem_value_alloca(&read_val); |
576 | snd_ctl_elem_value_alloca(&w_val); |
577 | |
578 | /* |
579 | * We need to copy the write value since writing can modify |
580 | * the value which causes surprises, and allocate an expected |
581 | * value if we expect to read back what we wrote. |
582 | */ |
583 | snd_ctl_elem_value_copy(w_val, write_val); |
584 | if (expected_val) { |
585 | error_expected = true; |
586 | } else { |
587 | error_expected = false; |
588 | snd_ctl_elem_value_alloca(&expected_val); |
589 | snd_ctl_elem_value_copy(expected_val, write_val); |
590 | } |
591 | |
592 | /* Store the value before we write */ |
593 | if (snd_ctl_elem_info_is_readable(ctl->info)) { |
594 | snd_ctl_elem_value_set_id(initial_val, ctl->id); |
595 | |
596 | err = snd_ctl_elem_read(ctl->card->handle, initial_val); |
597 | if (err < 0) { |
598 | ksft_print_msg(msg: "snd_ctl_elem_read() failed: %s\n" , |
599 | snd_strerror(err)); |
600 | return err; |
601 | } |
602 | } |
603 | |
604 | /* |
605 | * Do the write, if we have an expected value ignore the error |
606 | * and carry on to validate the expected value. |
607 | */ |
608 | err = snd_ctl_elem_write(ctl->card->handle, w_val); |
609 | if (err < 0 && !error_expected) { |
610 | ksft_print_msg(msg: "snd_ctl_elem_write() failed: %s\n" , |
611 | snd_strerror(err)); |
612 | return err; |
613 | } |
614 | |
615 | /* Can we do the verification part? */ |
616 | if (!snd_ctl_elem_info_is_readable(ctl->info)) |
617 | return err; |
618 | |
619 | snd_ctl_elem_value_set_id(read_val, ctl->id); |
620 | |
621 | err = snd_ctl_elem_read(ctl->card->handle, read_val); |
622 | if (err < 0) { |
623 | ksft_print_msg(msg: "snd_ctl_elem_read() failed: %s\n" , |
624 | snd_strerror(err)); |
625 | return err; |
626 | } |
627 | |
628 | /* |
629 | * Check for an event if the value changed, or confirm that |
630 | * there was none if it didn't. We rely on the kernel |
631 | * generating the notification before it returns from the |
632 | * write, this is currently true, should that ever change this |
633 | * will most likely break and need updating. |
634 | */ |
635 | if (!snd_ctl_elem_info_is_volatile(ctl->info)) { |
636 | err = wait_for_event(ctl, timeout: 0); |
637 | if (snd_ctl_elem_value_compare(initial_val, read_val)) { |
638 | if (err < 1) { |
639 | ksft_print_msg(msg: "No event generated for %s\n" , |
640 | ctl->name); |
641 | show_values(ctl, initial_val, read_val); |
642 | ctl->event_missing++; |
643 | } |
644 | } else { |
645 | if (err != 0) { |
646 | ksft_print_msg(msg: "Spurious event generated for %s\n" , |
647 | ctl->name); |
648 | show_values(ctl, initial_val, read_val); |
649 | ctl->event_spurious++; |
650 | } |
651 | } |
652 | } |
653 | |
654 | /* |
655 | * Use the libray to compare values, if there's a mismatch |
656 | * carry on and try to provide a more useful diagnostic than |
657 | * just "mismatch". |
658 | */ |
659 | if (!snd_ctl_elem_value_compare(expected_val, read_val)) |
660 | return 0; |
661 | |
662 | mismatch_shown = false; |
663 | for (i = 0; i < snd_ctl_elem_info_get_count(ctl->info); i++) |
664 | if (show_mismatch(ctl, i, read_val, expected_val)) |
665 | mismatch_shown = true; |
666 | |
667 | if (!mismatch_shown) |
668 | ksft_print_msg(msg: "%s read and written values differ\n" , |
669 | ctl->name); |
670 | |
671 | return -1; |
672 | } |
673 | |
674 | /* |
675 | * Make sure we can write the default value back to the control, this |
676 | * should validate that at least some write works. |
677 | */ |
678 | static void test_ctl_write_default(struct ctl_data *ctl) |
679 | { |
680 | int err; |
681 | |
682 | /* If the control is turned off let's be polite */ |
683 | if (snd_ctl_elem_info_is_inactive(ctl->info)) { |
684 | ksft_print_msg(msg: "%s is inactive\n" , ctl->name); |
685 | ksft_test_result_skip(msg: "write_default.%d.%d\n" , |
686 | ctl->card->card, ctl->elem); |
687 | return; |
688 | } |
689 | |
690 | if (!snd_ctl_elem_info_is_writable(ctl->info)) { |
691 | ksft_print_msg(msg: "%s is not writeable\n" , ctl->name); |
692 | ksft_test_result_skip(msg: "write_default.%d.%d\n" , |
693 | ctl->card->card, ctl->elem); |
694 | return; |
695 | } |
696 | |
697 | /* No idea what the default was for unreadable controls */ |
698 | if (!snd_ctl_elem_info_is_readable(ctl->info)) { |
699 | ksft_print_msg(msg: "%s couldn't read default\n" , ctl->name); |
700 | ksft_test_result_skip(msg: "write_default.%d.%d\n" , |
701 | ctl->card->card, ctl->elem); |
702 | return; |
703 | } |
704 | |
705 | err = write_and_verify(ctl, ctl->def_val, NULL); |
706 | |
707 | ksft_test_result(err >= 0, "write_default.%d.%d\n" , |
708 | ctl->card->card, ctl->elem); |
709 | } |
710 | |
711 | static bool test_ctl_write_valid_boolean(struct ctl_data *ctl) |
712 | { |
713 | int err, i, j; |
714 | bool fail = false; |
715 | snd_ctl_elem_value_t *val; |
716 | snd_ctl_elem_value_alloca(&val); |
717 | |
718 | snd_ctl_elem_value_set_id(val, ctl->id); |
719 | |
720 | for (i = 0; i < snd_ctl_elem_info_get_count(ctl->info); i++) { |
721 | for (j = 0; j < 2; j++) { |
722 | snd_ctl_elem_value_set_boolean(val, i, j); |
723 | err = write_and_verify(ctl, val, NULL); |
724 | if (err != 0) |
725 | fail = true; |
726 | } |
727 | } |
728 | |
729 | return !fail; |
730 | } |
731 | |
732 | static bool test_ctl_write_valid_integer(struct ctl_data *ctl) |
733 | { |
734 | int err; |
735 | int i; |
736 | long j, step; |
737 | bool fail = false; |
738 | snd_ctl_elem_value_t *val; |
739 | snd_ctl_elem_value_alloca(&val); |
740 | |
741 | snd_ctl_elem_value_set_id(val, ctl->id); |
742 | |
743 | step = snd_ctl_elem_info_get_step(ctl->info); |
744 | if (!step) |
745 | step = 1; |
746 | |
747 | for (i = 0; i < snd_ctl_elem_info_get_count(ctl->info); i++) { |
748 | for (j = snd_ctl_elem_info_get_min(ctl->info); |
749 | j <= snd_ctl_elem_info_get_max(ctl->info); j += step) { |
750 | |
751 | snd_ctl_elem_value_set_integer(val, i, j); |
752 | err = write_and_verify(ctl, val, NULL); |
753 | if (err != 0) |
754 | fail = true; |
755 | } |
756 | } |
757 | |
758 | |
759 | return !fail; |
760 | } |
761 | |
762 | static bool test_ctl_write_valid_integer64(struct ctl_data *ctl) |
763 | { |
764 | int err, i; |
765 | long long j, step; |
766 | bool fail = false; |
767 | snd_ctl_elem_value_t *val; |
768 | snd_ctl_elem_value_alloca(&val); |
769 | |
770 | snd_ctl_elem_value_set_id(val, ctl->id); |
771 | |
772 | step = snd_ctl_elem_info_get_step64(ctl->info); |
773 | if (!step) |
774 | step = 1; |
775 | |
776 | for (i = 0; i < snd_ctl_elem_info_get_count(ctl->info); i++) { |
777 | for (j = snd_ctl_elem_info_get_min64(ctl->info); |
778 | j <= snd_ctl_elem_info_get_max64(ctl->info); j += step) { |
779 | |
780 | snd_ctl_elem_value_set_integer64(val, i, j); |
781 | err = write_and_verify(ctl, val, NULL); |
782 | if (err != 0) |
783 | fail = true; |
784 | } |
785 | } |
786 | |
787 | return !fail; |
788 | } |
789 | |
790 | static bool test_ctl_write_valid_enumerated(struct ctl_data *ctl) |
791 | { |
792 | int err, i, j; |
793 | bool fail = false; |
794 | snd_ctl_elem_value_t *val; |
795 | snd_ctl_elem_value_alloca(&val); |
796 | |
797 | snd_ctl_elem_value_set_id(val, ctl->id); |
798 | |
799 | for (i = 0; i < snd_ctl_elem_info_get_count(ctl->info); i++) { |
800 | for (j = 0; j < snd_ctl_elem_info_get_items(ctl->info); j++) { |
801 | snd_ctl_elem_value_set_enumerated(val, i, j); |
802 | err = write_and_verify(ctl, val, NULL); |
803 | if (err != 0) |
804 | fail = true; |
805 | } |
806 | } |
807 | |
808 | return !fail; |
809 | } |
810 | |
811 | static void test_ctl_write_valid(struct ctl_data *ctl) |
812 | { |
813 | bool pass; |
814 | |
815 | /* If the control is turned off let's be polite */ |
816 | if (snd_ctl_elem_info_is_inactive(ctl->info)) { |
817 | ksft_print_msg(msg: "%s is inactive\n" , ctl->name); |
818 | ksft_test_result_skip(msg: "write_valid.%d.%d\n" , |
819 | ctl->card->card, ctl->elem); |
820 | return; |
821 | } |
822 | |
823 | if (!snd_ctl_elem_info_is_writable(ctl->info)) { |
824 | ksft_print_msg(msg: "%s is not writeable\n" , ctl->name); |
825 | ksft_test_result_skip(msg: "write_valid.%d.%d\n" , |
826 | ctl->card->card, ctl->elem); |
827 | return; |
828 | } |
829 | |
830 | switch (snd_ctl_elem_info_get_type(ctl->info)) { |
831 | case SND_CTL_ELEM_TYPE_BOOLEAN: |
832 | pass = test_ctl_write_valid_boolean(ctl); |
833 | break; |
834 | |
835 | case SND_CTL_ELEM_TYPE_INTEGER: |
836 | pass = test_ctl_write_valid_integer(ctl); |
837 | break; |
838 | |
839 | case SND_CTL_ELEM_TYPE_INTEGER64: |
840 | pass = test_ctl_write_valid_integer64(ctl); |
841 | break; |
842 | |
843 | case SND_CTL_ELEM_TYPE_ENUMERATED: |
844 | pass = test_ctl_write_valid_enumerated(ctl); |
845 | break; |
846 | |
847 | default: |
848 | /* No tests for this yet */ |
849 | ksft_test_result_skip(msg: "write_valid.%d.%d\n" , |
850 | ctl->card->card, ctl->elem); |
851 | return; |
852 | } |
853 | |
854 | /* Restore the default value to minimise disruption */ |
855 | write_and_verify(ctl, ctl->def_val, NULL); |
856 | |
857 | ksft_test_result(pass, "write_valid.%d.%d\n" , |
858 | ctl->card->card, ctl->elem); |
859 | } |
860 | |
861 | static bool test_ctl_write_invalid_value(struct ctl_data *ctl, |
862 | snd_ctl_elem_value_t *val) |
863 | { |
864 | int err; |
865 | |
866 | /* Ideally this will fail... */ |
867 | err = snd_ctl_elem_write(ctl->card->handle, val); |
868 | if (err < 0) |
869 | return false; |
870 | |
871 | /* ...but some devices will clamp to an in range value */ |
872 | err = snd_ctl_elem_read(ctl->card->handle, val); |
873 | if (err < 0) { |
874 | ksft_print_msg(msg: "%s failed to read: %s\n" , |
875 | ctl->name, snd_strerror(err)); |
876 | return true; |
877 | } |
878 | |
879 | return !ctl_value_valid(ctl, val); |
880 | } |
881 | |
882 | static bool test_ctl_write_invalid_boolean(struct ctl_data *ctl) |
883 | { |
884 | int i; |
885 | bool fail = false; |
886 | snd_ctl_elem_value_t *val; |
887 | snd_ctl_elem_value_alloca(&val); |
888 | |
889 | for (i = 0; i < snd_ctl_elem_info_get_count(ctl->info); i++) { |
890 | snd_ctl_elem_value_copy(val, ctl->def_val); |
891 | snd_ctl_elem_value_set_boolean(val, i, 2); |
892 | |
893 | if (test_ctl_write_invalid_value(ctl, val)) |
894 | fail = true; |
895 | } |
896 | |
897 | return !fail; |
898 | } |
899 | |
900 | static bool test_ctl_write_invalid_integer(struct ctl_data *ctl) |
901 | { |
902 | int i; |
903 | bool fail = false; |
904 | snd_ctl_elem_value_t *val; |
905 | snd_ctl_elem_value_alloca(&val); |
906 | |
907 | for (i = 0; i < snd_ctl_elem_info_get_count(ctl->info); i++) { |
908 | if (snd_ctl_elem_info_get_min(ctl->info) != LONG_MIN) { |
909 | /* Just under range */ |
910 | snd_ctl_elem_value_copy(val, ctl->def_val); |
911 | snd_ctl_elem_value_set_integer(val, i, |
912 | snd_ctl_elem_info_get_min(ctl->info) - 1); |
913 | |
914 | if (test_ctl_write_invalid_value(ctl, val)) |
915 | fail = true; |
916 | |
917 | /* Minimum representable value */ |
918 | snd_ctl_elem_value_copy(val, ctl->def_val); |
919 | snd_ctl_elem_value_set_integer(val, i, LONG_MIN); |
920 | |
921 | if (test_ctl_write_invalid_value(ctl, val)) |
922 | fail = true; |
923 | } |
924 | |
925 | if (snd_ctl_elem_info_get_max(ctl->info) != LONG_MAX) { |
926 | /* Just over range */ |
927 | snd_ctl_elem_value_copy(val, ctl->def_val); |
928 | snd_ctl_elem_value_set_integer(val, i, |
929 | snd_ctl_elem_info_get_max(ctl->info) + 1); |
930 | |
931 | if (test_ctl_write_invalid_value(ctl, val)) |
932 | fail = true; |
933 | |
934 | /* Maximum representable value */ |
935 | snd_ctl_elem_value_copy(val, ctl->def_val); |
936 | snd_ctl_elem_value_set_integer(val, i, LONG_MAX); |
937 | |
938 | if (test_ctl_write_invalid_value(ctl, val)) |
939 | fail = true; |
940 | } |
941 | } |
942 | |
943 | return !fail; |
944 | } |
945 | |
946 | static bool test_ctl_write_invalid_integer64(struct ctl_data *ctl) |
947 | { |
948 | int i; |
949 | bool fail = false; |
950 | snd_ctl_elem_value_t *val; |
951 | snd_ctl_elem_value_alloca(&val); |
952 | |
953 | for (i = 0; i < snd_ctl_elem_info_get_count(ctl->info); i++) { |
954 | if (snd_ctl_elem_info_get_min64(ctl->info) != LLONG_MIN) { |
955 | /* Just under range */ |
956 | snd_ctl_elem_value_copy(val, ctl->def_val); |
957 | snd_ctl_elem_value_set_integer64(val, i, |
958 | snd_ctl_elem_info_get_min64(ctl->info) - 1); |
959 | |
960 | if (test_ctl_write_invalid_value(ctl, val)) |
961 | fail = true; |
962 | |
963 | /* Minimum representable value */ |
964 | snd_ctl_elem_value_copy(val, ctl->def_val); |
965 | snd_ctl_elem_value_set_integer64(val, i, LLONG_MIN); |
966 | |
967 | if (test_ctl_write_invalid_value(ctl, val)) |
968 | fail = true; |
969 | } |
970 | |
971 | if (snd_ctl_elem_info_get_max64(ctl->info) != LLONG_MAX) { |
972 | /* Just over range */ |
973 | snd_ctl_elem_value_copy(val, ctl->def_val); |
974 | snd_ctl_elem_value_set_integer64(val, i, |
975 | snd_ctl_elem_info_get_max64(ctl->info) + 1); |
976 | |
977 | if (test_ctl_write_invalid_value(ctl, val)) |
978 | fail = true; |
979 | |
980 | /* Maximum representable value */ |
981 | snd_ctl_elem_value_copy(val, ctl->def_val); |
982 | snd_ctl_elem_value_set_integer64(val, i, LLONG_MAX); |
983 | |
984 | if (test_ctl_write_invalid_value(ctl, val)) |
985 | fail = true; |
986 | } |
987 | } |
988 | |
989 | return !fail; |
990 | } |
991 | |
992 | static bool test_ctl_write_invalid_enumerated(struct ctl_data *ctl) |
993 | { |
994 | int i; |
995 | bool fail = false; |
996 | snd_ctl_elem_value_t *val; |
997 | snd_ctl_elem_value_alloca(&val); |
998 | |
999 | snd_ctl_elem_value_set_id(val, ctl->id); |
1000 | |
1001 | for (i = 0; i < snd_ctl_elem_info_get_count(ctl->info); i++) { |
1002 | /* One beyond maximum */ |
1003 | snd_ctl_elem_value_copy(val, ctl->def_val); |
1004 | snd_ctl_elem_value_set_enumerated(val, i, |
1005 | snd_ctl_elem_info_get_items(ctl->info)); |
1006 | |
1007 | if (test_ctl_write_invalid_value(ctl, val)) |
1008 | fail = true; |
1009 | |
1010 | /* Maximum representable value */ |
1011 | snd_ctl_elem_value_copy(val, ctl->def_val); |
1012 | snd_ctl_elem_value_set_enumerated(val, i, UINT_MAX); |
1013 | |
1014 | if (test_ctl_write_invalid_value(ctl, val)) |
1015 | fail = true; |
1016 | |
1017 | } |
1018 | |
1019 | return !fail; |
1020 | } |
1021 | |
1022 | |
1023 | static void test_ctl_write_invalid(struct ctl_data *ctl) |
1024 | { |
1025 | bool pass; |
1026 | |
1027 | /* If the control is turned off let's be polite */ |
1028 | if (snd_ctl_elem_info_is_inactive(ctl->info)) { |
1029 | ksft_print_msg(msg: "%s is inactive\n" , ctl->name); |
1030 | ksft_test_result_skip(msg: "write_invalid.%d.%d\n" , |
1031 | ctl->card->card, ctl->elem); |
1032 | return; |
1033 | } |
1034 | |
1035 | if (!snd_ctl_elem_info_is_writable(ctl->info)) { |
1036 | ksft_print_msg(msg: "%s is not writeable\n" , ctl->name); |
1037 | ksft_test_result_skip(msg: "write_invalid.%d.%d\n" , |
1038 | ctl->card->card, ctl->elem); |
1039 | return; |
1040 | } |
1041 | |
1042 | switch (snd_ctl_elem_info_get_type(ctl->info)) { |
1043 | case SND_CTL_ELEM_TYPE_BOOLEAN: |
1044 | pass = test_ctl_write_invalid_boolean(ctl); |
1045 | break; |
1046 | |
1047 | case SND_CTL_ELEM_TYPE_INTEGER: |
1048 | pass = test_ctl_write_invalid_integer(ctl); |
1049 | break; |
1050 | |
1051 | case SND_CTL_ELEM_TYPE_INTEGER64: |
1052 | pass = test_ctl_write_invalid_integer64(ctl); |
1053 | break; |
1054 | |
1055 | case SND_CTL_ELEM_TYPE_ENUMERATED: |
1056 | pass = test_ctl_write_invalid_enumerated(ctl); |
1057 | break; |
1058 | |
1059 | default: |
1060 | /* No tests for this yet */ |
1061 | ksft_test_result_skip(msg: "write_invalid.%d.%d\n" , |
1062 | ctl->card->card, ctl->elem); |
1063 | return; |
1064 | } |
1065 | |
1066 | /* Restore the default value to minimise disruption */ |
1067 | write_and_verify(ctl, ctl->def_val, NULL); |
1068 | |
1069 | ksft_test_result(pass, "write_invalid.%d.%d\n" , |
1070 | ctl->card->card, ctl->elem); |
1071 | } |
1072 | |
1073 | static void test_ctl_event_missing(struct ctl_data *ctl) |
1074 | { |
1075 | ksft_test_result(!ctl->event_missing, "event_missing.%d.%d\n" , |
1076 | ctl->card->card, ctl->elem); |
1077 | } |
1078 | |
1079 | static void test_ctl_event_spurious(struct ctl_data *ctl) |
1080 | { |
1081 | ksft_test_result(!ctl->event_spurious, "event_spurious.%d.%d\n" , |
1082 | ctl->card->card, ctl->elem); |
1083 | } |
1084 | |
1085 | int main(void) |
1086 | { |
1087 | struct ctl_data *ctl; |
1088 | |
1089 | ksft_print_header(); |
1090 | |
1091 | find_controls(); |
1092 | |
1093 | ksft_set_plan(plan: num_controls * TESTS_PER_CONTROL); |
1094 | |
1095 | for (ctl = ctl_list; ctl != NULL; ctl = ctl->next) { |
1096 | /* |
1097 | * Must test get_value() before we write anything, the |
1098 | * test stores the default value for later cleanup. |
1099 | */ |
1100 | test_ctl_get_value(ctl); |
1101 | test_ctl_name(ctl); |
1102 | test_ctl_write_default(ctl); |
1103 | test_ctl_write_valid(ctl); |
1104 | test_ctl_write_invalid(ctl); |
1105 | test_ctl_event_missing(ctl); |
1106 | test_ctl_event_spurious(ctl); |
1107 | } |
1108 | |
1109 | ksft_exit_pass(); |
1110 | |
1111 | return 0; |
1112 | } |
1113 | |