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 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 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 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 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 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 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 struct pcm_feederdesc feeder_matrix_desc[] = { 402 { FEEDER_MATRIX, 0, 0, 0, 0 }, 403 { 0, 0, 0, 0, 0 } 404 }; 405 406 static kobj_method_t feeder_matrix_methods[] = { 407 KOBJMETHOD(feeder_init, feed_matrix_init), 408 KOBJMETHOD(feeder_free, feed_matrix_free), 409 KOBJMETHOD(feeder_feed, feed_matrix_feed), 410 KOBJMETHOD_END 411 }; 412 413 FEEDER_DECLARE(feeder_matrix, NULL); 414 415 /* External */ 416 int 417 feeder_matrix_setup(struct pcm_feeder *f, struct pcmchan_matrix *m_in, 418 struct pcmchan_matrix *m_out) 419 { 420 421 if (f == NULL || f->desc == NULL || f->desc->type != FEEDER_MATRIX || 422 f->data == NULL) 423 return (EINVAL); 424 425 return (feed_matrix_setup(f->data, m_in, m_out)); 426 } 427 428 /* 429 * feeder_matrix_default_id(): For a given number of channels, return 430 * default preferred id (example: both 5.1 and 431 * 6.0 are simply 6 channels, but 5.1 is more 432 * preferable). 433 */ 434 int 435 feeder_matrix_default_id(uint32_t ch) 436 { 437 438 if (ch < feeder_matrix_maps[SND_CHN_MATRIX_BEGIN].channels || 439 ch > feeder_matrix_maps[SND_CHN_MATRIX_END].channels) 440 return (SND_CHN_MATRIX_UNKNOWN); 441 442 return (feeder_matrix_maps[feeder_matrix_default_ids[ch]].id); 443 } 444 445 /* 446 * feeder_matrix_default_channel_map(): Ditto, but return matrix map 447 * instead. 448 */ 449 struct pcmchan_matrix * 450 feeder_matrix_default_channel_map(uint32_t ch) 451 { 452 453 if (ch < feeder_matrix_maps[SND_CHN_MATRIX_BEGIN].channels || 454 ch > feeder_matrix_maps[SND_CHN_MATRIX_END].channels) 455 return (NULL); 456 457 return (&feeder_matrix_maps[feeder_matrix_default_ids[ch]]); 458 } 459 460 /* 461 * feeder_matrix_default_format(): For a given audio format, return the 462 * proper audio format based on preferable 463 * matrix. 464 */ 465 uint32_t 466 feeder_matrix_default_format(uint32_t format) 467 { 468 struct pcmchan_matrix *m; 469 uint32_t i, ch, ext; 470 471 ch = AFMT_CHANNEL(format); 472 ext = AFMT_EXTCHANNEL(format); 473 474 if (ext != 0) { 475 for (i = SND_CHN_MATRIX_BEGIN; i <= SND_CHN_MATRIX_END; i++) { 476 if (feeder_matrix_maps[i].channels == ch && 477 feeder_matrix_maps[i].ext == ext) 478 return (SND_FORMAT(format, ch, ext)); 479 } 480 } 481 482 m = feeder_matrix_default_channel_map(ch); 483 if (m == NULL) 484 return (0x00000000); 485 486 return (SND_FORMAT(format, ch, m->ext)); 487 } 488 489 /* 490 * feeder_matrix_format_id(): For a given audio format, return its matrix 491 * id. 492 */ 493 int 494 feeder_matrix_format_id(uint32_t format) 495 { 496 uint32_t i, ch, ext; 497 498 ch = AFMT_CHANNEL(format); 499 ext = AFMT_EXTCHANNEL(format); 500 501 for (i = SND_CHN_MATRIX_BEGIN; i <= SND_CHN_MATRIX_END; i++) { 502 if (feeder_matrix_maps[i].channels == ch && 503 feeder_matrix_maps[i].ext == ext) 504 return (feeder_matrix_maps[i].id); 505 } 506 507 return (SND_CHN_MATRIX_UNKNOWN); 508 } 509 510 /* 511 * feeder_matrix_format_map(): For a given audio format, return its matrix 512 * map. 513 */ 514 struct pcmchan_matrix * 515 feeder_matrix_format_map(uint32_t format) 516 { 517 uint32_t i, ch, ext; 518 519 ch = AFMT_CHANNEL(format); 520 ext = AFMT_EXTCHANNEL(format); 521 522 for (i = SND_CHN_MATRIX_BEGIN; i <= SND_CHN_MATRIX_END; i++) { 523 if (feeder_matrix_maps[i].channels == ch && 524 feeder_matrix_maps[i].ext == ext) 525 return (&feeder_matrix_maps[i]); 526 } 527 528 return (NULL); 529 } 530 531 /* 532 * feeder_matrix_id_map(): For a given matrix id, return its matrix map. 533 */ 534 struct pcmchan_matrix * 535 feeder_matrix_id_map(int id) 536 { 537 538 if (id < SND_CHN_MATRIX_BEGIN || id > SND_CHN_MATRIX_END) 539 return (NULL); 540 541 return (&feeder_matrix_maps[id]); 542 } 543 544 /* 545 * feeder_matrix_compare(): Compare the simmilarities of matrices. 546 */ 547 int 548 feeder_matrix_compare(struct pcmchan_matrix *m_in, struct pcmchan_matrix *m_out) 549 { 550 uint32_t i; 551 552 if (m_in == m_out) 553 return (0); 554 555 if (m_in->channels != m_out->channels || m_in->ext != m_out->ext || 556 m_in->mask != m_out->mask) 557 return (1); 558 559 for (i = 0; i < nitems(m_in->map); i++) { 560 if (m_in->map[i].type != m_out->map[i].type) 561 return (1); 562 if (m_in->map[i].type == SND_CHN_T_MAX) 563 break; 564 if (m_in->map[i].members != m_out->map[i].members) 565 return (1); 566 if (i <= SND_CHN_T_END) { 567 if (m_in->offset[m_in->map[i].type] != 568 m_out->offset[m_out->map[i].type]) 569 return (1); 570 } 571 } 572 573 return (0); 574 } 575 576 /* 577 * XXX 4front interpretation of "surround" is ambigous and sort of 578 * conflicting with "rear"/"back". Map it to "side". Well.. 579 * who cares? 580 */ 581 static int snd_chn_to_oss[SND_CHN_T_MAX] = { 582 [SND_CHN_T_FL] = CHID_L, 583 [SND_CHN_T_FR] = CHID_R, 584 [SND_CHN_T_FC] = CHID_C, 585 [SND_CHN_T_LF] = CHID_LFE, 586 [SND_CHN_T_SL] = CHID_LS, 587 [SND_CHN_T_SR] = CHID_RS, 588 [SND_CHN_T_BL] = CHID_LR, 589 [SND_CHN_T_BR] = CHID_RR 590 }; 591 592 #define SND_CHN_OSS_VALIDMASK \ 593 (SND_CHN_T_MASK_FL | SND_CHN_T_MASK_FR | \ 594 SND_CHN_T_MASK_FC | SND_CHN_T_MASK_LF | \ 595 SND_CHN_T_MASK_SL | SND_CHN_T_MASK_SR | \ 596 SND_CHN_T_MASK_BL | SND_CHN_T_MASK_BR) 597 598 #define SND_CHN_OSS_MAX 8 599 #define SND_CHN_OSS_BEGIN CHID_L 600 #define SND_CHN_OSS_END CHID_RR 601 602 static int oss_to_snd_chn[SND_CHN_OSS_END + 1] = { 603 [CHID_L] = SND_CHN_T_FL, 604 [CHID_R] = SND_CHN_T_FR, 605 [CHID_C] = SND_CHN_T_FC, 606 [CHID_LFE] = SND_CHN_T_LF, 607 [CHID_LS] = SND_CHN_T_SL, 608 [CHID_RS] = SND_CHN_T_SR, 609 [CHID_LR] = SND_CHN_T_BL, 610 [CHID_RR] = SND_CHN_T_BR 611 }; 612 613 /* 614 * Used by SNDCTL_DSP_GET_CHNORDER. 615 */ 616 int 617 feeder_matrix_oss_get_channel_order(struct pcmchan_matrix *m, 618 unsigned long long *map) 619 { 620 unsigned long long tmpmap; 621 uint32_t i; 622 623 if (m == NULL || map == NULL || (m->mask & ~SND_CHN_OSS_VALIDMASK) || 624 m->channels > SND_CHN_OSS_MAX) 625 return (EINVAL); 626 627 tmpmap = 0x0000000000000000ULL; 628 629 for (i = 0; i < SND_CHN_OSS_MAX && m->map[i].type != SND_CHN_T_MAX; 630 i++) { 631 if ((1 << m->map[i].type) & ~SND_CHN_OSS_VALIDMASK) 632 return (EINVAL); 633 tmpmap |= 634 (unsigned long long)snd_chn_to_oss[m->map[i].type] << 635 (i * 4); 636 } 637 638 *map = tmpmap; 639 640 return (0); 641 } 642 643 /* 644 * Used by SNDCTL_DSP_SET_CHNORDER. 645 */ 646 int 647 feeder_matrix_oss_set_channel_order(struct pcmchan_matrix *m, 648 unsigned long long *map) 649 { 650 struct pcmchan_matrix tmp; 651 uint32_t chmask, i; 652 int ch, cheof; 653 654 if (m == NULL || map == NULL || (m->mask & ~SND_CHN_OSS_VALIDMASK) || 655 m->channels > SND_CHN_OSS_MAX || (*map & 0xffffffff00000000ULL)) 656 return (EINVAL); 657 658 tmp = *m; 659 tmp.channels = 0; 660 tmp.ext = 0; 661 tmp.mask = 0; 662 memset(tmp.offset, -1, sizeof(tmp.offset)); 663 cheof = 0; 664 665 for (i = 0; i < SND_CHN_OSS_MAX; i++) { 666 ch = (*map >> (i * 4)) & 0xf; 667 if (ch < SND_CHN_OSS_BEGIN) { 668 if (cheof == 0 && m->map[i].type != SND_CHN_T_MAX) 669 return (EINVAL); 670 cheof++; 671 tmp.map[i] = m->map[i]; 672 continue; 673 } else if (ch > SND_CHN_OSS_END) 674 return (EINVAL); 675 else if (cheof != 0) 676 return (EINVAL); 677 ch = oss_to_snd_chn[ch]; 678 chmask = 1 << ch; 679 /* channel not exist in matrix */ 680 if (!(chmask & m->mask)) 681 return (EINVAL); 682 /* duplicated channel */ 683 if (chmask & tmp.mask) 684 return (EINVAL); 685 tmp.map[i] = m->map[m->offset[ch]]; 686 if (tmp.map[i].type != ch) 687 return (EINVAL); 688 tmp.offset[ch] = i; 689 tmp.mask |= chmask; 690 tmp.channels++; 691 if (chmask & SND_CHN_T_MASK_LF) 692 tmp.ext++; 693 } 694 695 if (tmp.channels != m->channels || tmp.ext != m->ext || 696 tmp.mask != m->mask || 697 tmp.map[m->channels].type != SND_CHN_T_MAX) 698 return (EINVAL); 699 700 *m = tmp; 701 702 return (0); 703 } 704