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, &params1, 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