1558a398bSSimon Schubert /*-
22a1ad637SFrançois Tigeot * Copyright (c) 2005-2009 Ariff Abdullah <ariff@FreeBSD.org>
3558a398bSSimon Schubert * All rights reserved.
4558a398bSSimon Schubert *
5558a398bSSimon Schubert * Redistribution and use in source and binary forms, with or without
6558a398bSSimon Schubert * modification, are permitted provided that the following conditions
7558a398bSSimon Schubert * are met:
8558a398bSSimon Schubert * 1. Redistributions of source code must retain the above copyright
9558a398bSSimon Schubert * notice, this list of conditions and the following disclaimer.
10558a398bSSimon Schubert * 2. Redistributions in binary form must reproduce the above copyright
11558a398bSSimon Schubert * notice, this list of conditions and the following disclaimer in the
12558a398bSSimon Schubert * documentation and/or other materials provided with the distribution.
13558a398bSSimon Schubert *
14558a398bSSimon Schubert * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15558a398bSSimon Schubert * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16558a398bSSimon Schubert * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17558a398bSSimon Schubert * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18558a398bSSimon Schubert * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19558a398bSSimon Schubert * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20558a398bSSimon Schubert * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21558a398bSSimon Schubert * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22558a398bSSimon Schubert * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23558a398bSSimon Schubert * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24558a398bSSimon Schubert * SUCH DAMAGE.
25558a398bSSimon Schubert */
26558a398bSSimon Schubert
272a1ad637SFrançois Tigeot /* feeder_volume, a long 'Lost Technology' rather than a new feature. */
282a1ad637SFrançois Tigeot
292a1ad637SFrançois Tigeot #ifdef _KERNEL
302a1ad637SFrançois Tigeot #ifdef HAVE_KERNEL_OPTION_HEADERS
312a1ad637SFrançois Tigeot #include "opt_snd.h"
322a1ad637SFrançois Tigeot #endif
33558a398bSSimon Schubert #include <dev/sound/pcm/sound.h>
342a1ad637SFrançois Tigeot #include <dev/sound/pcm/pcm.h>
35558a398bSSimon Schubert #include "feeder_if.h"
36558a398bSSimon Schubert
372a1ad637SFrançois Tigeot #define SND_USE_FXDIV
382a1ad637SFrançois Tigeot #include "snd_fxdiv_gen.h"
392a1ad637SFrançois Tigeot
402a1ad637SFrançois Tigeot SND_DECLARE_FILE("$FreeBSD: head/sys/dev/sound/pcm/feeder_volume.c 193640 2009-06-07 19:12:08Z ariff $");
412a1ad637SFrançois Tigeot #endif
422a1ad637SFrançois Tigeot
432a1ad637SFrançois Tigeot typedef void (*feed_volume_t)(int *, int *, uint32_t, uint8_t *, uint32_t);
442a1ad637SFrançois Tigeot
452a1ad637SFrançois Tigeot #define FEEDVOLUME_CALC8(s, v) (SND_VOL_CALC_SAMPLE((intpcm_t) \
462a1ad637SFrançois Tigeot (s) << 8, v) >> 8)
472a1ad637SFrançois Tigeot #define FEEDVOLUME_CALC16(s, v) SND_VOL_CALC_SAMPLE((intpcm_t)(s), v)
482a1ad637SFrançois Tigeot #define FEEDVOLUME_CALC24(s, v) SND_VOL_CALC_SAMPLE((intpcm64_t)(s), v)
492a1ad637SFrançois Tigeot #define FEEDVOLUME_CALC32(s, v) SND_VOL_CALC_SAMPLE((intpcm64_t)(s), v)
502a1ad637SFrançois Tigeot
512a1ad637SFrançois Tigeot #define FEEDVOLUME_DECLARE(SIGN, BIT, ENDIAN) \
522a1ad637SFrançois Tigeot static void \
532a1ad637SFrançois Tigeot feed_volume_##SIGN##BIT##ENDIAN(int *vol, int *matrix, \
542a1ad637SFrançois Tigeot uint32_t channels, uint8_t *dst, uint32_t count) \
552a1ad637SFrançois Tigeot { \
562a1ad637SFrançois Tigeot intpcm##BIT##_t v; \
572a1ad637SFrançois Tigeot intpcm_t x; \
582a1ad637SFrançois Tigeot uint32_t i; \
592a1ad637SFrançois Tigeot \
602a1ad637SFrançois Tigeot dst += count * PCM_##BIT##_BPS * channels; \
612a1ad637SFrançois Tigeot do { \
622a1ad637SFrançois Tigeot i = channels; \
632a1ad637SFrançois Tigeot do { \
642a1ad637SFrançois Tigeot dst -= PCM_##BIT##_BPS; \
652a1ad637SFrançois Tigeot i--; \
662a1ad637SFrançois Tigeot x = PCM_READ_##SIGN##BIT##_##ENDIAN(dst); \
672a1ad637SFrançois Tigeot v = FEEDVOLUME_CALC##BIT(x, vol[matrix[i]]); \
682a1ad637SFrançois Tigeot x = PCM_CLAMP_##SIGN##BIT(v); \
692a1ad637SFrançois Tigeot _PCM_WRITE_##SIGN##BIT##_##ENDIAN(dst, x); \
702a1ad637SFrançois Tigeot } while (i != 0); \
712a1ad637SFrançois Tigeot } while (--count != 0); \
722a1ad637SFrançois Tigeot }
732a1ad637SFrançois Tigeot
742a1ad637SFrançois Tigeot #if BYTE_ORDER == LITTLE_ENDIAN || defined(SND_FEEDER_MULTIFORMAT)
752a1ad637SFrançois Tigeot FEEDVOLUME_DECLARE(S, 16, LE)
762a1ad637SFrançois Tigeot FEEDVOLUME_DECLARE(S, 32, LE)
772a1ad637SFrançois Tigeot #endif
782a1ad637SFrançois Tigeot #if BYTE_ORDER == BIG_ENDIAN || defined(SND_FEEDER_MULTIFORMAT)
792a1ad637SFrançois Tigeot FEEDVOLUME_DECLARE(S, 16, BE)
802a1ad637SFrançois Tigeot FEEDVOLUME_DECLARE(S, 32, BE)
812a1ad637SFrançois Tigeot #endif
822a1ad637SFrançois Tigeot #ifdef SND_FEEDER_MULTIFORMAT
832a1ad637SFrançois Tigeot FEEDVOLUME_DECLARE(S, 8, NE)
842a1ad637SFrançois Tigeot FEEDVOLUME_DECLARE(S, 24, LE)
852a1ad637SFrançois Tigeot FEEDVOLUME_DECLARE(S, 24, BE)
862a1ad637SFrançois Tigeot FEEDVOLUME_DECLARE(U, 8, NE)
872a1ad637SFrançois Tigeot FEEDVOLUME_DECLARE(U, 16, LE)
882a1ad637SFrançois Tigeot FEEDVOLUME_DECLARE(U, 24, LE)
892a1ad637SFrançois Tigeot FEEDVOLUME_DECLARE(U, 32, LE)
902a1ad637SFrançois Tigeot FEEDVOLUME_DECLARE(U, 16, BE)
912a1ad637SFrançois Tigeot FEEDVOLUME_DECLARE(U, 24, BE)
922a1ad637SFrançois Tigeot FEEDVOLUME_DECLARE(U, 32, BE)
932a1ad637SFrançois Tigeot #endif
942a1ad637SFrançois Tigeot
952a1ad637SFrançois Tigeot struct feed_volume_info {
962a1ad637SFrançois Tigeot uint32_t bps, channels;
972a1ad637SFrançois Tigeot feed_volume_t apply;
982a1ad637SFrançois Tigeot int volume_class;
992a1ad637SFrançois Tigeot int state;
1002a1ad637SFrançois Tigeot int matrix[SND_CHN_MAX];
1012a1ad637SFrançois Tigeot };
1022a1ad637SFrançois Tigeot
1032a1ad637SFrançois Tigeot #define FEEDVOLUME_ENTRY(SIGN, BIT, ENDIAN) \
1042a1ad637SFrançois Tigeot { \
1052a1ad637SFrançois Tigeot AFMT_##SIGN##BIT##_##ENDIAN, \
1062a1ad637SFrançois Tigeot feed_volume_##SIGN##BIT##ENDIAN \
1072a1ad637SFrançois Tigeot }
1082a1ad637SFrançois Tigeot
1092a1ad637SFrançois Tigeot static const struct {
1102a1ad637SFrançois Tigeot uint32_t format;
1112a1ad637SFrançois Tigeot feed_volume_t apply;
1122a1ad637SFrançois Tigeot } feed_volume_info_tab[] = {
1132a1ad637SFrançois Tigeot #if BYTE_ORDER == LITTLE_ENDIAN || defined(SND_FEEDER_MULTIFORMAT)
1142a1ad637SFrançois Tigeot FEEDVOLUME_ENTRY(S, 16, LE),
1152a1ad637SFrançois Tigeot FEEDVOLUME_ENTRY(S, 32, LE),
1162a1ad637SFrançois Tigeot #endif
1172a1ad637SFrançois Tigeot #if BYTE_ORDER == BIG_ENDIAN || defined(SND_FEEDER_MULTIFORMAT)
1182a1ad637SFrançois Tigeot FEEDVOLUME_ENTRY(S, 16, BE),
1192a1ad637SFrançois Tigeot FEEDVOLUME_ENTRY(S, 32, BE),
1202a1ad637SFrançois Tigeot #endif
1212a1ad637SFrançois Tigeot #ifdef SND_FEEDER_MULTIFORMAT
1222a1ad637SFrançois Tigeot FEEDVOLUME_ENTRY(S, 8, NE),
1232a1ad637SFrançois Tigeot FEEDVOLUME_ENTRY(S, 24, LE),
1242a1ad637SFrançois Tigeot FEEDVOLUME_ENTRY(S, 24, BE),
1252a1ad637SFrançois Tigeot FEEDVOLUME_ENTRY(U, 8, NE),
1262a1ad637SFrançois Tigeot FEEDVOLUME_ENTRY(U, 16, LE),
1272a1ad637SFrançois Tigeot FEEDVOLUME_ENTRY(U, 24, LE),
1282a1ad637SFrançois Tigeot FEEDVOLUME_ENTRY(U, 32, LE),
1292a1ad637SFrançois Tigeot FEEDVOLUME_ENTRY(U, 16, BE),
1302a1ad637SFrançois Tigeot FEEDVOLUME_ENTRY(U, 24, BE),
1312a1ad637SFrançois Tigeot FEEDVOLUME_ENTRY(U, 32, BE)
1322a1ad637SFrançois Tigeot #endif
1332a1ad637SFrançois Tigeot };
1342a1ad637SFrançois Tigeot
1352a1ad637SFrançois Tigeot #define FEEDVOLUME_TAB_SIZE ((int32_t) \
1362a1ad637SFrançois Tigeot (sizeof(feed_volume_info_tab) / \
1372a1ad637SFrançois Tigeot sizeof(feed_volume_info_tab[0])))
138558a398bSSimon Schubert
139558a398bSSimon Schubert static int
feed_volume_init(struct pcm_feeder * f)1402a1ad637SFrançois Tigeot feed_volume_init(struct pcm_feeder *f)
1412a1ad637SFrançois Tigeot {
1422a1ad637SFrançois Tigeot struct feed_volume_info *info;
1432a1ad637SFrançois Tigeot struct pcmchan_matrix *m;
1442a1ad637SFrançois Tigeot uint32_t i;
1452a1ad637SFrançois Tigeot int ret;
1462a1ad637SFrançois Tigeot
1472a1ad637SFrançois Tigeot if (f->desc->in != f->desc->out ||
1482a1ad637SFrançois Tigeot AFMT_CHANNEL(f->desc->in) > SND_CHN_MAX)
1492a1ad637SFrançois Tigeot return (EINVAL);
1502a1ad637SFrançois Tigeot
1512a1ad637SFrançois Tigeot for (i = 0; i < FEEDVOLUME_TAB_SIZE; i++) {
1522a1ad637SFrançois Tigeot if (AFMT_ENCODING(f->desc->in) ==
1532a1ad637SFrançois Tigeot feed_volume_info_tab[i].format) {
15467931cc4SFrançois Tigeot info = kmalloc(sizeof(*info), M_DEVBUF,
155*4e8e900cSMatthew Dillon M_WAITOK | M_ZERO);
1562a1ad637SFrançois Tigeot if (info == NULL)
1572a1ad637SFrançois Tigeot return (ENOMEM);
1582a1ad637SFrançois Tigeot
1592a1ad637SFrançois Tigeot info->bps = AFMT_BPS(f->desc->in);
1602a1ad637SFrançois Tigeot info->channels = AFMT_CHANNEL(f->desc->in);
1612a1ad637SFrançois Tigeot info->apply = feed_volume_info_tab[i].apply;
1622a1ad637SFrançois Tigeot info->volume_class = SND_VOL_C_PCM;
1632a1ad637SFrançois Tigeot info->state = FEEDVOLUME_ENABLE;
1642a1ad637SFrançois Tigeot
1652a1ad637SFrançois Tigeot f->data = info;
1662a1ad637SFrançois Tigeot m = feeder_matrix_default_channel_map(info->channels);
1672a1ad637SFrançois Tigeot if (m == NULL) {
16867931cc4SFrançois Tigeot kfree(info, M_DEVBUF);
1692a1ad637SFrançois Tigeot return (EINVAL);
1702a1ad637SFrançois Tigeot }
1712a1ad637SFrançois Tigeot
1722a1ad637SFrançois Tigeot ret = feeder_volume_apply_matrix(f, m);
1732a1ad637SFrançois Tigeot if (ret != 0)
17467931cc4SFrançois Tigeot kfree(info, M_DEVBUF);
1752a1ad637SFrançois Tigeot
1762a1ad637SFrançois Tigeot return (ret);
1772a1ad637SFrançois Tigeot }
1782a1ad637SFrançois Tigeot }
1792a1ad637SFrançois Tigeot
1802a1ad637SFrançois Tigeot return (EINVAL);
1812a1ad637SFrançois Tigeot }
1822a1ad637SFrançois Tigeot
1832a1ad637SFrançois Tigeot static int
feed_volume_free(struct pcm_feeder * f)1842a1ad637SFrançois Tigeot feed_volume_free(struct pcm_feeder *f)
1852a1ad637SFrançois Tigeot {
1862a1ad637SFrançois Tigeot struct feed_volume_info *info;
1872a1ad637SFrançois Tigeot
1882a1ad637SFrançois Tigeot info = f->data;
1892a1ad637SFrançois Tigeot if (info != NULL)
19067931cc4SFrançois Tigeot kfree(info, M_DEVBUF);
1912a1ad637SFrançois Tigeot
1922a1ad637SFrançois Tigeot f->data = NULL;
1932a1ad637SFrançois Tigeot
1942a1ad637SFrançois Tigeot return (0);
1952a1ad637SFrançois Tigeot }
1962a1ad637SFrançois Tigeot
1972a1ad637SFrançois Tigeot static int
feed_volume_set(struct pcm_feeder * f,int what,int value)1982a1ad637SFrançois Tigeot feed_volume_set(struct pcm_feeder *f, int what, int value)
1992a1ad637SFrançois Tigeot {
2002a1ad637SFrançois Tigeot struct feed_volume_info *info;
2012a1ad637SFrançois Tigeot struct pcmchan_matrix *m;
2022a1ad637SFrançois Tigeot int ret;
2032a1ad637SFrançois Tigeot
2042a1ad637SFrançois Tigeot info = f->data;
2052a1ad637SFrançois Tigeot ret = 0;
2062a1ad637SFrançois Tigeot
2072a1ad637SFrançois Tigeot switch (what) {
2082a1ad637SFrançois Tigeot case FEEDVOLUME_CLASS:
2092a1ad637SFrançois Tigeot if (value < SND_VOL_C_BEGIN || value > SND_VOL_C_END)
2102a1ad637SFrançois Tigeot return (EINVAL);
2112a1ad637SFrançois Tigeot info->volume_class = value;
2122a1ad637SFrançois Tigeot break;
2132a1ad637SFrançois Tigeot case FEEDVOLUME_CHANNELS:
2142a1ad637SFrançois Tigeot if (value < SND_CHN_MIN || value > SND_CHN_MAX)
2152a1ad637SFrançois Tigeot return (EINVAL);
2162a1ad637SFrançois Tigeot m = feeder_matrix_default_channel_map(value);
2172a1ad637SFrançois Tigeot if (m == NULL)
2182a1ad637SFrançois Tigeot return (EINVAL);
2192a1ad637SFrançois Tigeot ret = feeder_volume_apply_matrix(f, m);
2202a1ad637SFrançois Tigeot break;
2212a1ad637SFrançois Tigeot case FEEDVOLUME_STATE:
2222a1ad637SFrançois Tigeot if (!(value == FEEDVOLUME_ENABLE || value == FEEDVOLUME_BYPASS))
2232a1ad637SFrançois Tigeot return (EINVAL);
2242a1ad637SFrançois Tigeot info->state = value;
2252a1ad637SFrançois Tigeot break;
2262a1ad637SFrançois Tigeot default:
2272a1ad637SFrançois Tigeot return (EINVAL);
2282a1ad637SFrançois Tigeot break;
2292a1ad637SFrançois Tigeot }
2302a1ad637SFrançois Tigeot
2312a1ad637SFrançois Tigeot return (ret);
2322a1ad637SFrançois Tigeot }
2332a1ad637SFrançois Tigeot
2342a1ad637SFrançois Tigeot static int
feed_volume_feed(struct pcm_feeder * f,struct pcm_channel * c,uint8_t * b,uint32_t count,void * source)2352a1ad637SFrançois Tigeot feed_volume_feed(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b,
236558a398bSSimon Schubert uint32_t count, void *source)
237558a398bSSimon Schubert {
2382a1ad637SFrançois Tigeot struct feed_volume_info *info;
2392a1ad637SFrançois Tigeot uint32_t j, align;
2402a1ad637SFrançois Tigeot int i, *vol, *matrix;
2412a1ad637SFrançois Tigeot uint8_t *dst;
242558a398bSSimon Schubert
2432a1ad637SFrançois Tigeot /*
2442a1ad637SFrançois Tigeot * Fetch filter data operation.
2452a1ad637SFrançois Tigeot */
2462a1ad637SFrançois Tigeot info = f->data;
2472a1ad637SFrançois Tigeot
2482a1ad637SFrançois Tigeot if (info->state == FEEDVOLUME_BYPASS)
2492a1ad637SFrançois Tigeot return (FEEDER_FEED(f->source, c, b, count, source));
2502a1ad637SFrançois Tigeot
2512a1ad637SFrançois Tigeot vol = c->volume[SND_VOL_C_VAL(info->volume_class)];
2522a1ad637SFrançois Tigeot matrix = info->matrix;
2532a1ad637SFrançois Tigeot
2542a1ad637SFrançois Tigeot /*
2552a1ad637SFrançois Tigeot * First, let see if we really need to apply gain at all.
2562a1ad637SFrançois Tigeot */
2572a1ad637SFrançois Tigeot j = 0;
2582a1ad637SFrançois Tigeot i = info->channels;
2592a1ad637SFrançois Tigeot do {
2602a1ad637SFrançois Tigeot if (vol[matrix[--i]] != SND_VOL_FLAT) {
2612a1ad637SFrançois Tigeot j = 1;
2622a1ad637SFrançois Tigeot break;
263558a398bSSimon Schubert }
2642a1ad637SFrançois Tigeot } while (i != 0);
2652a1ad637SFrançois Tigeot
2662a1ad637SFrançois Tigeot /* Nope, just bypass entirely. */
2672a1ad637SFrançois Tigeot if (j == 0)
2682a1ad637SFrançois Tigeot return (FEEDER_FEED(f->source, c, b, count, source));
2692a1ad637SFrançois Tigeot
2702a1ad637SFrançois Tigeot dst = b;
2712a1ad637SFrançois Tigeot align = info->bps * info->channels;
2722a1ad637SFrançois Tigeot
2732a1ad637SFrançois Tigeot do {
2742a1ad637SFrançois Tigeot if (count < align)
2752a1ad637SFrançois Tigeot break;
2762a1ad637SFrançois Tigeot
2772a1ad637SFrançois Tigeot j = SND_FXDIV(FEEDER_FEED(f->source, c, dst, count, source),
2782a1ad637SFrançois Tigeot align);
2792a1ad637SFrançois Tigeot if (j == 0)
2802a1ad637SFrançois Tigeot break;
2812a1ad637SFrançois Tigeot
2822a1ad637SFrançois Tigeot info->apply(vol, matrix, info->channels, dst, j);
2832a1ad637SFrançois Tigeot
2842a1ad637SFrançois Tigeot j *= align;
2852a1ad637SFrançois Tigeot dst += j;
2862a1ad637SFrançois Tigeot count -= j;
2872a1ad637SFrançois Tigeot
2882a1ad637SFrançois Tigeot } while (count != 0);
2892a1ad637SFrançois Tigeot
2902a1ad637SFrançois Tigeot return (dst - b);
291558a398bSSimon Schubert }
292558a398bSSimon Schubert
2932a1ad637SFrançois Tigeot static struct pcm_feederdesc feeder_volume_desc[] = {
2942a1ad637SFrançois Tigeot { FEEDER_VOLUME, 0, 0, 0, 0 },
2952a1ad637SFrançois Tigeot { 0, 0, 0, 0, 0 }
296558a398bSSimon Schubert };
2972a1ad637SFrançois Tigeot
2982a1ad637SFrançois Tigeot static kobj_method_t feeder_volume_methods[] = {
2992a1ad637SFrançois Tigeot KOBJMETHOD(feeder_init, feed_volume_init),
3002a1ad637SFrançois Tigeot KOBJMETHOD(feeder_free, feed_volume_free),
3012a1ad637SFrançois Tigeot KOBJMETHOD(feeder_set, feed_volume_set),
3022a1ad637SFrançois Tigeot KOBJMETHOD(feeder_feed, feed_volume_feed),
3037774cda2SSascha Wildner KOBJMETHOD_END
304558a398bSSimon Schubert };
3052a1ad637SFrançois Tigeot
3062a1ad637SFrançois Tigeot FEEDER_DECLARE(feeder_volume, NULL);
3072a1ad637SFrançois Tigeot
3082a1ad637SFrançois Tigeot /* Extern */
3092a1ad637SFrançois Tigeot
3102a1ad637SFrançois Tigeot /*
3112a1ad637SFrançois Tigeot * feeder_volume_apply_matrix(): For given matrix map, apply its configuration
3122a1ad637SFrançois Tigeot * to feeder_volume matrix structure. There are
3132a1ad637SFrançois Tigeot * possibilites that feeder_volume be inserted
3142a1ad637SFrançois Tigeot * before or after feeder_matrix, which in this
3152a1ad637SFrançois Tigeot * case feeder_volume must be in a good terms
3162a1ad637SFrançois Tigeot * with _current_ matrix.
3172a1ad637SFrançois Tigeot */
3182a1ad637SFrançois Tigeot int
feeder_volume_apply_matrix(struct pcm_feeder * f,struct pcmchan_matrix * m)3192a1ad637SFrançois Tigeot feeder_volume_apply_matrix(struct pcm_feeder *f, struct pcmchan_matrix *m)
3202a1ad637SFrançois Tigeot {
3212a1ad637SFrançois Tigeot struct feed_volume_info *info;
3222a1ad637SFrançois Tigeot uint32_t i;
3232a1ad637SFrançois Tigeot
3242a1ad637SFrançois Tigeot if (f == NULL || f->desc == NULL || f->desc->type != FEEDER_VOLUME ||
3252a1ad637SFrançois Tigeot f->data == NULL || m == NULL || m->channels < SND_CHN_MIN ||
3262a1ad637SFrançois Tigeot m->channels > SND_CHN_MAX)
3272a1ad637SFrançois Tigeot return (EINVAL);
3282a1ad637SFrançois Tigeot
3292a1ad637SFrançois Tigeot info = f->data;
3302a1ad637SFrançois Tigeot
3312a1ad637SFrançois Tigeot for (i = 0; i < (sizeof(info->matrix) / sizeof(info->matrix[0])); i++) {
3322a1ad637SFrançois Tigeot if (i < m->channels)
3332a1ad637SFrançois Tigeot info->matrix[i] = m->map[i].type;
3342a1ad637SFrançois Tigeot else
3352a1ad637SFrançois Tigeot info->matrix[i] = SND_CHN_T_FL;
3362a1ad637SFrançois Tigeot }
3372a1ad637SFrançois Tigeot
3382a1ad637SFrançois Tigeot info->channels = m->channels;
3392a1ad637SFrançois Tigeot
3402a1ad637SFrançois Tigeot return (0);
3412a1ad637SFrançois Tigeot }
342