1 /*
2 * Copyright (c) 2019-2025 Allwinner Technology Co., Ltd. ALL rights reserved.
3 *
4 * Allwinner is a trademark of Allwinner Technology Co.,Ltd., registered in
5 * the the people's Republic of China and other countries.
6 * All Allwinner Technology Co.,Ltd. trademarks are used with permission.
7 *
8 * DISCLAIMER
9 * THIRD PARTY LICENCES MAY BE REQUIRED TO IMPLEMENT THE SOLUTION/PRODUCT.
10 * IF YOU NEED TO INTEGRATE THIRD PARTY’S TECHNOLOGY (SONY, DTS, DOLBY, AVS OR MPEGLA, ETC.)
11 * IN ALLWINNERS’SDK OR PRODUCTS, YOU SHALL BE SOLELY RESPONSIBLE TO OBTAIN
12 * ALL APPROPRIATELY REQUIRED THIRD PARTY LICENCES.
13 * ALLWINNER SHALL HAVE NO WARRANTY, INDEMNITY OR OTHER OBLIGATIONS WITH RESPECT TO MATTERS
14 * COVERED UNDER ANY REQUIRED THIRD PARTY LICENSE.
15 * YOU ARE SOLELY RESPONSIBLE FOR YOUR USAGE OF THIRD PARTY’S TECHNOLOGY.
16 *
17 *
18 * THIS SOFTWARE IS PROVIDED BY ALLWINNER"AS IS" AND TO THE MAXIMUM EXTENT
19 * PERMITTED BY LAW, ALLWINNER EXPRESSLY DISCLAIMS ALL WARRANTIES OF ANY KIND,
20 * WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION REGARDING
21 * THE TITLE, NON-INFRINGEMENT, ACCURACY, CONDITION, COMPLETENESS, PERFORMANCE
22 * OR MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
23 * IN NO EVENT SHALL ALLWINNER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
25 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
26 * LOSS OF USE, DATA, OR PROFITS, OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
28 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
30 * OF THE POSSIBILITY OF SUCH DAMAGE.
31 */
32
33 #include <stdlib.h>
34 #include <stdio.h>
35 #include <errno.h>
36 #include "pcm_local.h"
37
38 #ifdef REFINE_DEBUG
dump_hw_params(snd_pcm_hw_params_t * params,const char * type,snd_pcm_hw_param_t var,unsigned int val,int err)39 static void dump_hw_params(snd_pcm_hw_params_t *params, const char *type,
40 snd_pcm_hw_param_t var, unsigned int val, int err)
41 {
42 awalsa_info("ALSA ERROR hw_params: %s (%s)\n", type, snd_pcm_hw_param_name(var));
43 fprintf(stderr, " value = ");
44 switch (var) {
45 case SND_PCM_HW_PARAM_ACCESS:
46 fprintf(stderr, "%s", snd_pcm_access_name(val));
47 break;
48 case SND_PCM_HW_PARAM_FORMAT:
49 fprintf(stderr, "%s", snd_pcm_format_name(val));
50 break;
51 default:
52 fprintf(stderr, "%u", val);
53 }
54 fprintf(stderr, " (err: %d)\n", err);
55 snd_pcm_hw_params_dump(params);
56 }
57 #else
dump_hw_params(snd_pcm_hw_params_t * params,const char * type,snd_pcm_hw_param_t var,unsigned int val,int err)58 static inline void dump_hw_params(snd_pcm_hw_params_t *params, const char *type,
59 snd_pcm_hw_param_t var, unsigned int val, int err)
60 {
61 }
62 #endif
63
snd_range_print(const snd_interval_t * i)64 static void snd_range_print(const snd_interval_t *i)
65 {
66 if (snd_range_empty(i))
67 printf("NONE");
68 else if (i->range.min == 0 && i->range.openmin == 0 &&
69 i->range.max == UINT_MAX && i->range.openmax == 0)
70 printf("ALL");
71 else if (snd_range_single(i) && i->range.integer)
72 printf("%u", snd_range_value(i));
73 else
74 printf("%c%lu %lu%c",
75 i->range.openmin ? '(' : '[',
76 (long unsigned int)i->range.min, (long unsigned int)i->range.max,
77 i->range.openmax? ')' : ']');
78 }
79
snd_pcm_hw_param_dump(const snd_pcm_hw_params_t * params,snd_pcm_hw_param_t var)80 void snd_pcm_hw_param_dump(const snd_pcm_hw_params_t *params, snd_pcm_hw_param_t var)
81 {
82 if (hw_is_mask(var)) {
83 const snd_interval_t *interval = hw_param_interval_c(params, var);
84 if (snd_mask_empty(interval))
85 printf(" NONE");
86 else if (snd_mask_full(interval))
87 printf(" ALL");
88 else {
89 unsigned int k;
90 for (k = 0; k <= SND_MASK_BITS; ++k) {
91 if (snd_mask_test(interval, k)) {
92 const char *s;
93 switch (var) {
94 case SND_PCM_HW_PARAM_ACCESS:
95 s = snd_pcm_access_name(k);
96 break;
97 case SND_PCM_HW_PARAM_FORMAT:
98 s = snd_pcm_format_name(k);
99 break;
100 default:
101 assert(0);
102 s = NULL;
103 }
104 if (s)
105 printf(" %s", s);
106 }
107 }
108 }
109 return;
110 }
111 if (hw_is_range(var)) {
112 snd_range_print(hw_param_interval_c(params, var));
113 return;
114 }
115 assert(0);
116 }
117
boundary_sub(int a,int adir,int b,int bdir,int * c,int * cdir)118 static void boundary_sub(int a, int adir, int b, int bdir, int *c, int *cdir)
119 {
120 adir = adir < 0 ? -1 : (adir > 0 ? 1 : 0);
121 bdir = bdir < 0 ? -1 : (bdir > 0 ? 1 : 0);
122 *c = a - b;
123 *cdir = adir - bdir;
124 if (*cdir == -2) {
125 assert(*c > INT_MIN);
126 (*c)--;
127 } else if (*cdir == 2) {
128 assert(*c < INT_MAX);
129 (*c)++;
130 }
131 }
132
boundary_lt(unsigned int a,int adir,unsigned int b,int bdir)133 static int boundary_lt(unsigned int a, int adir, unsigned int b, int bdir)
134 {
135 assert(a > 0 || adir >= 0);
136 assert(b > 0 || bdir >= 0);
137 if (adir < 0) {
138 a--;
139 adir = 1;
140 } else if (adir > 0)
141 adir = 1;
142 if (bdir < 0) {
143 b--;
144 bdir = 1;
145 } else if (bdir > 0)
146 bdir = 1;
147 return a < b || (a == b && adir < bdir);
148 }
149
150 /* Return 1 if min is nearer to best than max */
boundary_nearer(int min,int mindir,int best,int bestdir,int max,int maxdir)151 static int boundary_nearer(int min, int mindir, int best, int bestdir, int max, int maxdir)
152 {
153 int dmin, dmindir;
154 int dmax, dmaxdir;
155 boundary_sub(best, bestdir, min, mindir, &dmin, &dmindir);
156 boundary_sub(max, maxdir, best, bestdir, &dmax, &dmaxdir);
157 return boundary_lt(dmin, dmindir, dmax, dmaxdir);
158 }
159
snd_pcm_hw_param_empty(const snd_pcm_hw_params_t * params,snd_pcm_hw_param_t var)160 int snd_pcm_hw_param_empty(const snd_pcm_hw_params_t *params,
161 snd_pcm_hw_param_t var)
162 {
163 if (hw_is_mask(var))
164 return snd_mask_empty(hw_param_interval_c(params, var));
165 if (hw_is_range(var))
166 return snd_range_empty(hw_param_interval_c(params, var));
167 assert(0);
168 return -EINVAL;
169 }
170
snd_pcm_hw_param_always_eq(const snd_pcm_hw_params_t * params,snd_pcm_hw_param_t var,const snd_pcm_hw_params_t * params1)171 int snd_pcm_hw_param_always_eq(const snd_pcm_hw_params_t *params,
172 snd_pcm_hw_param_t var,
173 const snd_pcm_hw_params_t *params1)
174 {
175 if (hw_is_mask(var))
176 return snd_mask_always_eq(hw_param_interval_c(params, var),
177 hw_param_interval_c(params1, var));
178 if (hw_is_range(var))
179 return snd_range_always_eq(hw_param_interval_c(params, var),
180 hw_param_interval_c(params1, var));
181 assert(0);
182 return -EINVAL;
183 }
184
snd_pcm_hw_param_never_eq(const snd_pcm_hw_params_t * params,snd_pcm_hw_param_t var,const snd_pcm_hw_params_t * params1)185 int snd_pcm_hw_param_never_eq(const snd_pcm_hw_params_t *params,
186 snd_pcm_hw_param_t var,
187 const snd_pcm_hw_params_t *params1)
188 {
189 if (hw_is_mask(var))
190 return snd_mask_never_eq(hw_param_interval_c(params, var),
191 hw_param_interval_c(params1, var));
192 if (hw_is_range(var))
193 return snd_range_never_eq(hw_param_interval_c(params, var),
194 hw_param_interval_c(params1, var));
195 assert(0);
196 return -EINVAL;
197 }
198
_snd_pcm_hw_param_any(snd_pcm_hw_params_t * params,snd_pcm_hw_param_t var)199 void _snd_pcm_hw_param_any(snd_pcm_hw_params_t *params, snd_pcm_hw_param_t var)
200 {
201 if (hw_is_mask(var)) {
202 snd_mask_any(hw_param_interval(params, var));
203 params->cmask |= 1 << var;
204 params->rmask |= 1 << var;
205 return;
206 }
207 if (hw_is_range(var)) {
208 snd_range_any(hw_param_interval(params, var));
209 params->cmask |= 1 << var;
210 params->rmask |= 1 << var;
211 return;
212 }
213 assert(0);
214 }
215
_snd_pcm_hw_params_any(snd_pcm_hw_params_t * params)216 void _snd_pcm_hw_params_any(snd_pcm_hw_params_t *params)
217 {
218 unsigned int k;
219 memset(params, 0, sizeof(*params));
220 for (k = SND_PCM_HW_PARAM_FIRST_INTERVAL; k <= SND_PCM_HW_PARAM_LAST_INTERVAL; k++)
221 _snd_pcm_hw_param_any(params, k);
222 params->rmask = ~0U;
223 params->cmask = 0;
224 }
225
snd_pcm_hw_param_get(const snd_pcm_hw_params_t * params,snd_pcm_hw_param_t var,unsigned int * val,int * dir)226 int snd_pcm_hw_param_get(const snd_pcm_hw_params_t *params, snd_pcm_hw_param_t var,
227 unsigned int *val, int *dir)
228 {
229 const snd_interval_t *i = hw_param_interval_c(params, var);
230 if (hw_is_mask(var)) {
231 if (snd_mask_empty(i) || !snd_mask_single(i))
232 return -EINVAL;
233 if (dir)
234 *dir = 0;
235 if (val)
236 *val = snd_mask_value(i);
237 return 0;
238 } else if (hw_is_range(var)) {
239 if (snd_range_empty(i) || !snd_range_single(i))
240 return -EINVAL;
241 if (dir)
242 *dir = i->range.openmin;
243 if (val)
244 *val = snd_range_value(i);
245 return 0;
246 }
247 assert(0);
248 return -EINVAL;
249 }
250
snd_pcm_hw_param_get_min(const snd_pcm_hw_params_t * params,snd_pcm_hw_param_t var,unsigned int * val,int * dir)251 int snd_pcm_hw_param_get_min(const snd_pcm_hw_params_t *params, snd_pcm_hw_param_t var,
252 unsigned int *val, int *dir)
253 {
254 const snd_interval_t *i = hw_param_interval_c(params, var);
255 if (hw_is_mask(var)) {
256 assert(!snd_mask_empty(i));
257 if (dir)
258 *dir = 0;
259 if (val)
260 *val = snd_mask_min(i);
261 return 0;
262 } else if (hw_is_range(var)) {
263 assert(!snd_range_empty(i));
264 if (dir)
265 *dir = i->range.openmin;
266 if (val)
267 *val = snd_range_min(i);
268 return 0;
269 }
270 assert(0);
271 return 0;
272 }
273
snd_pcm_hw_param_get_max(const snd_pcm_hw_params_t * params,snd_pcm_hw_param_t var,unsigned int * val,int * dir)274 int snd_pcm_hw_param_get_max(const snd_pcm_hw_params_t *params, snd_pcm_hw_param_t var,
275 unsigned int *val, int *dir)
276 {
277 const snd_interval_t *i = hw_param_interval_c(params, var);
278 if (hw_is_mask(var)) {
279 assert(!snd_mask_empty(i));
280 if (dir)
281 *dir = 0;
282 if (val)
283 *val = snd_mask_max(i);
284 return 0;
285 } else if (hw_is_range(var)) {
286 assert(!snd_range_empty(i));
287 if (dir)
288 *dir = - (int) i->range.openmax;
289 if (val)
290 *val = snd_range_max(i);
291 return 0;
292 }
293 assert(0);
294 return 0;
295 }
296
_snd_pcm_hw_param_set_mask(snd_pcm_hw_params_t * params,snd_pcm_hw_param_t var,const snd_interval_t * val)297 int _snd_pcm_hw_param_set_mask(snd_pcm_hw_params_t *params,
298 snd_pcm_hw_param_t var, const snd_interval_t *val)
299 {
300 int changed;
301 assert(hw_is_mask(var));
302 changed = snd_mask_refine(hw_param_interval(params, var), val);
303 if (changed) {
304 params->cmask |= 1 << var;
305 params->rmask |= 1 << var;
306 }
307 return changed;
308 }
309
snd_pcm_hw_param_set_mask(snd_pcm_t * pcm,snd_pcm_hw_params_t * params,snd_set_mode_t mode,snd_pcm_hw_param_t var,const snd_interval_t * val)310 int snd_pcm_hw_param_set_mask(snd_pcm_t *pcm, snd_pcm_hw_params_t *params,
311 snd_set_mode_t mode,
312 snd_pcm_hw_param_t var, const snd_interval_t *val)
313 {
314 snd_pcm_hw_params_t save;
315 int err;
316 switch (mode) {
317 case SND_CHANGE:
318 break;
319 case SND_TRY:
320 save = *params;
321 break;
322 case SND_TEST:
323 save = *params;
324 params = &save;
325 break;
326 default:
327 assert(0);
328 return -EINVAL;
329 }
330 err = _snd_pcm_hw_param_set_mask(params, var, val);
331 if (err < 0)
332 goto _fail;
333 if (mode != SND_TEST && params->rmask) {
334 err = snd_pcm_hw_refine(pcm, params);
335 if (err < 0)
336 goto _fail;
337 }
338 return 0;
339 _fail:
340 if (mode == SND_TRY)
341 *params = save;
342 return err;
343 }
344
_snd_pcm_hw_param_set_range(snd_pcm_hw_params_t * params,snd_pcm_hw_param_t var,const snd_interval_t * val)345 int _snd_pcm_hw_param_set_range(snd_pcm_hw_params_t *params,
346 snd_pcm_hw_param_t var, const snd_interval_t *val)
347 {
348 int changed;
349 assert(hw_is_range(var));
350 changed = snd_range_refine(hw_param_interval(params, var), val);
351 if (changed) {
352 params->cmask |= 1 << var;
353 params->rmask |= 1 << var;
354 }
355 return changed;
356 }
357
_snd_pcm_hw_param_set_first(snd_pcm_hw_params_t * params,snd_pcm_hw_param_t var)358 static int _snd_pcm_hw_param_set_first(snd_pcm_hw_params_t *params,
359 snd_pcm_hw_param_t var)
360 {
361 int changed;
362 if (hw_is_mask(var))
363 changed = snd_mask_refine_first(hw_param_interval(params, var));
364 else if (hw_is_range(var))
365 changed = snd_range_refine_first(hw_param_interval(params, var));
366 else {
367 assert(0);
368 return -EINVAL;
369 }
370 if (changed > 0) {
371 params->cmask |= 1 << var;
372 params->rmask |= 1 << var;
373 }
374 return changed;
375 }
376
snd_pcm_hw_param_set_first(snd_pcm_t * pcm,snd_pcm_hw_params_t * params,snd_pcm_hw_param_t var,unsigned int * rval,int * dir)377 int snd_pcm_hw_param_set_first(snd_pcm_t *pcm,
378 snd_pcm_hw_params_t *params,
379 snd_pcm_hw_param_t var,
380 unsigned int *rval, int *dir)
381 {
382 int err;
383
384 err = _snd_pcm_hw_param_set_first(params, var);
385 if (err < 0)
386 return err;
387 if (params->rmask) {
388 err = snd_pcm_hw_refine(pcm, params);
389 if (err < 0)
390 return err;
391 }
392 return snd_pcm_hw_param_get(params, var, rval, dir);
393 }
394
_snd_pcm_hw_param_set_last(snd_pcm_hw_params_t * params,snd_pcm_hw_param_t var)395 static int _snd_pcm_hw_param_set_last(snd_pcm_hw_params_t *params,
396 snd_pcm_hw_param_t var)
397 {
398 int changed;
399 if (hw_is_mask(var))
400 changed = snd_mask_refine_last(hw_param_interval(params, var));
401 else if (hw_is_range(var))
402 changed = snd_range_refine_last(hw_param_interval(params, var));
403 else {
404 assert(0);
405 return -EINVAL;
406 }
407 if (changed > 0) {
408 params->cmask |= 1 << var;
409 params->rmask |= 1 << var;
410 }
411 return changed;
412 }
413
snd_pcm_hw_param_set_last(snd_pcm_t * pcm,snd_pcm_hw_params_t * params,snd_pcm_hw_param_t var,unsigned int * rval,int * dir)414 int snd_pcm_hw_param_set_last(snd_pcm_t *pcm,
415 snd_pcm_hw_params_t *params,
416 snd_pcm_hw_param_t var,
417 unsigned int *rval, int *dir)
418 {
419 int err;
420
421 err = _snd_pcm_hw_param_set_last(params, var);
422 if (err < 0)
423 return err;
424 if (params->rmask) {
425 err = snd_pcm_hw_refine(pcm, params);
426 if (err < 0)
427 return err;
428 }
429 return snd_pcm_hw_param_get(params, var, rval, dir);
430 }
431
_snd_pcm_hw_param_set_min(snd_pcm_hw_params_t * params,snd_pcm_hw_param_t var,unsigned int val,int dir)432 int _snd_pcm_hw_param_set_min(snd_pcm_hw_params_t *params,
433 snd_pcm_hw_param_t var, unsigned int val, int dir)
434 {
435 int changed;
436 int openmin = 0;
437 if (dir) {
438 if (dir > 0) {
439 openmin = 1;
440 } else if (dir < 0) {
441 if (val > 0) {
442 openmin = 1;
443 val--;
444 }
445 }
446 }
447 if (hw_is_mask(var))
448 changed = snd_mask_refine_min(hw_param_interval(params, var), val + !!openmin);
449 else if (hw_is_range(var))
450 changed = snd_range_refine_min(hw_param_interval(params, var), val, openmin);
451 else {
452 assert(0);
453 return -EINVAL;
454 }
455 if (changed) {
456 params->cmask |= 1 << var;
457 params->rmask |= 1 << var;
458 }
459 return changed;
460 }
461
snd_pcm_hw_param_set_min(snd_pcm_t * pcm,snd_pcm_hw_params_t * params,snd_set_mode_t mode,snd_pcm_hw_param_t var,unsigned int * val,int * dir)462 int snd_pcm_hw_param_set_min(snd_pcm_t *pcm, snd_pcm_hw_params_t *params,
463 snd_set_mode_t mode,
464 snd_pcm_hw_param_t var, unsigned int *val, int *dir)
465 {
466 snd_pcm_hw_params_t save;
467 int err;
468 switch (mode) {
469 case SND_CHANGE:
470 break;
471 case SND_TRY:
472 save = *params;
473 break;
474 case SND_TEST:
475 save = *params;
476 params = &save;
477 break;
478 default:
479 assert(0);
480 return -EINVAL;
481 }
482 err = _snd_pcm_hw_param_set_min(params, var, *val, dir ? *dir : 0);
483 if (err < 0)
484 goto _fail;
485 if ((mode != SND_TEST || hw_is_range(var)) && params->rmask) {
486 err = snd_pcm_hw_refine(pcm, params);
487 if (err < 0)
488 goto _fail;
489 if (snd_pcm_hw_param_empty(params, var)) {
490 err = -ENOENT;
491 goto _fail;
492 }
493 }
494 return snd_pcm_hw_param_get_min(params, var, val, dir);
495 _fail:
496 if (mode == SND_TRY)
497 *params = save;
498 if (err < 0 && mode == SND_TRY)
499 dump_hw_params(params, "set_min", var, *val, err);
500 return err;
501 }
502
_snd_pcm_hw_param_set_max(snd_pcm_hw_params_t * params,snd_pcm_hw_param_t var,unsigned int val,int dir)503 int _snd_pcm_hw_param_set_max(snd_pcm_hw_params_t *params,
504 snd_pcm_hw_param_t var, unsigned int val, int dir)
505 {
506 int changed;
507 int openmax = 0;
508 if (dir) {
509 if (dir < 0) {
510 openmax = 1;
511 } else if (dir > 0) {
512 openmax = 1;
513 val++;
514 }
515 }
516 if (hw_is_mask(var)) {
517 if (val == 0 && openmax) {
518 snd_mask_none(hw_param_interval(params, var));
519 changed = -EINVAL;
520 } else
521 changed = snd_mask_refine_max(hw_param_interval(params, var),
522 val - !!openmax);
523 } else if (hw_is_range(var))
524 changed = snd_range_refine_max(hw_param_interval(params, var), val, openmax);
525 else {
526 assert(0);
527 return -EINVAL;
528 }
529 if (changed) {
530 params->cmask |= 1 << var;
531 params->rmask |= 1 << var;
532 }
533 return changed;
534 }
535
snd_pcm_hw_param_set_max(snd_pcm_t * pcm,snd_pcm_hw_params_t * params,snd_set_mode_t mode,snd_pcm_hw_param_t var,unsigned int * val,int * dir)536 int snd_pcm_hw_param_set_max(snd_pcm_t *pcm, snd_pcm_hw_params_t *params,
537 snd_set_mode_t mode,
538 snd_pcm_hw_param_t var, unsigned int *val, int *dir)
539 {
540 snd_pcm_hw_params_t save;
541 int err;
542 switch (mode) {
543 case SND_CHANGE:
544 break;
545 case SND_TRY:
546 save = *params;
547 break;
548 case SND_TEST:
549 save = *params;
550 params = &save;
551 break;
552 default:
553 assert(0);
554 return -EINVAL;
555 }
556 err = _snd_pcm_hw_param_set_max(params, var, *val, dir ? *dir : 0);
557 if (err < 0)
558 goto _fail;
559 if ((mode != SND_TEST || hw_is_range(var)) && params->rmask) {
560 err = snd_pcm_hw_refine(pcm, params);
561 if (err < 0)
562 goto _fail;
563 if (snd_pcm_hw_param_empty(params, var)) {
564 err = -ENOENT;
565 goto _fail;
566 }
567 }
568 return snd_pcm_hw_param_get_max(params, var, val, dir);
569 _fail:
570 if (mode == SND_TRY)
571 *params = save;
572 if (err < 0 && mode == SND_TRY)
573 dump_hw_params(params, "set_max", var, *val, err);
574 return err;
575 }
576
_snd_pcm_hw_param_set_minmax(snd_pcm_hw_params_t * params,snd_pcm_hw_param_t var,unsigned int min,int mindir,unsigned int max,int maxdir)577 int _snd_pcm_hw_param_set_minmax(snd_pcm_hw_params_t *params,
578 snd_pcm_hw_param_t var,
579 unsigned int min, int mindir,
580 unsigned int max, int maxdir)
581 {
582 int changed, c1, c2;
583 int openmin = 0, openmax = 0;
584 if (mindir) {
585 if (mindir > 0) {
586 openmin = 1;
587 } else if (mindir < 0) {
588 if (min > 0) {
589 openmin = 1;
590 min--;
591 }
592 }
593 }
594 if (maxdir) {
595 if (maxdir < 0) {
596 openmax = 1;
597 } else if (maxdir > 0) {
598 openmax = 1;
599 max++;
600 }
601 }
602 snd_interval_t *i = hw_param_interval(params, var);
603 if (hw_is_mask(var)) {
604 if (max == 0 && openmax) {
605 snd_mask_none(i);
606 changed = -EINVAL;
607 } else {
608 c1 = snd_mask_refine_min(i, min + !!openmin);
609 if (c1 < 0)
610 changed = c1;
611 else {
612 c2 = snd_mask_refine_max(i, max - !!openmax);
613 if (c2 < 0)
614 changed = c2;
615 else
616 changed = (c1 || c2);
617 }
618 }
619 } else if (hw_is_range(var)) {
620 c1 = snd_range_refine_min(i, min, openmin);
621 if (c1 < 0)
622 changed = c1;
623 else {
624 c2 = snd_range_refine_max(i, max, openmax);
625 if (c2 < 0)
626 changed = c2;
627 else
628 changed = (c1 || c2);
629 }
630 } else {
631 assert(0);
632 return -EINVAL;
633 }
634 if (changed) {
635 params->cmask |= 1 << var;
636 params->rmask |= 1 << var;
637 }
638 return changed;
639 }
640
snd_pcm_hw_param_set_minmax(snd_pcm_t * pcm,snd_pcm_hw_params_t * params,snd_set_mode_t mode,snd_pcm_hw_param_t var,unsigned int * min,int * mindir,unsigned int * max,int * maxdir)641 int snd_pcm_hw_param_set_minmax(snd_pcm_t *pcm, snd_pcm_hw_params_t *params,
642 snd_set_mode_t mode,
643 snd_pcm_hw_param_t var,
644 unsigned int *min, int *mindir,
645 unsigned int *max, int *maxdir)
646 {
647 snd_pcm_hw_params_t save;
648 int err;
649 switch (mode) {
650 case SND_CHANGE:
651 break;
652 case SND_TRY:
653 save = *params;
654 break;
655 case SND_TEST:
656 save = *params;
657 params = &save;
658 break;
659 default:
660 assert(0);
661 return -EINVAL;
662 }
663 err = _snd_pcm_hw_param_set_minmax(params, var,
664 *min, mindir ? *mindir : 0,
665 *max, maxdir ? *maxdir : 0);
666 if (err < 0)
667 goto _fail;
668 if ((mode != SND_TEST || hw_is_range(var)) && params->rmask) {
669 err = snd_pcm_hw_refine(pcm, params);
670 if (err < 0)
671 goto _fail;
672 }
673 err = snd_pcm_hw_param_get_min(params, var, min, mindir);
674 if (err < 0)
675 return err;
676 return snd_pcm_hw_param_get_max(params, var, max, maxdir);
677 _fail:
678 if (mode == SND_TRY)
679 *params = save;
680 if (err < 0)
681 dump_hw_params(params, "set_minmax", var, *min, err);
682 return err;
683 }
684
_snd_pcm_hw_param_refine(snd_pcm_hw_params_t * params,snd_pcm_hw_param_t var,const snd_pcm_hw_params_t * src)685 int _snd_pcm_hw_param_refine(snd_pcm_hw_params_t *params,
686 snd_pcm_hw_param_t var,
687 const snd_pcm_hw_params_t *src)
688 {
689 int changed = 0;
690 snd_interval_t *d = hw_param_interval(params, var);
691 const snd_interval_t *s = hw_param_interval_c(src, var);
692 if (hw_is_mask(var))
693 changed = snd_mask_refine(d, s);
694 else if (hw_is_range(var))
695 changed = snd_range_refine(d, s);
696 else
697 return 0; /* NOP / reserved */
698 if (changed) {
699 params->cmask |= 1 << var;
700 params->rmask |= 1 << var;
701 }
702 return changed;
703 }
704
_snd_pcm_hw_params_refine(snd_pcm_hw_params_t * params,unsigned int vars,const snd_pcm_hw_params_t * src)705 int _snd_pcm_hw_params_refine(snd_pcm_hw_params_t *params,
706 unsigned int vars,
707 const snd_pcm_hw_params_t *src)
708 {
709 int changed, err = 0;
710 unsigned int k;
711 for (k = 0; k <= SND_PCM_HW_PARAM_LAST_INTERVAL; ++k) {
712 if (!(vars & (1 << k)))
713 continue;
714 changed = _snd_pcm_hw_param_refine(params, k, src);
715 if (changed < 0)
716 err = changed;
717 }
718 #if 0
719 params->info &= src->info;
720 params->flags = src->flags; /* propagate all flags to slave */
721 #endif
722 return err;
723 }
724
snd_pcm_hw_refine(snd_pcm_t * pcm,snd_pcm_hw_params_t * params)725 int snd_pcm_hw_refine(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
726 {
727 int res;
728
729 assert(pcm && params);
730 assert(pcm->ops->hw_refine);
731 awalsa_debug("\n");
732
733 #ifdef REFINE_DEBUG
734 awalsa_info("refine '%s' (begin)\n", pcm->name);
735 snd_pcm_hw_params_dump(params);
736 #endif
737 res = pcm->ops->hw_refine(pcm->op_arg, params);
738 #ifdef REFINE_DEBUG
739 awalsa_info("refine '%s' (end: %d)\n", pcm->name, res);
740 snd_pcm_hw_params_dump(params);
741 #endif
742 return res;
743 }
744
_snd_pcm_hw_param_set(snd_pcm_hw_params_t * params,snd_pcm_hw_param_t var,unsigned int val,int dir)745 int _snd_pcm_hw_param_set(snd_pcm_hw_params_t *params,
746 snd_pcm_hw_param_t var, unsigned int val, int dir)
747 {
748 int changed;
749 snd_interval_t *interval = hw_param_interval(params, var);
750
751 awalsa_debug("\n");
752 if (hw_is_mask(var)) {
753 if (val == 0 && dir < 0) {
754 changed = -EINVAL;
755 snd_mask_none(interval);
756 } else {
757 if (dir > 0)
758 val++;
759 else if (dir < 0)
760 val--;
761 changed = snd_mask_refine_set(interval, val);
762 }
763 } else if (hw_is_range(var)) {
764 if (val == 0 && dir < 0) {
765 changed = -EINVAL;
766 snd_range_none(interval);
767 } else if (dir == 0) {
768 changed = snd_range_refine_set(interval, val);
769 } else {
770 snd_interval_t t;
771 t.range.openmin = 1;
772 t.range.openmax = 1;
773 t.range.empty = 0;
774 t.range.integer = 0;
775 if (dir < 0) {
776 t.range.min = val - 1;
777 t.range.max = val;
778 } else {
779 t.range.min = val;
780 t.range.max = val + 1;
781 }
782 changed = snd_range_refine(interval, &t);
783 }
784 } else {
785 assert(0);
786 return -EINVAL;
787 }
788 if (changed) {
789 params->cmask |= 1 << var;
790 params->rmask |= 1 << var;
791 }
792 return changed;
793 }
794
snd_pcm_hw_param_set(snd_pcm_t * pcm,snd_pcm_hw_params_t * params,snd_set_mode_t mode,snd_pcm_hw_param_t var,unsigned int val,int dir)795 int snd_pcm_hw_param_set(snd_pcm_t *pcm, snd_pcm_hw_params_t *params,
796 snd_set_mode_t mode,
797 snd_pcm_hw_param_t var, unsigned int val, int dir)
798 {
799 snd_pcm_hw_params_t save;
800 int ret;
801 switch (mode) {
802 case SND_CHANGE:
803 break;
804 case SND_TRY:
805 save = *params;
806 break;
807 case SND_TEST:
808 save = *params;
809 params = &save;
810 break;
811 default:
812 assert(0);
813 return -EINVAL;
814 }
815 awalsa_debug("\n");
816 ret = _snd_pcm_hw_param_set(params, var, val, dir);
817 if (ret < 0)
818 goto _fail;
819 if ((mode != SND_TEST || hw_is_range(var)) && params->rmask) {
820 ret = snd_pcm_hw_refine(pcm, params);
821 if (ret < 0)
822 goto _fail;
823 }
824 return 0;
825 _fail:
826 if (mode == SND_TRY)
827 *params = save;
828 if (ret < 0 && mode == SND_TRY)
829 dump_hw_params(params, "set", var, val, ret);
830 return ret;
831 }
832
snd_pcm_hw_param_set_near(snd_pcm_t * pcm,snd_pcm_hw_params_t * params,snd_pcm_hw_param_t var,unsigned int * val,int * dir)833 int snd_pcm_hw_param_set_near(snd_pcm_t *pcm, snd_pcm_hw_params_t *params,
834 snd_pcm_hw_param_t var,
835 unsigned int *val, int *dir)
836 {
837 snd_pcm_hw_params_t save;
838 int err;
839 unsigned int best = *val, saved_min;
840 int last = 0;
841 unsigned int min, max;
842 int mindir, maxdir;
843 int valdir = dir ? *dir : 0;
844 snd_interval_t *i;
845
846 awalsa_debug("\n");
847
848 /* FIXME */
849 if (best > INT_MAX)
850 best = INT_MAX;
851 min = max = best;
852 mindir = maxdir = valdir;
853 if (maxdir > 0)
854 maxdir = 0;
855 else if (maxdir == 0)
856 maxdir = -1;
857 else {
858 maxdir = 1;
859 max--;
860 }
861 save = *params;
862 saved_min = min;
863 err = snd_pcm_hw_param_set_min(pcm, params, SND_CHANGE, var, &min, &mindir);
864
865 i = hw_param_interval(params, var);
866 if (!snd_range_empty(i) && snd_range_single(i)) {
867 err = snd_pcm_hw_param_get_min(params, var, val, dir);
868 if (err < 0)
869 dump_hw_params(params, "set_near", var, *val, err);
870 return err;
871 }
872
873 if (err >= 0) {
874 snd_pcm_hw_params_t params1;
875 if (min == saved_min && mindir == valdir)
876 goto _end;
877 params1 = save;
878 err = snd_pcm_hw_param_set_max(pcm, ¶ms1, SND_CHANGE, var, &max, &maxdir);
879 if (err < 0)
880 goto _end;
881 if (boundary_nearer(max, maxdir, best, valdir, min, mindir)) {
882 *params = params1;
883 last = 1;
884 }
885 } else {
886 *params = save;
887 err = snd_pcm_hw_param_set_max(pcm, params, SND_CHANGE, var, &max, &maxdir);
888 if (err < 0) {
889 dump_hw_params(params, "set_near", var, *val, err);
890 return err;
891 }
892 last = 1;
893 }
894 _end:
895 if (last)
896 err = snd_pcm_hw_param_set_last(pcm, params, var, val, dir);
897 else
898 err = snd_pcm_hw_param_set_first(pcm, params, var, val, dir);
899 if (err < 0)
900 dump_hw_params(params, "set_near", var, *val, err);
901 return err;
902 }
903
snd_pcm_hw_param_set_near_minmax(snd_pcm_t * pcm,snd_pcm_hw_params_t * params,snd_pcm_hw_param_t var,unsigned int min,int * mindir,unsigned int max,int * maxdir)904 static int snd_pcm_hw_param_set_near_minmax(snd_pcm_t *pcm,
905 snd_pcm_hw_params_t *params,
906 snd_pcm_hw_param_t var,
907 unsigned int min, int *mindir,
908 unsigned int max, int *maxdir)
909 {
910 snd_pcm_hw_params_t tmp;
911 int err;
912 if (!boundary_lt(min, *mindir, max, *maxdir))
913 return snd_pcm_hw_param_set_near(pcm, params, var, &min, mindir);
914 tmp = *params;
915 err = snd_pcm_hw_param_set_near(pcm, &tmp, var, &min, mindir);
916 if (err < 0)
917 return err;
918 if (boundary_lt(min, *mindir, max, *maxdir)) {
919 tmp = *params;
920 err = snd_pcm_hw_param_set_near(pcm, &tmp, var, &max, maxdir);
921 } else {
922 max = min;
923 *maxdir = *mindir;
924 }
925 err = snd_pcm_hw_param_set_minmax(pcm, params, SND_CHANGE, var, &min, mindir,
926 &max, maxdir);
927 if (err < 0)
928 return err;
929 return 0;
930 }
931
snd_pcm_hw_param_refine_near(snd_pcm_t * pcm,snd_pcm_hw_params_t * params,snd_pcm_hw_param_t var,const snd_pcm_hw_params_t * src)932 int snd_pcm_hw_param_refine_near(snd_pcm_t *pcm,
933 snd_pcm_hw_params_t *params,
934 snd_pcm_hw_param_t var,
935 const snd_pcm_hw_params_t *src)
936 {
937 unsigned int min, max;
938 int mindir, maxdir, err;
939
940 if ((err = snd_pcm_hw_param_get_min(src, var, &min, &mindir)) < 0)
941 return err;
942 if ((err = snd_pcm_hw_param_get_max(src, var, &max, &maxdir)) < 0)
943 return err;
944 if ((err = snd_pcm_hw_param_set_near_minmax(pcm, params, var,
945 min, &mindir, max, &maxdir)) < 0)
946 return err;
947 return 0;
948 }
949
snd_pcm_hw_param_refine_multiple(snd_pcm_t * pcm,snd_pcm_hw_params_t * params,snd_pcm_hw_param_t var,const snd_pcm_hw_params_t * src)950 int snd_pcm_hw_param_refine_multiple(snd_pcm_t *pcm,
951 snd_pcm_hw_params_t *params,
952 snd_pcm_hw_param_t var,
953 const snd_pcm_hw_params_t *src)
954 {
955 const snd_interval_t *it = hw_param_interval_c(src, var);
956 const snd_interval_t *st = hw_param_interval_c(params, var);
957 if (snd_range_single(it)) {
958 unsigned int best = snd_range_min(it), cur, prev;
959 cur = best;
960 for (;;) {
961 if (st->range.max < cur || (st->range.max == cur && st->range.openmax))
962 break;
963 if (it->range.min <= cur && ! (it->range.min == cur && st->range.openmin)) {
964 if (! snd_pcm_hw_param_set(pcm, params, SND_TRY, var, cur, 0))
965 return 0; /* ok */
966 }
967 prev = cur;
968 cur += best;
969 if (cur <= prev)
970 break;
971 }
972 }
973 return snd_pcm_hw_param_refine_near(pcm, params, var, src);
974 }
975
976 typedef struct snd_pcm_hw_rule snd_pcm_hw_rule_t;
977
978 static const snd_pcm_hw_rule_t refine_rules[] = {
979 {
980 .var = SND_PCM_HW_PARAM_FORMAT,
981 .func = snd_pcm_hw_rule_format,
982 .deps = { SND_PCM_HW_PARAM_SAMPLE_BITS, -1 },
983 .private_data = 0,
984 },
985 {
986 .var = SND_PCM_HW_PARAM_SAMPLE_BITS,
987 .func = snd_pcm_hw_rule_sample_bits,
988 .deps = { SND_PCM_HW_PARAM_FORMAT,
989 SND_PCM_HW_PARAM_SAMPLE_BITS, -1 },
990 .private_data = 0,
991 },
992 {
993 .var = SND_PCM_HW_PARAM_SAMPLE_BITS,
994 .func = snd_pcm_hw_rule_div,
995 .deps = { SND_PCM_HW_PARAM_FRAME_BITS,
996 SND_PCM_HW_PARAM_CHANNELS, -1 },
997 .private_data = 0,
998 },
999 {
1000 .var = SND_PCM_HW_PARAM_FRAME_BITS,
1001 .func = snd_pcm_hw_rule_mul,
1002 .deps = { SND_PCM_HW_PARAM_SAMPLE_BITS,
1003 SND_PCM_HW_PARAM_CHANNELS, -1 },
1004 .private_data = 0,
1005 },
1006 {
1007 .var = SND_PCM_HW_PARAM_FRAME_BITS,
1008 .func = snd_pcm_hw_rule_mulkdiv,
1009 .deps = { SND_PCM_HW_PARAM_PERIOD_BYTES,
1010 SND_PCM_HW_PARAM_PERIOD_SIZE, -1 },
1011 .private_data = (void*) 8,
1012 },
1013 {
1014 .var = SND_PCM_HW_PARAM_FRAME_BITS,
1015 .func = snd_pcm_hw_rule_mulkdiv,
1016 .deps = { SND_PCM_HW_PARAM_BUFFER_BYTES,
1017 SND_PCM_HW_PARAM_BUFFER_SIZE, -1 },
1018 .private_data = (void*) 8,
1019 },
1020 {
1021 .var = SND_PCM_HW_PARAM_CHANNELS,
1022 .func = snd_pcm_hw_rule_div,
1023 .deps = { SND_PCM_HW_PARAM_FRAME_BITS,
1024 SND_PCM_HW_PARAM_SAMPLE_BITS, -1 },
1025 .private_data = 0,
1026 },
1027 {
1028 .var = SND_PCM_HW_PARAM_RATE,
1029 .func = snd_pcm_hw_rule_mulkdiv,
1030 .deps = { SND_PCM_HW_PARAM_PERIOD_SIZE,
1031 SND_PCM_HW_PARAM_PERIOD_TIME, -1 },
1032 .private_data = (void*) 1000000,
1033 },
1034 {
1035 .var = SND_PCM_HW_PARAM_RATE,
1036 .func = snd_pcm_hw_rule_mulkdiv,
1037 .deps = { SND_PCM_HW_PARAM_BUFFER_SIZE,
1038 SND_PCM_HW_PARAM_BUFFER_TIME, -1 },
1039 .private_data = (void*) 1000000,
1040 },
1041 {
1042 .var = SND_PCM_HW_PARAM_PERIODS,
1043 .func = snd_pcm_hw_rule_div,
1044 .deps = { SND_PCM_HW_PARAM_BUFFER_SIZE,
1045 SND_PCM_HW_PARAM_PERIOD_SIZE, -1 },
1046 .private_data = 0,
1047 },
1048 {
1049 .var = SND_PCM_HW_PARAM_PERIOD_SIZE,
1050 .func = snd_pcm_hw_rule_div,
1051 .deps = { SND_PCM_HW_PARAM_BUFFER_SIZE,
1052 SND_PCM_HW_PARAM_PERIODS, -1 },
1053 .private_data = 0,
1054 },
1055 {
1056 .var = SND_PCM_HW_PARAM_PERIOD_SIZE,
1057 .func = snd_pcm_hw_rule_mulkdiv,
1058 .deps = { SND_PCM_HW_PARAM_PERIOD_BYTES,
1059 SND_PCM_HW_PARAM_FRAME_BITS, -1 },
1060 .private_data = (void*) 8,
1061 },
1062 {
1063 .var = SND_PCM_HW_PARAM_PERIOD_SIZE,
1064 .func = snd_pcm_hw_rule_muldivk,
1065 .deps = { SND_PCM_HW_PARAM_PERIOD_TIME,
1066 SND_PCM_HW_PARAM_RATE, -1 },
1067 .private_data = (void*) 1000000,
1068 },
1069 {
1070 .var = SND_PCM_HW_PARAM_BUFFER_SIZE,
1071 .func = snd_pcm_hw_rule_mul,
1072 .deps = { SND_PCM_HW_PARAM_PERIOD_SIZE,
1073 SND_PCM_HW_PARAM_PERIODS, -1 },
1074 .private_data = 0,
1075 },
1076 {
1077 .var = SND_PCM_HW_PARAM_BUFFER_SIZE,
1078 .func = snd_pcm_hw_rule_mulkdiv,
1079 .deps = { SND_PCM_HW_PARAM_BUFFER_BYTES,
1080 SND_PCM_HW_PARAM_FRAME_BITS, -1 },
1081 .private_data = (void*) 8,
1082 },
1083 {
1084 .var = SND_PCM_HW_PARAM_BUFFER_SIZE,
1085 .func = snd_pcm_hw_rule_muldivk,
1086 .deps = { SND_PCM_HW_PARAM_BUFFER_TIME,
1087 SND_PCM_HW_PARAM_RATE, -1 },
1088 .private_data = (void*) 1000000,
1089 },
1090 {
1091 .var = SND_PCM_HW_PARAM_PERIOD_BYTES,
1092 .func = snd_pcm_hw_rule_muldivk,
1093 .deps = { SND_PCM_HW_PARAM_PERIOD_SIZE,
1094 SND_PCM_HW_PARAM_FRAME_BITS, -1 },
1095 .private_data = (void*) 8,
1096 },
1097 {
1098 .var = SND_PCM_HW_PARAM_BUFFER_BYTES,
1099 .func = snd_pcm_hw_rule_muldivk,
1100 .deps = { SND_PCM_HW_PARAM_BUFFER_SIZE,
1101 SND_PCM_HW_PARAM_FRAME_BITS, -1 },
1102 .private_data = (void*) 8,
1103 },
1104 {
1105 .var = SND_PCM_HW_PARAM_PERIOD_TIME,
1106 .func = snd_pcm_hw_rule_mulkdiv,
1107 .deps = { SND_PCM_HW_PARAM_PERIOD_SIZE,
1108 SND_PCM_HW_PARAM_RATE, -1 },
1109 .private_data = (void*) 1000000,
1110 },
1111 {
1112 .var = SND_PCM_HW_PARAM_BUFFER_TIME,
1113 .func = snd_pcm_hw_rule_mulkdiv,
1114 .deps = { SND_PCM_HW_PARAM_BUFFER_SIZE,
1115 SND_PCM_HW_PARAM_RATE, -1 },
1116 .private_data = (void*) 1000000,
1117 },
1118 };
1119
1120 #define RULES (sizeof(refine_rules) / sizeof(refine_rules[0]))
1121
1122 static const snd_interval_t refine_intervals[
1123 SND_PCM_HW_PARAM_LAST_INTERVAL - SND_PCM_HW_PARAM_FIRST_INTERVAL + 1] = {
1124 [SND_PCM_HW_PARAM_ACCESS - SND_PCM_HW_PARAM_FIRST_INTERVAL] = {
1125 .mask = 0x1f,
1126 },
1127 [SND_PCM_HW_PARAM_FORMAT - SND_PCM_HW_PARAM_FIRST_INTERVAL] = {
1128 .mask = 0xffff,
1129 },
1130 [SND_PCM_HW_PARAM_SAMPLE_BITS - SND_PCM_HW_PARAM_FIRST_INTERVAL] = { .range = {
1131 .min = 1, .max = UINT_MAX,
1132 .openmin = 0, .openmax = 0, .integer = 1, .empty = 0,
1133 }},
1134 [SND_PCM_HW_PARAM_FRAME_BITS - SND_PCM_HW_PARAM_FIRST_INTERVAL] = { .range = {
1135 .min = 1, .max = UINT_MAX,
1136 .openmin = 0, .openmax = 0, .integer = 1, .empty = 0,
1137 }},
1138 [SND_PCM_HW_PARAM_CHANNELS - SND_PCM_HW_PARAM_FIRST_INTERVAL] = { .range = {
1139 .min = 1, .max = UINT_MAX,
1140 .openmin = 0, .openmax = 0, .integer = 1, .empty = 0,
1141 }},
1142 [SND_PCM_HW_PARAM_RATE - SND_PCM_HW_PARAM_FIRST_INTERVAL] = { .range = {
1143 .min = 1, .max = UINT_MAX,
1144 .openmin = 0, .openmax = 0, .integer = 0, .empty = 0,
1145 }},
1146 [SND_PCM_HW_PARAM_PERIOD_TIME - SND_PCM_HW_PARAM_FIRST_INTERVAL] = { .range = {
1147 .min = 0, .max = UINT_MAX,
1148 .openmin = 0, .openmax = 0, .integer = 0, .empty = 0,
1149 }},
1150 [SND_PCM_HW_PARAM_PERIOD_SIZE - SND_PCM_HW_PARAM_FIRST_INTERVAL] = { .range = {
1151 .min = 0, .max = UINT_MAX,
1152 .openmin = 0, .openmax = 0, .integer = 0, .empty = 0,
1153 }},
1154 [SND_PCM_HW_PARAM_PERIOD_BYTES - SND_PCM_HW_PARAM_FIRST_INTERVAL] = { .range = {
1155 .min = 0, .max = UINT_MAX,
1156 .openmin = 0, .openmax = 0, .integer = 0, .empty = 0,
1157 }},
1158 [SND_PCM_HW_PARAM_PERIODS - SND_PCM_HW_PARAM_FIRST_INTERVAL] = { .range = {
1159 .min = 0, .max = UINT_MAX,
1160 .openmin = 0, .openmax = 0, .integer = 0, .empty = 0,
1161 }},
1162 [SND_PCM_HW_PARAM_BUFFER_TIME - SND_PCM_HW_PARAM_FIRST_INTERVAL] = { .range = {
1163 .min = 1, .max = UINT_MAX,
1164 .openmin = 0, .openmax = 0, .integer = 0, .empty = 0,
1165 }},
1166 [SND_PCM_HW_PARAM_BUFFER_SIZE - SND_PCM_HW_PARAM_FIRST_INTERVAL] = { .range = {
1167 .min = 1, .max = UINT_MAX,
1168 .openmin = 0, .openmax = 0, .integer = 1, .empty = 0,
1169 }},
1170 [SND_PCM_HW_PARAM_BUFFER_BYTES - SND_PCM_HW_PARAM_FIRST_INTERVAL] = { .range = {
1171 .min = 1, .max = UINT_MAX,
1172 .openmin = 0, .openmax = 0, .integer = 1, .empty = 0,
1173 }},
1174 };
1175
1176 #if 0
1177 #define RULES_DEBUG
1178 #endif
1179
snd_pcm_hw_refine_soft(snd_pcm_t * pcm,snd_pcm_hw_params_t * params)1180 int snd_pcm_hw_refine_soft(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
1181 {
1182 unsigned int k;
1183 unsigned int rstamps[RULES];
1184 unsigned int vstamps[SND_PCM_HW_PARAM_LAST_INTERVAL + 1];
1185 unsigned int stamp = 2;
1186 int changed, again;
1187 #ifdef RULES_DEBUG
1188 awalsa_info("refine_soft '%s' (begin)\n", pcm->name);
1189 snd_pcm_hw_params_dump(params);
1190 #endif
1191
1192 for (k = SND_PCM_HW_PARAM_FIRST_MASK; k <= SND_PCM_HW_PARAM_LAST_MASK; k++) {
1193 if (!(params->rmask & (1 << k)))
1194 continue;
1195 changed = snd_mask_refine(hw_param_interval(params, k),
1196 &refine_intervals[k - SND_PCM_HW_PARAM_FIRST_INTERVAL]);
1197 if (changed)
1198 params->cmask |= 1 << k;
1199 if (changed < 0)
1200 goto _err;
1201 }
1202
1203 for (k = SND_PCM_HW_PARAM_FIRST_RANGE; k <= SND_PCM_HW_PARAM_LAST_RANGE; k++) {
1204 if (!(params->rmask & (1 << k)))
1205 continue;
1206 changed = snd_range_refine(hw_param_interval(params, k),
1207 &refine_intervals[k - SND_PCM_HW_PARAM_FIRST_INTERVAL]);
1208 if (changed)
1209 params->cmask |= 1 << k;
1210 if (changed < 0)
1211 goto _err;
1212 }
1213
1214 for (k = 0; k < RULES; k++)
1215 rstamps[k] = 0;
1216 for (k = 0; k <= SND_PCM_HW_PARAM_LAST_INTERVAL; k++)
1217 vstamps[k] = (params->rmask & (1 << k)) ? 1 : 0;
1218 do {
1219 again = 0;
1220 for (k = 0; k < RULES; k++) {
1221 const snd_pcm_hw_rule_t *r = &refine_rules[k];
1222 unsigned int d;
1223 int doit = 0;
1224 for (d = 0; r->deps[d] >= 0; d++) {
1225 if (vstamps[r->deps[d]] > rstamps[k]) {
1226 doit = 1;
1227 break;
1228 }
1229 }
1230 if (!doit)
1231 continue;
1232 #ifdef RULES_DEBUG
1233 printf("Rule %d (%p): ", k, r->func);
1234 if (r->var >= 0) {
1235 printf("%s=", snd_pcm_hw_param_name(r->var));
1236 snd_pcm_hw_param_dump(params, r->var);
1237 printf(" -> ");
1238 }
1239 #endif
1240 changed = r->func(params, (struct snd_pcm_hw_rule *)r);
1241 #ifdef RULES_DEBUG
1242 if (r->var >= 0)
1243 snd_pcm_hw_param_dump(params, r->var);
1244 for (d = 0; r->deps[d] >= 0; d++) {
1245 printf(" %s=", snd_pcm_hw_param_name(r->deps[d]));
1246 snd_pcm_hw_param_dump(params, r->deps[d]);
1247 }
1248 printf("\n");
1249 #endif
1250 rstamps[k] = stamp;
1251 if (changed && r->var >= 0) {
1252 params->cmask |= 1 << r->var;
1253 vstamps[r->var] = stamp;
1254 again = 1;
1255 }
1256 if (changed < 0)
1257 goto _err;
1258 stamp++;
1259 }
1260 } while (again);
1261 #if 0
1262 if (!params->msbits) {
1263 i = hw_param_interval(params, SND_PCM_HW_PARAM_SAMPLE_BITS);
1264 if (snd_interval_single(i))
1265 params->msbits = snd_interval_value(i);
1266 }
1267
1268 if (!params->rate_den) {
1269 i = hw_param_interval(params, SND_PCM_HW_PARAM_RATE);
1270 if (snd_interval_single(i)) {
1271 params->rate_num = snd_interval_value(i);
1272 params->rate_den = 1;
1273 }
1274 }
1275 #endif
1276 params->rmask = 0;
1277 return 0;
1278 _err:
1279 #ifdef RULES_DEBUG
1280 awalsa_info("refine_soft '%s' (end-%i)\n", pcm->name, changed);
1281 snd_pcm_hw_params_dump(params);
1282 #endif
1283 return changed;
1284 }
1285
snd_pcm_hw_refine_slave(snd_pcm_t * pcm,snd_pcm_hw_params_t * params,int (* cprepare)(snd_pcm_t * pcm,snd_pcm_hw_params_t * params),int (* cchange)(snd_pcm_t * pcm,snd_pcm_hw_params_t * params,snd_pcm_hw_params_t * sparams),int (* sprepare)(snd_pcm_t * pcm,snd_pcm_hw_params_t * params),int (* schange)(snd_pcm_t * pcm,snd_pcm_hw_params_t * params,snd_pcm_hw_params_t * sparams),int (* srefine)(snd_pcm_t * pcm,snd_pcm_hw_params_t * sparams))1286 int snd_pcm_hw_refine_slave(snd_pcm_t *pcm, snd_pcm_hw_params_t *params,
1287 int (*cprepare)(snd_pcm_t *pcm,
1288 snd_pcm_hw_params_t *params),
1289 int (*cchange)(snd_pcm_t *pcm,
1290 snd_pcm_hw_params_t *params,
1291 snd_pcm_hw_params_t *sparams),
1292 int (*sprepare)(snd_pcm_t *pcm,
1293 snd_pcm_hw_params_t *params),
1294 int (*schange)(snd_pcm_t *pcm,
1295 snd_pcm_hw_params_t *params,
1296 snd_pcm_hw_params_t *sparams),
1297 int (*srefine)(snd_pcm_t *pcm,
1298 snd_pcm_hw_params_t *sparams))
1299
1300 {
1301 snd_pcm_hw_params_t sparams;
1302 int err;
1303 unsigned int cmask, changed;
1304
1305 awalsa_debug("\n");
1306 err = cprepare(pcm, params);
1307 if (err < 0)
1308 return err;
1309 err = sprepare(pcm, &sparams);
1310 if (err < 0) {
1311 awalsa_err("Slave PCM not usable\n");
1312 return err;
1313 }
1314 #ifdef RULES_DEBUG
1315 awalsa_info("hw_refine_slave - enter '%s'\n", pcm->name);
1316 #endif
1317 do {
1318 cmask = params->cmask;
1319 params->cmask = 0;
1320 #ifdef RULES_DEBUG
1321 awalsa_info("schange '%s' (client)\n", pcm->name);
1322 snd_pcm_hw_params_dump(params);
1323 awalsa_info("schange '%s' (slave)\n", pcm->name);
1324 snd_pcm_hw_params_dump(&sparams);
1325 #endif
1326 err = schange(pcm, params, &sparams);
1327 if (err >= 0) {
1328 #ifdef RULES_DEBUG
1329 awalsa_info("srefine '%s' (client)\n", pcm->name);
1330 snd_pcm_hw_params_dump(params);
1331 awalsa_info("srefine '%s' (slave)\n", pcm->name);
1332 snd_pcm_hw_params_dump(&sparams);
1333 #endif
1334 err = srefine(pcm, &sparams);
1335 if (err < 0) {
1336 #ifdef RULES_DEBUG
1337 awalsa_info("srefine '%s', err < 0 (%i) (client)\n", pcm->name, err);
1338 snd_pcm_hw_params_dump(params);
1339 awalsa_info("srefine '%s', err < 0 (%i) (slave)\n", pcm->name, err);
1340 snd_pcm_hw_params_dump(&sparams);
1341 #endif
1342 cchange(pcm, params, &sparams);
1343 return err;
1344 }
1345 } else {
1346 #ifdef RULES_DEBUG
1347 awalsa_info("schange '%s', err < 0 (%i) (client)\n", pcm->name, err);
1348 snd_pcm_hw_params_dump(params);
1349 awalsa_info("schange '%s', err < 0 (%i) (slave)\n", pcm->name, err);
1350 snd_pcm_hw_params_dump(&sparams);
1351 #endif
1352 cchange(pcm, params, &sparams);
1353 return err;
1354 }
1355 #ifdef RULES_DEBUG
1356 awalsa_info("cchange '%s'\n", pcm->name);
1357 #endif
1358 err = cchange(pcm, params, &sparams);
1359 if (err < 0)
1360 return err;
1361 #ifdef RULES_DEBUG
1362 awalsa_info("refine_soft '%s'\n", pcm->name);
1363 #endif
1364 err = snd_pcm_hw_refine_soft(pcm, params);
1365 changed = params->cmask;
1366 params->cmask |= cmask;
1367 if (err < 0)
1368 return err;
1369 #ifdef RULES_DEBUG
1370 awalsa_info("refine_soft ok '%s'\n", pcm->name);
1371 #endif
1372 } while (changed);
1373 #ifdef RULES_DEBUG
1374 awalsa_info("refine_slave - leave '%s'\n", pcm->name);
1375 #endif
1376 return 0;
1377 }
1378
snd_pcm_hw_params_slave(snd_pcm_t * pcm,snd_pcm_hw_params_t * params,int (* cchange)(snd_pcm_t * pcm,snd_pcm_hw_params_t * params,snd_pcm_hw_params_t * sparams),int (* sprepare)(snd_pcm_t * pcm,snd_pcm_hw_params_t * params),int (* schange)(snd_pcm_t * pcm,snd_pcm_hw_params_t * params,snd_pcm_hw_params_t * sparams),int (* sparams)(snd_pcm_t * pcm,snd_pcm_hw_params_t * sparams))1379 int snd_pcm_hw_params_slave(snd_pcm_t *pcm, snd_pcm_hw_params_t *params,
1380 int (*cchange)(snd_pcm_t *pcm,
1381 snd_pcm_hw_params_t *params,
1382 snd_pcm_hw_params_t *sparams),
1383 int (*sprepare)(snd_pcm_t *pcm,
1384 snd_pcm_hw_params_t *params),
1385 int (*schange)(snd_pcm_t *pcm,
1386 snd_pcm_hw_params_t *params,
1387 snd_pcm_hw_params_t *sparams),
1388 int (*sparams)(snd_pcm_t *pcm,
1389 snd_pcm_hw_params_t *sparams))
1390
1391 {
1392 snd_pcm_hw_params_t slave_params;
1393 int err;
1394 err = sprepare(pcm, &slave_params);
1395 if (err < 0)
1396 return err;
1397 err = schange(pcm, params, &slave_params);
1398 if (err < 0)
1399 return err;
1400 err = sparams(pcm, &slave_params);
1401 if (err < 0)
1402 cchange(pcm, params, &slave_params);
1403 return err;
1404 }
1405
1406 #if 0
1407 #define CHOOSE_DEBUG
1408 #endif
1409
1410 /* Choose one configuration from configuration space defined by PARAMS
1411 The configuration chosen is that obtained fixing in this order:
1412 first access
1413 first format
1414 first subformat
1415 min channels
1416 min rate
1417 min period time
1418 max buffer size
1419 min tick time
1420 */
snd_pcm_hw_params_choose(snd_pcm_t * pcm,snd_pcm_hw_params_t * params)1421 static int snd_pcm_hw_params_choose(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
1422 {
1423 int err;
1424 #ifdef CHOOSE_DEBUG
1425 awalsa_info("CHOOSE called:\n");
1426 snd_pcm_hw_params_dump(params);
1427 #endif
1428
1429 err = snd_pcm_hw_param_set_first(pcm, params, SND_PCM_HW_PARAM_ACCESS, NULL, 0);
1430 if (err < 0)
1431 return err;
1432 err = snd_pcm_hw_param_set_first(pcm, params, SND_PCM_HW_PARAM_FORMAT, NULL, 0);
1433 if (err < 0)
1434 return err;
1435 err = snd_pcm_hw_param_set_first(pcm, params, SND_PCM_HW_PARAM_CHANNELS, NULL, 0);
1436 if (err < 0)
1437 return err;
1438 err = snd_pcm_hw_param_set_first(pcm, params, SND_PCM_HW_PARAM_RATE, NULL, 0);
1439 if (err < 0)
1440 return err;
1441 #if 0
1442 if (pcm->minperiodtime > 0) {
1443 unsigned int min, max;
1444 int dir = 1;
1445 err = snd_pcm_hw_param_get_min(params, SND_PCM_HW_PARAM_PERIOD_TIME, &min, &dir);
1446 if (err >= 0)
1447 err = snd_pcm_hw_param_get_max(params, SND_PCM_HW_PARAM_PERIOD_TIME, &max, &dir);
1448 if (err >= 0 && (long)min < pcm->minperiodtime &&
1449 (long)max > pcm->minperiodtime) {
1450 min = pcm->minperiodtime; dir = 1;
1451 snd_pcm_hw_param_set_min(pcm, params, SND_CHANGE, SND_PCM_HW_PARAM_PERIOD_TIME, &min, &dir);
1452 }
1453 }
1454 #endif
1455
1456 #if 0
1457 /* old mode */
1458 err = snd_pcm_hw_param_set_first(pcm, params, SND_PCM_HW_PARAM_PERIOD_TIME, NULL, 0);
1459 if (err < 0)
1460 return err;
1461 err = snd_pcm_hw_param_set_first(pcm, params, SND_PCM_HW_PARAM_PERIOD_SIZE, NULL, 0);
1462 if (err < 0)
1463 return err;
1464 err = snd_pcm_hw_param_set_last(pcm, params, SND_PCM_HW_PARAM_BUFFER_SIZE, NULL, 0);
1465 if (err < 0)
1466 return err;
1467 #else
1468 /* determine buffer size first */
1469 err = snd_pcm_hw_param_set_last(pcm, params, SND_PCM_HW_PARAM_BUFFER_SIZE, NULL, 0);
1470 if (err < 0)
1471 return err;
1472 err = snd_pcm_hw_param_set_first(pcm, params, SND_PCM_HW_PARAM_PERIOD_SIZE, NULL, 0);
1473 if (err < 0)
1474 return err;
1475 err = snd_pcm_hw_param_set_first(pcm, params, SND_PCM_HW_PARAM_PERIOD_TIME, NULL, 0);
1476 if (err < 0)
1477 return err;
1478 #endif
1479
1480 #if 0
1481 err = snd_pcm_hw_param_set_first(pcm, params, SND_PCM_HW_PARAM_TICK_TIME, NULL, 0);
1482 if (err < 0)
1483 return err;
1484 #endif
1485 #ifdef CHOOSE_DEBUG
1486 awalsa_info("choose done\n");
1487 snd_pcm_hw_params_dump(params);
1488 #endif
1489 return 0;
1490 }
1491
snd_pcm_sw_params_default(snd_pcm_t * pcm,snd_pcm_sw_params_t * params)1492 static int snd_pcm_sw_params_default(snd_pcm_t *pcm, snd_pcm_sw_params_t *params)
1493 {
1494 params->avail_min = pcm->period_size;
1495 params->start_threshold = 1;
1496 params->stop_threshold = pcm->buffer_size;
1497 params->silence_size = 0;
1498 params->boundary = pcm->buffer_size;
1499 if (!pcm->buffer_size) {
1500 awalsa_info("buffer size is 0...\n");
1501 return 0;
1502 }
1503 while (params->boundary * 2 <= LONG_MAX - pcm->buffer_size)
1504 params->boundary *= 2;
1505 return 0;
1506 }
1507
_snd_pcm_hw_params_internal(snd_pcm_t * pcm,snd_pcm_hw_params_t * params)1508 int _snd_pcm_hw_params_internal(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
1509 {
1510 int ret;
1511 snd_pcm_sw_params_t sw;
1512
1513 awalsa_debug("\n");
1514 assert(pcm->ops->hw_params);
1515
1516 ret = snd_pcm_hw_refine(pcm, params);
1517 if (ret < 0)
1518 return ret;
1519 snd_pcm_hw_params_choose(pcm, params);
1520 if (pcm->setup) {
1521 ret = snd_pcm_hw_free(pcm);
1522 if (ret < 0)
1523 return ret;
1524 }
1525
1526 ret = pcm->ops->hw_params(pcm->op_arg, params);
1527 if (ret < 0)
1528 return ret;
1529
1530 pcm->setup = 1;
1531
1532 snd_pcm_hw_params_get_access(params, &pcm->access);
1533 snd_pcm_hw_params_get_format(params, &pcm->format);
1534 snd_pcm_hw_params_get_channels(params, &pcm->channels);
1535 snd_pcm_hw_params_get_rate(params, &pcm->rate, NULL);
1536 snd_pcm_hw_params_get_period_time(params, &pcm->period_time, NULL);
1537 snd_pcm_hw_params_get_period_size(params, &pcm->period_size, NULL);
1538 snd_pcm_hw_params_get_buffer_size(params, &pcm->buffer_size);
1539 pcm->sample_bits = snd_pcm_format_physical_width(pcm->format);
1540 pcm->frame_bits = pcm->sample_bits * pcm->channels;
1541
1542 awalsa_debug("pcm type=%s\n", snd_pcm_type_name(snd_pcm_type(pcm)));
1543 awalsa_debug("access:%u\n", pcm->access);
1544 awalsa_debug("format:%u\n", pcm->format);
1545 awalsa_debug("channels:%u\n", pcm->channels);
1546 awalsa_debug("rate:%u\n", pcm->rate);
1547 awalsa_debug("period_time:%u\n", pcm->period_time);
1548 awalsa_debug("period_size:%lu\n", pcm->period_size);
1549 awalsa_debug("buffer_size:%lu\n", pcm->buffer_size);
1550 awalsa_debug("sample_bits:%u\n", pcm->sample_bits);
1551 awalsa_debug("frame_bits:%u\n", pcm->frame_bits);
1552
1553 snd_pcm_sw_params_default(pcm, &sw);
1554 ret = snd_pcm_sw_params(pcm, &sw);
1555 if (ret < 0)
1556 return ret;
1557
1558 if (pcm->mmap_rw ||
1559 pcm->access == SND_PCM_ACCESS_MMAP_INTERLEAVED ||
1560 pcm->access == SND_PCM_ACCESS_MMAP_NONINTERLEAVED ||
1561 pcm->access == SND_PCM_ACCESS_MMAP_COMPLEX) {
1562 ret = snd_pcm_mmap(pcm);
1563 }
1564 if (ret < 0)
1565 return ret;
1566 return 0;
1567 }
1568