1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 2008-2009 Ariff Abdullah <ariff@FreeBSD.org>
5 * All rights reserved.
6 * Copyright (c) 2024-2025 The FreeBSD Foundation
7 *
8 * Portions of this software were developed by Christos Margiolis
9 * <christos@FreeBSD.org> under sponsorship from the FreeBSD Foundation.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 */
32
33 /*
34 * feeder_matrix: Generic any-to-any channel matrixing. Probably not the
35 * accurate way of doing things, but it should be fast and
36 * transparent enough, not to mention capable of handling
37 * possible non-standard way of multichannel interleaving
38 * order. In other words, it is tough to break.
39 *
40 * The Good:
41 * + very generic and compact, provided that the supplied matrix map is in a
42 * sane form.
43 * + should be fast enough.
44 *
45 * The Bad:
46 * + somebody might disagree with it.
47 * + 'matrix' is kind of 0x7a69, due to prolong mental block.
48 */
49
50 #ifdef _KERNEL
51 #ifdef HAVE_KERNEL_OPTION_HEADERS
52 #include "opt_snd.h"
53 #endif
54 #include <dev/sound/pcm/sound.h>
55 #include <dev/sound/pcm/pcm.h>
56 #include "feeder_if.h"
57
58 #define SND_USE_FXDIV
59 #include "snd_fxdiv_gen.h"
60 #endif
61
62 #define FEEDMATRIX_RESERVOIR (SND_CHN_MAX * PCM_32_BPS)
63
64 #define SND_CHN_T_EOF 0x00e0fe0f
65 #define SND_CHN_T_NULL 0x0e0e0e0e
66
67 struct feed_matrix_info {
68 uint32_t fmt;
69 uint32_t bps;
70 uint32_t ialign, oalign;
71 uint32_t in, out;
72 struct {
73 int chn[SND_CHN_T_MAX + 1];
74 int mul, shift;
75 } matrix[SND_CHN_T_MAX + 1];
76 uint8_t reservoir[FEEDMATRIX_RESERVOIR];
77 };
78
79 static struct pcmchan_matrix feeder_matrix_maps[SND_CHN_MATRIX_MAX] = {
80 [SND_CHN_MATRIX_1_0] = SND_CHN_MATRIX_MAP_1_0,
81 [SND_CHN_MATRIX_2_0] = SND_CHN_MATRIX_MAP_2_0,
82 [SND_CHN_MATRIX_2_1] = SND_CHN_MATRIX_MAP_2_1,
83 [SND_CHN_MATRIX_3_0] = SND_CHN_MATRIX_MAP_3_0,
84 [SND_CHN_MATRIX_3_1] = SND_CHN_MATRIX_MAP_3_1,
85 [SND_CHN_MATRIX_4_0] = SND_CHN_MATRIX_MAP_4_0,
86 [SND_CHN_MATRIX_4_1] = SND_CHN_MATRIX_MAP_4_1,
87 [SND_CHN_MATRIX_5_0] = SND_CHN_MATRIX_MAP_5_0,
88 [SND_CHN_MATRIX_5_1] = SND_CHN_MATRIX_MAP_5_1,
89 [SND_CHN_MATRIX_6_0] = SND_CHN_MATRIX_MAP_6_0,
90 [SND_CHN_MATRIX_6_1] = SND_CHN_MATRIX_MAP_6_1,
91 [SND_CHN_MATRIX_7_0] = SND_CHN_MATRIX_MAP_7_0,
92 [SND_CHN_MATRIX_7_1] = SND_CHN_MATRIX_MAP_7_1
93 };
94
95 static int feeder_matrix_default_ids[9] = {
96 [0] = SND_CHN_MATRIX_UNKNOWN,
97 [1] = SND_CHN_MATRIX_1,
98 [2] = SND_CHN_MATRIX_2,
99 [3] = SND_CHN_MATRIX_3,
100 [4] = SND_CHN_MATRIX_4,
101 [5] = SND_CHN_MATRIX_5,
102 [6] = SND_CHN_MATRIX_6,
103 [7] = SND_CHN_MATRIX_7,
104 [8] = SND_CHN_MATRIX_8
105 };
106
107 #ifdef _KERNEL
108 #define FEEDMATRIX_CLIP_CHECK(...)
109 #else
110 #define FEEDMATRIX_CLIP_CHECK(v, BIT) do { \
111 if ((v) < PCM_S##BIT##_MIN || (v) > PCM_S##BIT##_MAX) \
112 errx(1, "\n\n%s(): Sample clipping: %jd\n", \
113 __func__, (intmax_t)(v)); \
114 } while (0)
115 #endif
116
117 __always_inline static void
feed_matrix_apply(struct feed_matrix_info * info,uint8_t * src,uint8_t * dst,uint32_t count,const uint32_t fmt)118 feed_matrix_apply(struct feed_matrix_info *info, uint8_t *src, uint8_t *dst,
119 uint32_t count, const uint32_t fmt)
120 {
121 intpcm64_t accum;
122 intpcm_t v;
123 int i, j;
124
125 do {
126 for (i = 0; info->matrix[i].chn[0] != SND_CHN_T_EOF; i++) {
127 if (info->matrix[i].chn[0] == SND_CHN_T_NULL) {
128 pcm_sample_write(dst, 0, fmt);
129 dst += info->bps;
130 continue;
131 } else if (info->matrix[i].chn[1] == SND_CHN_T_EOF) {
132 v = pcm_sample_read(src +
133 info->matrix[i].chn[0], fmt);
134 pcm_sample_write(dst, v, fmt);
135 dst += info->bps;
136 continue;
137 }
138
139 accum = 0;
140 for (j = 0; info->matrix[i].chn[j] != SND_CHN_T_EOF;
141 j++) {
142 v = pcm_sample_read(src +
143 info->matrix[i].chn[j], fmt);
144 accum += v;
145 }
146
147 accum = (accum * info->matrix[i].mul) >>
148 info->matrix[i].shift;
149
150 FEEDMATRIX_CLIP_CHECK(accum, AFMT_BIT(fmt));
151
152 v = pcm_clamp(accum, fmt);
153 pcm_sample_write(dst, v, fmt);
154 dst += info->bps;
155 }
156 src += info->ialign;
157 } while (--count != 0);
158 }
159
160 static void
feed_matrix_reset(struct feed_matrix_info * info)161 feed_matrix_reset(struct feed_matrix_info *info)
162 {
163 uint32_t i, j;
164
165 for (i = 0; i < nitems(info->matrix); i++) {
166 for (j = 0;
167 j < (sizeof(info->matrix[i].chn) /
168 sizeof(info->matrix[i].chn[0])); j++) {
169 info->matrix[i].chn[j] = SND_CHN_T_EOF;
170 }
171 info->matrix[i].mul = 1;
172 info->matrix[i].shift = 0;
173 }
174 }
175
176 static int
feed_matrix_setup(struct feed_matrix_info * info,struct pcmchan_matrix * m_in,struct pcmchan_matrix * m_out)177 feed_matrix_setup(struct feed_matrix_info *info, struct pcmchan_matrix *m_in,
178 struct pcmchan_matrix *m_out)
179 {
180 uint32_t i, j, ch, in_mask, merge_mask;
181 int mul, shift;
182
183 if (info == NULL || m_in == NULL || m_out == NULL ||
184 AFMT_CHANNEL(info->in) != m_in->channels ||
185 AFMT_CHANNEL(info->out) != m_out->channels ||
186 m_in->channels < SND_CHN_MIN || m_in->channels > SND_CHN_MAX ||
187 m_out->channels < SND_CHN_MIN || m_out->channels > SND_CHN_MAX)
188 return (EINVAL);
189
190 feed_matrix_reset(info);
191
192 /*
193 * If both in and out are part of standard matrix and identical, skip
194 * everything altogether.
195 */
196 if (m_in->id == m_out->id && !(m_in->id < SND_CHN_MATRIX_BEGIN ||
197 m_in->id > SND_CHN_MATRIX_END))
198 return (0);
199
200 /*
201 * Special case for mono input matrix. If the output supports
202 * possible 'center' channel, route it there. Otherwise, let it be
203 * matrixed to left/right.
204 */
205 if (m_in->id == SND_CHN_MATRIX_1_0) {
206 if (m_out->id == SND_CHN_MATRIX_1_0)
207 in_mask = SND_CHN_T_MASK_FL;
208 else if (m_out->mask & SND_CHN_T_MASK_FC)
209 in_mask = SND_CHN_T_MASK_FC;
210 else
211 in_mask = SND_CHN_T_MASK_FL | SND_CHN_T_MASK_FR;
212 } else
213 in_mask = m_in->mask;
214
215 /* Merge, reduce, expand all possibilites. */
216 for (ch = SND_CHN_T_BEGIN; ch <= SND_CHN_T_END &&
217 m_out->map[ch].type != SND_CHN_T_MAX; ch += SND_CHN_T_STEP) {
218 merge_mask = m_out->map[ch].members & in_mask;
219 if (merge_mask == 0) {
220 info->matrix[ch].chn[0] = SND_CHN_T_NULL;
221 continue;
222 }
223
224 j = 0;
225 for (i = SND_CHN_T_BEGIN; i <= SND_CHN_T_END;
226 i += SND_CHN_T_STEP) {
227 if (merge_mask & (1 << i)) {
228 if (m_in->offset[i] >= 0 &&
229 m_in->offset[i] < (int)m_in->channels)
230 info->matrix[ch].chn[j++] =
231 m_in->offset[i] * info->bps;
232 else {
233 info->matrix[ch].chn[j++] =
234 SND_CHN_T_EOF;
235 break;
236 }
237 }
238 }
239
240 #define FEEDMATRIX_ATTN_SHIFT 16
241
242 if (j > 1) {
243 /*
244 * XXX For channel that require accumulation from
245 * multiple channels, apply a slight attenuation to
246 * avoid clipping.
247 */
248 mul = (1 << (FEEDMATRIX_ATTN_SHIFT - 1)) + 143 - j;
249 shift = FEEDMATRIX_ATTN_SHIFT;
250 while ((mul & 1) == 0 && shift > 0) {
251 mul >>= 1;
252 shift--;
253 }
254 info->matrix[ch].mul = mul;
255 info->matrix[ch].shift = shift;
256 }
257 }
258
259 #ifndef _KERNEL
260 fprintf(stderr, "Total: %d\n", ch);
261
262 for (i = 0; info->matrix[i].chn[0] != SND_CHN_T_EOF; i++) {
263 fprintf(stderr, "%d: [", i);
264 for (j = 0; info->matrix[i].chn[j] != SND_CHN_T_EOF; j++) {
265 if (j != 0)
266 fprintf(stderr, ", ");
267 fprintf(stderr, "%d",
268 (info->matrix[i].chn[j] == SND_CHN_T_NULL) ?
269 0xffffffff : info->matrix[i].chn[j] / info->bps);
270 }
271 fprintf(stderr, "] attn: (x * %d) >> %d\n",
272 info->matrix[i].mul, info->matrix[i].shift);
273 }
274 #endif
275
276 return (0);
277 }
278
279 static int
feed_matrix_init(struct pcm_feeder * f)280 feed_matrix_init(struct pcm_feeder *f)
281 {
282 struct feed_matrix_info *info;
283 struct pcmchan_matrix *m_in, *m_out;
284 int ret;
285
286 if (AFMT_ENCODING(f->desc->in) != AFMT_ENCODING(f->desc->out))
287 return (EINVAL);
288
289 info = malloc(sizeof(*info), M_DEVBUF, M_NOWAIT | M_ZERO);
290 if (info == NULL)
291 return (ENOMEM);
292
293 info->in = f->desc->in;
294 info->out = f->desc->out;
295 info->fmt = AFMT_ENCODING(info->in);
296 info->bps = AFMT_BPS(info->in);
297 info->ialign = AFMT_ALIGN(info->in);
298 info->oalign = AFMT_ALIGN(info->out);
299
300 m_in = feeder_matrix_format_map(info->in);
301 m_out = feeder_matrix_format_map(info->out);
302
303 ret = feed_matrix_setup(info, m_in, m_out);
304 if (ret != 0) {
305 free(info, M_DEVBUF);
306 return (ret);
307 }
308
309 f->data = info;
310
311 return (0);
312 }
313
314 static int
feed_matrix_free(struct pcm_feeder * f)315 feed_matrix_free(struct pcm_feeder *f)
316 {
317 struct feed_matrix_info *info;
318
319 info = f->data;
320 if (info != NULL)
321 free(info, M_DEVBUF);
322
323 f->data = NULL;
324
325 return (0);
326 }
327
328 static int
feed_matrix_feed(struct pcm_feeder * f,struct pcm_channel * c,uint8_t * b,uint32_t count,void * source)329 feed_matrix_feed(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b,
330 uint32_t count, void *source)
331 {
332 struct feed_matrix_info *info;
333 uint32_t j, inmax;
334 uint8_t *src, *dst;
335
336 info = f->data;
337 if (info->matrix[0].chn[0] == SND_CHN_T_EOF)
338 return (FEEDER_FEED(f->source, c, b, count, source));
339
340 dst = b;
341 count = SND_FXROUND(count, info->oalign);
342 inmax = info->ialign + info->oalign;
343
344 /*
345 * This loop might look simmilar to other feeder_* loops, but be
346 * advised: matrixing might involve overlapping (think about
347 * swapping end to front or something like that). In this regard it
348 * might be simmilar to feeder_format, but feeder_format works on
349 * 'sample' domain where it can be fitted into single 32bit integer
350 * while matrixing works on 'sample frame' domain.
351 */
352 do {
353 if (count < info->oalign)
354 break;
355
356 if (count < inmax) {
357 src = info->reservoir;
358 j = info->ialign;
359 } else {
360 if (info->ialign == info->oalign)
361 j = count - info->oalign;
362 else if (info->ialign > info->oalign)
363 j = SND_FXROUND(count - info->oalign,
364 info->ialign);
365 else
366 j = (SND_FXDIV(count, info->oalign) - 1) *
367 info->ialign;
368 src = dst + count - j;
369 }
370
371 j = SND_FXDIV(FEEDER_FEED(f->source, c, src, j, source),
372 info->ialign);
373 if (j == 0)
374 break;
375
376 /* Optimize some common formats. */
377 switch (info->fmt) {
378 case AFMT_S16_NE:
379 feed_matrix_apply(info, src, dst, j, AFMT_S16_NE);
380 break;
381 case AFMT_S24_NE:
382 feed_matrix_apply(info, src, dst, j, AFMT_S24_NE);
383 break;
384 case AFMT_S32_NE:
385 feed_matrix_apply(info, src, dst, j, AFMT_S32_NE);
386 break;
387 default:
388 feed_matrix_apply(info, src, dst, j, info->fmt);
389 break;
390 }
391
392 j *= info->oalign;
393 dst += j;
394 count -= j;
395
396 } while (count != 0);
397
398 return (dst - b);
399 }
400
401 static kobj_method_t feeder_matrix_methods[] = {
402 KOBJMETHOD(feeder_init, feed_matrix_init),
403 KOBJMETHOD(feeder_free, feed_matrix_free),
404 KOBJMETHOD(feeder_feed, feed_matrix_feed),
405 KOBJMETHOD_END
406 };
407
408 FEEDER_DECLARE(feeder_matrix, FEEDER_MATRIX);
409
410 /* External */
411 int
feeder_matrix_setup(struct pcm_feeder * f,struct pcmchan_matrix * m_in,struct pcmchan_matrix * m_out)412 feeder_matrix_setup(struct pcm_feeder *f, struct pcmchan_matrix *m_in,
413 struct pcmchan_matrix *m_out)
414 {
415
416 if (f == NULL || f->desc == NULL || f->class->type != FEEDER_MATRIX ||
417 f->data == NULL)
418 return (EINVAL);
419
420 return (feed_matrix_setup(f->data, m_in, m_out));
421 }
422
423 /*
424 * feeder_matrix_default_id(): For a given number of channels, return
425 * default preferred id (example: both 5.1 and
426 * 6.0 are simply 6 channels, but 5.1 is more
427 * preferable).
428 */
429 int
feeder_matrix_default_id(uint32_t ch)430 feeder_matrix_default_id(uint32_t ch)
431 {
432
433 if (ch < feeder_matrix_maps[SND_CHN_MATRIX_BEGIN].channels ||
434 ch > feeder_matrix_maps[SND_CHN_MATRIX_END].channels)
435 return (SND_CHN_MATRIX_UNKNOWN);
436
437 return (feeder_matrix_maps[feeder_matrix_default_ids[ch]].id);
438 }
439
440 /*
441 * feeder_matrix_default_channel_map(): Ditto, but return matrix map
442 * instead.
443 */
444 struct pcmchan_matrix *
feeder_matrix_default_channel_map(uint32_t ch)445 feeder_matrix_default_channel_map(uint32_t ch)
446 {
447
448 if (ch < feeder_matrix_maps[SND_CHN_MATRIX_BEGIN].channels ||
449 ch > feeder_matrix_maps[SND_CHN_MATRIX_END].channels)
450 return (NULL);
451
452 return (&feeder_matrix_maps[feeder_matrix_default_ids[ch]]);
453 }
454
455 /*
456 * feeder_matrix_default_format(): For a given audio format, return the
457 * proper audio format based on preferable
458 * matrix.
459 */
460 uint32_t
feeder_matrix_default_format(uint32_t format)461 feeder_matrix_default_format(uint32_t format)
462 {
463 struct pcmchan_matrix *m;
464 uint32_t i, ch, ext;
465
466 ch = AFMT_CHANNEL(format);
467 ext = AFMT_EXTCHANNEL(format);
468
469 if (ext != 0) {
470 for (i = SND_CHN_MATRIX_BEGIN; i <= SND_CHN_MATRIX_END; i++) {
471 if (feeder_matrix_maps[i].channels == ch &&
472 feeder_matrix_maps[i].ext == ext)
473 return (SND_FORMAT(format, ch, ext));
474 }
475 }
476
477 m = feeder_matrix_default_channel_map(ch);
478 if (m == NULL)
479 return (0x00000000);
480
481 return (SND_FORMAT(format, ch, m->ext));
482 }
483
484 /*
485 * feeder_matrix_format_id(): For a given audio format, return its matrix
486 * id.
487 */
488 int
feeder_matrix_format_id(uint32_t format)489 feeder_matrix_format_id(uint32_t format)
490 {
491 uint32_t i, ch, ext;
492
493 ch = AFMT_CHANNEL(format);
494 ext = AFMT_EXTCHANNEL(format);
495
496 for (i = SND_CHN_MATRIX_BEGIN; i <= SND_CHN_MATRIX_END; i++) {
497 if (feeder_matrix_maps[i].channels == ch &&
498 feeder_matrix_maps[i].ext == ext)
499 return (feeder_matrix_maps[i].id);
500 }
501
502 return (SND_CHN_MATRIX_UNKNOWN);
503 }
504
505 /*
506 * feeder_matrix_format_map(): For a given audio format, return its matrix
507 * map.
508 */
509 struct pcmchan_matrix *
feeder_matrix_format_map(uint32_t format)510 feeder_matrix_format_map(uint32_t format)
511 {
512 uint32_t i, ch, ext;
513
514 ch = AFMT_CHANNEL(format);
515 ext = AFMT_EXTCHANNEL(format);
516
517 for (i = SND_CHN_MATRIX_BEGIN; i <= SND_CHN_MATRIX_END; i++) {
518 if (feeder_matrix_maps[i].channels == ch &&
519 feeder_matrix_maps[i].ext == ext)
520 return (&feeder_matrix_maps[i]);
521 }
522
523 return (NULL);
524 }
525
526 /*
527 * feeder_matrix_id_map(): For a given matrix id, return its matrix map.
528 */
529 struct pcmchan_matrix *
feeder_matrix_id_map(int id)530 feeder_matrix_id_map(int id)
531 {
532
533 if (id < SND_CHN_MATRIX_BEGIN || id > SND_CHN_MATRIX_END)
534 return (NULL);
535
536 return (&feeder_matrix_maps[id]);
537 }
538
539 /*
540 * feeder_matrix_compare(): Compare the simmilarities of matrices.
541 */
542 int
feeder_matrix_compare(struct pcmchan_matrix * m_in,struct pcmchan_matrix * m_out)543 feeder_matrix_compare(struct pcmchan_matrix *m_in, struct pcmchan_matrix *m_out)
544 {
545 uint32_t i;
546
547 if (m_in == m_out)
548 return (0);
549
550 if (m_in->channels != m_out->channels || m_in->ext != m_out->ext ||
551 m_in->mask != m_out->mask)
552 return (1);
553
554 for (i = 0; i < nitems(m_in->map); i++) {
555 if (m_in->map[i].type != m_out->map[i].type)
556 return (1);
557 if (m_in->map[i].type == SND_CHN_T_MAX)
558 break;
559 if (m_in->map[i].members != m_out->map[i].members)
560 return (1);
561 if (i <= SND_CHN_T_END) {
562 if (m_in->offset[m_in->map[i].type] !=
563 m_out->offset[m_out->map[i].type])
564 return (1);
565 }
566 }
567
568 return (0);
569 }
570
571 /*
572 * XXX 4front interpretation of "surround" is ambigous and sort of
573 * conflicting with "rear"/"back". Map it to "side". Well..
574 * who cares?
575 */
576 static int snd_chn_to_oss[SND_CHN_T_MAX] = {
577 [SND_CHN_T_FL] = CHID_L,
578 [SND_CHN_T_FR] = CHID_R,
579 [SND_CHN_T_FC] = CHID_C,
580 [SND_CHN_T_LF] = CHID_LFE,
581 [SND_CHN_T_SL] = CHID_LS,
582 [SND_CHN_T_SR] = CHID_RS,
583 [SND_CHN_T_BL] = CHID_LR,
584 [SND_CHN_T_BR] = CHID_RR
585 };
586
587 #define SND_CHN_OSS_VALIDMASK \
588 (SND_CHN_T_MASK_FL | SND_CHN_T_MASK_FR | \
589 SND_CHN_T_MASK_FC | SND_CHN_T_MASK_LF | \
590 SND_CHN_T_MASK_SL | SND_CHN_T_MASK_SR | \
591 SND_CHN_T_MASK_BL | SND_CHN_T_MASK_BR)
592
593 #define SND_CHN_OSS_MAX 8
594 #define SND_CHN_OSS_BEGIN CHID_L
595 #define SND_CHN_OSS_END CHID_RR
596
597 static int oss_to_snd_chn[SND_CHN_OSS_END + 1] = {
598 [CHID_L] = SND_CHN_T_FL,
599 [CHID_R] = SND_CHN_T_FR,
600 [CHID_C] = SND_CHN_T_FC,
601 [CHID_LFE] = SND_CHN_T_LF,
602 [CHID_LS] = SND_CHN_T_SL,
603 [CHID_RS] = SND_CHN_T_SR,
604 [CHID_LR] = SND_CHN_T_BL,
605 [CHID_RR] = SND_CHN_T_BR
606 };
607
608 /*
609 * Used by SNDCTL_DSP_GET_CHNORDER.
610 */
611 int
feeder_matrix_oss_get_channel_order(struct pcmchan_matrix * m,unsigned long long * map)612 feeder_matrix_oss_get_channel_order(struct pcmchan_matrix *m,
613 unsigned long long *map)
614 {
615 unsigned long long tmpmap;
616 uint32_t i;
617
618 if (m == NULL || map == NULL || (m->mask & ~SND_CHN_OSS_VALIDMASK) ||
619 m->channels > SND_CHN_OSS_MAX)
620 return (EINVAL);
621
622 tmpmap = 0x0000000000000000ULL;
623
624 for (i = 0; i < SND_CHN_OSS_MAX && m->map[i].type != SND_CHN_T_MAX;
625 i++) {
626 if ((1 << m->map[i].type) & ~SND_CHN_OSS_VALIDMASK)
627 return (EINVAL);
628 tmpmap |=
629 (unsigned long long)snd_chn_to_oss[m->map[i].type] <<
630 (i * 4);
631 }
632
633 *map = tmpmap;
634
635 return (0);
636 }
637
638 /*
639 * Used by SNDCTL_DSP_SET_CHNORDER.
640 */
641 int
feeder_matrix_oss_set_channel_order(struct pcmchan_matrix * m,unsigned long long * map)642 feeder_matrix_oss_set_channel_order(struct pcmchan_matrix *m,
643 unsigned long long *map)
644 {
645 struct pcmchan_matrix tmp;
646 uint32_t chmask, i;
647 int ch, cheof;
648
649 if (m == NULL || map == NULL || (m->mask & ~SND_CHN_OSS_VALIDMASK) ||
650 m->channels > SND_CHN_OSS_MAX || (*map & 0xffffffff00000000ULL))
651 return (EINVAL);
652
653 tmp = *m;
654 tmp.channels = 0;
655 tmp.ext = 0;
656 tmp.mask = 0;
657 memset(tmp.offset, -1, sizeof(tmp.offset));
658 cheof = 0;
659
660 for (i = 0; i < SND_CHN_OSS_MAX; i++) {
661 ch = (*map >> (i * 4)) & 0xf;
662 if (ch < SND_CHN_OSS_BEGIN) {
663 if (cheof == 0 && m->map[i].type != SND_CHN_T_MAX)
664 return (EINVAL);
665 cheof++;
666 tmp.map[i] = m->map[i];
667 continue;
668 } else if (ch > SND_CHN_OSS_END)
669 return (EINVAL);
670 else if (cheof != 0)
671 return (EINVAL);
672 ch = oss_to_snd_chn[ch];
673 chmask = 1 << ch;
674 /* channel not exist in matrix */
675 if (!(chmask & m->mask))
676 return (EINVAL);
677 /* duplicated channel */
678 if (chmask & tmp.mask)
679 return (EINVAL);
680 tmp.map[i] = m->map[m->offset[ch]];
681 if (tmp.map[i].type != ch)
682 return (EINVAL);
683 tmp.offset[ch] = i;
684 tmp.mask |= chmask;
685 tmp.channels++;
686 if (chmask & SND_CHN_T_MASK_LF)
687 tmp.ext++;
688 }
689
690 if (tmp.channels != m->channels || tmp.ext != m->ext ||
691 tmp.mask != m->mask ||
692 tmp.map[m->channels].type != SND_CHN_T_MAX)
693 return (EINVAL);
694
695 *m = tmp;
696
697 return (0);
698 }
699