--- sys/conf/files.orig 2008-03-20 13:51:15.000000000 +0800 +++ sys/conf/files 2008-04-07 21:28:27.000000000 +0800 @@ -65,6 +65,21 @@ compile-with "CC='${CC}' AWK=${AWK} sh $S/tools/emu10k1-mkalsa.sh $S/gnu/dev/sound/pci/p17v-alsa.h p17v-alsa%diked.h" \ no-obj no-implicit-rule before-depend \ clean "p17v-alsa%diked.h" +feeder_eq_gen.h optional sound \ + dependency "$S/tools/feeder_eq_mkfilter.awk" \ + compile-with "${AWK} -f $S/tools/feeder_eq_mkfilter.awk -- ${FEEDER_EQ_PRESETS} > feeder_eq_gen.h" \ + no-obj no-implicit-rule before-depend \ + clean "feeder_eq_gen.h" +feeder_rate_gen.h optional sound \ + dependency "$S/tools/feeder_rate_mkfilter.awk" \ + compile-with "${AWK} -f $S/tools/feeder_rate_mkfilter.awk -- ${FEEDER_RATE_PRESETS} > feeder_rate_gen.h" \ + no-obj no-implicit-rule before-depend \ + clean "feeder_rate_gen.h" +snd_fxdiv_gen.h optional sound \ + dependency "$S/tools/snd_fxdiv_gen.awk" \ + compile-with "${AWK} -f $S/tools/snd_fxdiv_gen.awk -- > snd_fxdiv_gen.h" \ + no-obj no-implicit-rule before-depend \ + clean "snd_fxdiv_gen.h" miidevs.h optional miibus | mii \ dependency "$S/tools/miidevs2h.awk $S/dev/mii/miidevs" \ compile-with "${AWK} -f $S/tools/miidevs2h.awk $S/dev/mii/miidevs" \ @@ -1172,10 +1187,22 @@ dev/sound/pcm/dsp.c optional sound dev/sound/pcm/fake.c optional sound dev/sound/pcm/feeder.c optional sound -dev/sound/pcm/feeder_fmt.c optional sound +dev/sound/pcm/feeder_chain.c optional sound +dev/sound/pcm/feeder_eq.c optional sound \ + dependency "feeder_eq_gen.h" \ + dependency "snd_fxdiv_gen.h" dev/sound/pcm/feeder_if.m optional sound -dev/sound/pcm/feeder_rate.c optional sound -dev/sound/pcm/feeder_volume.c optional sound +dev/sound/pcm/feeder_format.c optional sound \ + dependency "snd_fxdiv_gen.h" +dev/sound/pcm/feeder_matrix.c optional sound \ + dependency "snd_fxdiv_gen.h" +dev/sound/pcm/feeder_mixer.c optional sound \ + dependency "snd_fxdiv_gen.h" +dev/sound/pcm/feeder_rate.c optional sound \ + dependency "feeder_rate_gen.h" \ + dependency "snd_fxdiv_gen.h" +dev/sound/pcm/feeder_volume.c optional sound \ + dependency "snd_fxdiv_gen.h" dev/sound/pcm/mixer.c optional sound dev/sound/pcm/mixer_if.m optional sound dev/sound/pcm/sndstat.c optional sound --- sys/tools/feeder_eq_mkfilter.awk.orig 1970-01-01 07:30:00.000000000 +07302008-07-03 10:57:51.000000000 +0800 +++ sys/tools/feeder_eq_mkfilter.awk 2008-06-30 10:14:34.000000000 +0800 @@ -0,0 +1,463 @@ +#!/usr/bin/awk -f +# +# Copyright (c) 2008 Ariff Abdullah +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# $FreeBSD$ +# + +# +# Based on +# +# "Cookbook formulae for audio EQ biquad filter coefficients" +# by Robert Bristow-Johnson +# +# - http://www.musicdsp.org/files/Audio-EQ-Cookbook.txt +# + + + +# +# Some basic Math functions. +# +function abs(x) +{ + return (((x < 0) ? -x : x) + 0); +} + +function fabs(x) +{ + return (((x < 0.0) ? -x : x) + 0.0); +} + +function floor(x, r) +{ + r = int(x); + if (r > x) + r--; + return (r + 0); +} + +function pow(x, y) +{ + return (exp(1.0 * y * log(1.0 * x))); +} + +# +# What the hell... +# +function shl(x, y) +{ + while (y > 0) { + x *= 2; + y--; + } + return (x); +} + +function feedeq_w0(fc, rate) +{ + return ((2.0 * M_PI * fc) / (1.0 * rate)); +} + +function feedeq_A(gain, A) +{ + if (FEEDEQ_TYPE == FEEDEQ_TYPE_PEQ || FEEDEQ_TYPE == FEEDEQ_TYPE_SHELF) + A = pow(10, gain / 40.0); + else + A = sqrt(pow(10, gain / 20.0)); + + return (A); +} + +function feedeq_alpha(w0, A, QS) +{ + if (FEEDEQ_TYPE == FEEDEQ_TYPE_PEQ) + alpha = sin(w0) / (2.0 * QS); + else if (FEEDEQ_TYPE == FEEDEQ_TYPE_SHELF) + alpha = sin(w0) * 0.5 * sqrt(A + ((1.0 / A) * \ + ((1.0 / QS) - 1.0)) + 2.0); + else + alpha = 0.0; + + return (alpha); +} + +function feedeq_fx_floor(v, r) +{ + if (fabs(v) < fabs(smallest)) + smallest = v; + if (fabs(v) > fabs(largest)) + largest = v; + + r = floor((v * FEEDEQ_COEFF_ONE) + 0.5); + + if (r < INT32_MIN || r > INT32_MAX) + printf("\n#error overflow v=%f, " \ + "please reduce FEEDEQ_COEFF_SHIFT\n", v); + + return (r); +} + +function feedeq_gen_iir_coeffs(coeffs, rate, gain, \ + w0, A, alpha, a0, a1, a2, b0, b1, b2) +{ + w0 = feedeq_w0(FEEDEQ_TREBLE_SFREQ, 1.0 * rate); + A = feedeq_A(1.0 * gain); + alpha = feedeq_alpha(w0, A, FEEDEQ_TREBLE_SLOPE); + + if (FEEDEQ_TYPE == FEEDEQ_TYPE_PEQ) { + b0 = 1.0 + (alpha * A); + b1 = -2.0 * cos(w0); + b2 = 1.0 - (alpha * A); + a0 = 1.0 + (alpha / A); + a1 = -2.0 * cos(w0); + a2 = 1.0 - (alpha / A); + } else if (FEEDEQ_TYPE == FEEDEQ_TYPE_SHELF) { + b0 = A*((A+1.0)+((A-1.0)*cos(w0))+(2.0*sqrt(A)*alpha)); + b1 = -2.0*A*((A-1.0)+((A+1.0)*cos(w0)) ); + b2 = A*((A+1.0)+((A-1.0)*cos(w0))-(2.0*sqrt(A)*alpha)); + a0 = (A+1.0)-((A-1.0)*cos(w0))+(2.0*sqrt(A)*alpha ); + a1 = 2.0 * ((A-1.0)-((A+1.0)*cos(w0)) ); + a2 = (A+1.0)-((A-1.0)*cos(w0))-(2.0*sqrt(A)*alpha ); + } else + b0 = b1 = b2 = a0 = a1 = a2 = 0.0; + + b0 /= a0; + b1 /= a0; + b2 /= a0; + a1 /= a0; + a2 /= a0; + + coeffs["treble", gain, 0] = feedeq_fx_floor(a0); + coeffs["treble", gain, 1] = feedeq_fx_floor(a1); + coeffs["treble", gain, 2] = feedeq_fx_floor(a2); + coeffs["treble", gain, 3] = feedeq_fx_floor(b0); + coeffs["treble", gain, 4] = feedeq_fx_floor(b1); + coeffs["treble", gain, 5] = feedeq_fx_floor(b2); + + w0 = feedeq_w0(FEEDEQ_BASS_SFREQ, 1.0 * rate); + A = feedeq_A(1.0 * gain); + alpha = feedeq_alpha(w0, A, FEEDEQ_BASS_SLOPE); + + if (FEEDEQ_TYPE == FEEDEQ_TYPE_PEQ) { + b0 = 1.0 + (alpha * A); + b1 = -2.0 * cos(w0); + b2 = 1.0 - (alpha * A); + a0 = 1.0 + (alpha / A); + a1 = -2.0 * cos(w0); + a2 = 1.0 - (alpha / A); + } else if (FEEDEQ_TYPE == FEEDEQ_TYPE_SHELF) { + b0 = A*((A+1.0)-((A-1.0)*cos(w0))+(2.0*sqrt(A)*alpha)); + b1 = 2.0*A*((A-1.0)-((A+1.0)*cos(w0)) ); + b2 = A*((A+1.0)-((A-1.0)*cos(w0))-(2.0*sqrt(A)*alpha)); + a0 = (A+1.0)+((A-1.0)*cos(w0))+(2.0*sqrt(A)*alpha ); + a1 = -2.0 * ((A-1.0)+((A+1.0)*cos(w0)) ); + a2 = (A+1.0)+((A-1.0)*cos(w0))-(2.0*sqrt(A)*alpha ); + } else + b0 = b1 = b2 = a0 = a1 = a2 = 0.0; + + b0 /= a0; + b1 /= a0; + b2 /= a0; + a1 /= a0; + a2 /= a0; + + coeffs["bass", gain, 0] = feedeq_fx_floor(a0); + coeffs["bass", gain, 1] = feedeq_fx_floor(a1); + coeffs["bass", gain, 2] = feedeq_fx_floor(a2); + coeffs["bass", gain, 3] = feedeq_fx_floor(b0); + coeffs["bass", gain, 4] = feedeq_fx_floor(b1); + coeffs["bass", gain, 5] = feedeq_fx_floor(b2); +} + +function feedeq_gen_freq_coeffs(frq, g, i, v) +{ + coeffs[0] = 0; + + for (g = (FEEDEQ_GAIN_MIN * FEEDEQ_GAIN_DIV); \ + g <= (FEEDEQ_GAIN_MAX * FEEDEQ_GAIN_DIV); \ + g += FEEDEQ_GAIN_STEP) { + feedeq_gen_iir_coeffs(coeffs, frq, \ + g * FEEDEQ_GAIN_RECIPROCAL); + } + + printf("\nstatic struct feed_eq_coeff eq_%d[%d] " \ + "= {\n", frq, FEEDEQ_LEVELS); + for (g = (FEEDEQ_GAIN_MIN * FEEDEQ_GAIN_DIV); \ + g <= (FEEDEQ_GAIN_MAX * FEEDEQ_GAIN_DIV); \ + g += FEEDEQ_GAIN_STEP) { + printf(" {{ "); + for (i = 1; i < 6; i++) { + v = coeffs["treble", g * FEEDEQ_GAIN_RECIPROCAL, i]; + printf("%s0x%08x%s", \ + (v < 0) ? "-" : " ", abs(v), \ + (i == 5) ? " " : ", "); + } + printf("},\n { "); + for (i = 1; i < 6; i++) { + v = coeffs["bass", g * FEEDEQ_GAIN_RECIPROCAL, i]; + printf("%s0x%08x%s", \ + (v < 0) ? "-" : " ", abs(v), \ + (i == 5) ? " " : ", "); + } + printf("}}%s\n", \ + (g < (FEEDEQ_GAIN_MAX * FEEDEQ_GAIN_DIV)) ? "," : ""); + } + printf("};\n"); +} + +function feedeq_calc_preamp(norm, gain, shift, mul, bit, attn) +{ + shift = FEEDEQ_PREAMP_SHIFT; + + if (floor(FEEDEQ_PREAMP_BITDB) == 6 && \ + (1.0 * floor(gain)) == gain && (floor(gain) % 6) == 0) { + mul = 1; + shift = floor(floor(gain) / 6); + } else { + bit = 32.0 - ((1.0 * gain) / (1.0 * FEEDEQ_PREAMP_BITDB)); + attn = pow(2.0, bit) / pow(2.0, 32.0); + mul = floor((attn * FEEDEQ_PREAMP_ONE) + 0.5); + } + + while ((mul % 2) == 0 && shift > 0) { + mul = floor(mul / 2); + shift--; + } + + norm["mul"] = mul; + norm["shift"] = shift; +} + +BEGIN { + M_PI = atan2(0.0, -1.0); + + INT32_MAX = 1 + ((shl(1, 30) - 1) * 2); + INT32_MIN = -1 - INT32_MAX; + + FEEDEQ_TYPE_PEQ = 0; + FEEDEQ_TYPE_SHELF = 1; + + FEEDEQ_TYPE = FEEDEQ_TYPE_PEQ; + + FEEDEQ_COEFF_SHIFT = 24; + FEEDEQ_COEFF_ONE = shl(1, FEEDEQ_COEFF_SHIFT); + + FEEDEQ_PREAMP_SHIFT = 31; + FEEDEQ_PREAMP_ONE = shl(1, FEEDEQ_PREAMP_SHIFT); + FEEDEQ_PREAMP_BITDB = 6; # 20.0 * (log(2.0) / log(10.0)); + + FEEDEQ_GAIN_DIV = 10; + i = 0; + j = 1; + while (j < FEEDEQ_GAIN_DIV) { + j *= 2; + i++; + } + FEEDEQ_GAIN_SHIFT = i; + FEEDEQ_GAIN_FMASK = shl(1, FEEDEQ_GAIN_SHIFT) - 1; + + FEEDEQ_GAIN_RECIPROCAL = 1.0 / FEEDEQ_GAIN_DIV; + + if (ARGC == 2) { + i = 1; + split(ARGV[1], arg, ":"); + while (match(arg[i], "^[^0-9]*$")) { + if (arg[i] == "PEQ") { + FEEDEQ_TYPE = FEEDEQ_TYPE_PEQ; + } else if (arg[i] == "SHELF") { + FEEDEQ_TYPE = FEEDEQ_TYPE_SHELF; + } + i++; + } + split(arg[i++], subarg, ","); + FEEDEQ_TREBLE_SFREQ = 1.0 * subarg[1]; + FEEDEQ_TREBLE_SLOPE = 1.0 * subarg[2]; + split(arg[i++], subarg, ","); + FEEDEQ_BASS_SFREQ = 1.0 * subarg[1]; + FEEDEQ_BASS_SLOPE = 1.0 * subarg[2]; + split(arg[i++], subarg, ","); + FEEDEQ_GAIN_MIN = floor(1.0 * subarg[1]); + FEEDEQ_GAIN_MAX = floor(1.0 * subarg[2]); + if (length(subarg) > 2) { + j = floor(1.0 * FEEDEQ_GAIN_DIV * subarg[3]); + if (j < 2) + j = 1; + else if (j < 5) + j = 2; + else if (j < 10) + j = 5; + else + j = 10; + if (j > FEEDEQ_GAIN_DIV || (FEEDEQ_GAIN_DIV % j) != 0) + j = FEEDEQ_GAIN_DIV; + FEEDEQ_GAIN_STEP = j; + } else + FEEDEQ_GAIN_STEP = FEEDEQ_GAIN_DIV; + split(arg[i], subarg, ","); + for (i = 1; i <= length(subarg); i++) + allfreq[i - 1] = floor(1.0 * subarg[i]); + } else { + FEEDEQ_TREBLE_SFREQ = 16000.0; + FEEDEQ_TREBLE_SLOPE = 0.25; + FEEDEQ_BASS_SFREQ = 62.0; + FEEDEQ_BASS_SLOPE = 0.25; + + FEEDEQ_GAIN_MIN = -9; + FEEDEQ_GAIN_MAX = 9; + + FEEDEQ_GAIN_STEP = FEEDEQ_GAIN_DIV; + + + allfreq[0] = 44100; + allfreq[1] = 48000; + allfreq[2] = 88200; + allfreq[3] = 96000; + allfreq[4] = 176400; + allfreq[5] = 192000; + } + + FEEDEQ_LEVELS = ((FEEDEQ_GAIN_MAX - FEEDEQ_GAIN_MIN) * \ + floor(FEEDEQ_GAIN_DIV / FEEDEQ_GAIN_STEP)) + 1; + + FEEDEQ_ERR_CLIP = 0; + + smallest = 10.000000; + largest = 0.000010; + + printf("#ifndef _FEEDER_EQ_GEN_H_\n"); + printf("#define _FEEDER_EQ_GEN_H_\n\n"); + printf("/*\n"); + printf(" * Generated using feeder_eq_mkfilter.awk, heaven, wind and awesome.\n"); + printf(" *\n"); + printf(" * DO NOT EDIT!\n"); + printf(" */\n\n"); + printf("/*\n"); + printf(" * EQ: %s\n", (FEEDEQ_TYPE == FEEDEQ_TYPE_SHELF) ? \ + "Shelving" : "Peaking EQ"); + printf(" *\n * Settings:\n"); + printf(" * %d,%.4f,%d,%.4f:%d,%d,%.1f:", \ + FEEDEQ_TREBLE_SFREQ, FEEDEQ_TREBLE_SLOPE, \ + FEEDEQ_BASS_SFREQ, FEEDEQ_BASS_SLOPE, \ + FEEDEQ_GAIN_MIN, FEEDEQ_GAIN_MAX, \ + FEEDEQ_GAIN_STEP * FEEDEQ_GAIN_RECIPROCAL); + for (i = 0; i < length(allfreq); i++) { + if (i != 0) + printf(","); + printf("%d", allfreq[i]); + } + printf("\n"); + printf(" */\n\n"); + printf("struct feed_eq_coeff_tone {\n"); + printf("\tint32_t a1, a2;\n"); + printf("\tint32_t b0, b1, b2;\n"); + printf("};\n\n"); + printf("struct feed_eq_coeff {\n"); + #printf("\tstruct {\n"); + #printf("\t\tint32_t a1, a2;\n"); + #printf("\t\tint32_t b0, b1, b2;\n"); + #printf("\t} treble, bass;\n"); + printf("\tstruct feed_eq_coeff_tone treble;\n"); + printf("\tstruct feed_eq_coeff_tone bass;\n"); + #printf("\tstruct {\n"); + #printf("\t\tint32_t a1, a2;\n"); + #printf("\t\tint32_t b0, b1, b2;\n"); + #printf("\t} bass;\n"); + printf("};\n"); + for (i = 0; i < length(allfreq); i++) + feedeq_gen_freq_coeffs(allfreq[i]); + printf("\n"); + printf("static const struct {\n"); + printf("\tuint32_t rate;\n"); + printf("\tstruct feed_eq_coeff *coeff;\n"); + printf("} feed_eq_tab[] = {\n"); + for (i = 0; i < length(allfreq); i++) { + printf("\t{ %6d, eq_%-6d },\n", allfreq[i], allfreq[i]); + } + printf("};\n"); + + printf("\n#define FEEDEQ_RATE_MIN\t\t%d\n", allfreq[0]); + printf("#define FEEDEQ_RATE_MAX\t\t%d\n", allfreq[length(allfreq) - 1]); + printf("\n#define FEEDEQ_TAB_SIZE\t\t\t\t\t\t\t\\\n"); + printf("\t((int32_t)(sizeof(feed_eq_tab) / sizeof(feed_eq_tab[0])))\n"); + + printf("\nstatic const struct {\n"); + printf("\tint32_t mul, shift;\n"); + printf("} feed_eq_preamp[] = {\n"); + for (i = (FEEDEQ_GAIN_MAX * 2 * FEEDEQ_GAIN_DIV); i >= 0; \ + i -= FEEDEQ_GAIN_STEP) { + feedeq_calc_preamp(norm, i * FEEDEQ_GAIN_RECIPROCAL); + dbgain = ((FEEDEQ_GAIN_MAX * FEEDEQ_GAIN_DIV) - i) * \ + FEEDEQ_GAIN_RECIPROCAL; + printf("\t{ 0x%08x, 0x%08x },\t/* %+5.1f dB */\n", \ + norm["mul"], norm["shift"], dbgain); + } + printf("};\n"); + + printf("\n#define FEEDEQ_GAIN_MIN\t\t%d", FEEDEQ_GAIN_MIN); + printf("\n#define FEEDEQ_GAIN_MAX\t\t%d\n", FEEDEQ_GAIN_MAX); + + printf("\n#define FEEDEQ_GAIN_SHIFT\t%d\n", FEEDEQ_GAIN_SHIFT); + printf("#define FEEDEQ_GAIN_DIV\t\t%d\n", FEEDEQ_GAIN_DIV); + printf("#define FEEDEQ_GAIN_FMASK\t0x%08x\n", FEEDEQ_GAIN_FMASK); + printf("#define FEEDEQ_GAIN_STEP\t%d\n", FEEDEQ_GAIN_STEP); + + #printf("\n#define FEEDEQ_PREAMP_MIN\t-%d\n", \ + # shl(FEEDEQ_GAIN_MAX, FEEDEQ_GAIN_SHIFT)); + #printf("#define FEEDEQ_PREAMP_MAX\t%d\n", \ + # shl(FEEDEQ_GAIN_MAX, FEEDEQ_GAIN_SHIFT)); + + printf("\n#define FEEDEQ_COEFF_SHIFT\t%d\n", FEEDEQ_COEFF_SHIFT); + + #feedeq_calc_preamp(norm, FEEDEQ_GAIN_MAX); + + #printf("#define FEEDEQ_COEFF_NORM(v)\t("); + #if (norm["mul"] == 1) + # printf("(v) >> %d", norm["shift"]); + #else + # printf("(0x%xLL * (v)) >> %d", norm["mul"], norm["shift"]); + #printf(")\n"); + + #printf("\n#define FEEDEQ_LEVELS\t\t%d\n", FEEDEQ_LEVELS); + if (FEEDEQ_ERR_CLIP != 0) + printf("\n#define FEEDEQ_ERR_CLIP\t\t%d\n", FEEDEQ_ERR_CLIP); + printf("\n/*\n"); + printf(" * volume level mapping (0 - 100):\n"); + printf(" *\n"); + + for (i = 0; i <= 100; i++) { + ind = floor((i * FEEDEQ_LEVELS) / 100); + if (ind >= FEEDEQ_LEVELS) + ind = FEEDEQ_LEVELS - 1; + printf(" *\t%3d -> %3d (%+5.1f dB)\n", \ + i, ind, FEEDEQ_GAIN_MIN + \ + (ind * (FEEDEQ_GAIN_RECIPROCAL * FEEDEQ_GAIN_STEP))); + } + + printf(" */\n"); + printf("\n/*\n * smallest: %.32f\n * largest: %.32f\n */\n", \ + smallest, largest); + printf("\n#endif\t/* !_FEEDER_EQ_GEN_H_ */\n"); +} --- sys/tools/feeder_rate_mkfilter.awk.orig 1970-01-01 07:30:00.000000000 +07302008-07-03 10:57:51.000000000 +0800 +++ sys/tools/feeder_rate_mkfilter.awk 2008-07-03 10:56:10.000000000 +0800 @@ -0,0 +1,753 @@ +#!/usr/bin/awk -f +# +# Copyright (c) 2007-2008 Ariff Abdullah +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# + +# +# FIR filter design by windowing method. This might become one of the +# funniest joke I've ever written due to too many tricks being applied to +# ensure maximum precision (well, in fact this is already have the same +# precision granularity compared to its C counterpart). Nevertheless, it's +# working, precise, dynamically tunable based on "presets" (so that +# LIARS^H^H^H^H^H audiophiles can tune it based on their own +# placebo^H^H^H^H^H^H^H needs). +# + +# +# Some basic Math functions. +# +function abs(x) +{ + return (((x < 0) ? -x : x) + 0); +} + +function fabs(x) +{ + return (((x < 0.0) ? -x : x) + 0.0); +} + +function ceil(x, r) +{ + r = int(x); + if (r < x) + r++; + return (r + 0); +} + +function floor(x, r) +{ + r = int(x); + if (r > x) + r--; + return (r + 0); +} + +function pow(x, y) +{ + return (exp(1.0 * y * log(1.0 * x))); +} + +# +# What the hell... +# +function shl(x, y) +{ + while (y > 0) { + x *= 2; + y--; + } + return (x); +} + +function shr(x, y) +{ + while (y > 0 && x != 0) { + x = floor(x / 2); + y--; + } + return (x); +} + +function fx_floor(v, o, r) +{ + if (fabs(v) < fabs(smallest)) + smallest = v; + if (fabs(v) > fabs(largest)) + largest = v; + + r = floor((v * o) + 0.5); + if (r < INT32_MIN || r > INT32_MAX) + printf("\n#error overflow v=%f, please reduce %d\n", v, o); + + return (r); +} + +# +# Kaiser linear piecewise functions. +# +function kaiserAttn2Beta(attn, beta) +{ + if (attn < 0.0) + return (Z_KAISER_BETA_DEFAULT); + + if (attn > 50.0) + beta = 0.1102 * ((1.0 * attn) - 8.7); + else if (attn > 21.0) + beta = (0.5842 * pow((1.0 * attn) - 21.0, 0.4)) + \ + (0.07886 * ((1.0 * attn) - 21.0)); + else + beta = 0.0; + + return (beta); +} + +function kaiserBeta2Attn(beta, x, y, i, attn, xbeta) +{ + if (beta < Z_WINDOW_KAISER) + return (Z_KAISER_ATTN_DEFAULT); + + if (beta > kaiserAttn2Beta(50.0)) + attn = ((1.0 * beta) / 0.1102) + 8.7; + else { + x = 21.0; + y = 50.0; + attn = 0.5 * (x + y); + for (i = 0; i < 128; i++) { + xbeta = kaiserAttn2Beta(attn) + if (beta == xbeta || \ + (i > 63 && \ + fabs(beta - xbeta) < Z_KAISER_EPSILON)) + break; + if (beta > xbeta) + x = attn; + else + y = attn; + attn = 0.5 * (x + y); + } + } + + return (attn); +} + +function kaiserRolloff(len, attn) +{ + return (1.0 - (((1.0 * attn) - 7.95) / (((1.0 * len) - 1.0) * 14.36))); +} + +# +# 0th order modified Bessel function of the first kind. +# +function I0(x, s, u, n, h, t) +{ + s = n = u = 1.0; + h = x * 0.5; + + do { + t = h / n; + n += 1.0; + t *= t; + u *= t; + s += u; + } while (u >= (I0_EPSILON * s)); + + return (s); +} + +function wname(beta) +{ + if (beta >= Z_WINDOW_KAISER) + return ("Kaiser"); + else if (beta == Z_WINDOW_BLACKMAN_NUTTALL) + return ("Blackman - Nuttall"); + else if (beta == Z_WINDOW_NUTTALL) + return ("Nuttall"); + else if (beta == Z_WINDOW_BLACKMAN_HARRIS) + return ("Blackman - Harris"); + else if (beta == Z_WINDOW_BLACKMAN) + return ("Blackman"); + else if (beta == Z_WINDOW_HAMMING) + return ("Hamming"); + else if (beta == Z_WINDOW_HANN) + return ("Hann"); + else + return ("What The Hell !?!?"); +} + +function rolloff_round(x) +{ + if (x < 0.67) + x = 0.67; + else if (x > 1.0) + x = 1.0; + + return (x); +} + +function tap_round(x, y) +{ + y = floor(x + 3); + y -= y % 4; + return (y); +} + +function lpf(imp, n, rolloff, beta, num, i, j, x, nm, ibeta, w) +{ + rolloff = rolloff_round(rolloff + (Z_NYQUIST_HOVER * (1.0 - rolloff))); + imp[0] = rolloff; + + # + # Generate ideal sinc impulses, locate the last zero-crossing and pad + # the remaining with 0. + # + # Note that there are other (faster) ways of calculating this without + # the misery of traversing the entire sinc given the fact that the + # distance between each zero crossings is actually the bandwidth of + # the impulses, but it seems having 0.0001% chances of failure due to + # limited precision. + # + j = n; + for (i = 1; i < n; i++) { + x = (M_PI * i) / (1.0 * num); + imp[i] = sin(x * rolloff) / x; + if (i != 1 && (imp[i] * imp[i - 1]) <= 0.0) + j = i; + } + + for (i = j; i < n; i++) + imp[i] = 0.0; + + nm = 1.0 * (j - 1); + + if (beta >= Z_WINDOW_KAISER) + ibeta = I0(beta); + + for (i = 1; i < j; i++) { + if (beta >= Z_WINDOW_KAISER) { + # Kaiser window... + x = (1.0 * i) / nm; + w = I0(beta * sqrt(1.0 - (x * x))) / ibeta; + } else { + # Cosined windows... + x = (M_PI * i) / nm; + if (beta == Z_WINDOW_BLACKMAN_NUTTALL) { + # Blackman - Nuttall + w = 0.36335819 + (0.4891775 * cos(x)) + \ + (0.1365995 * cos(2 * x)) + \ + (0.0106411 * cos(3 * x)); + } else if (beta == Z_WINDOW_NUTTALL) { + # Nuttall + w = 0.355768 + (0.487396 * cos(x)) + \ + (0.144232 * cos(2 * x)) + \ + (0.012604 * cos(3 * x)); + } else if (beta == Z_WINDOW_BLACKMAN_HARRIS) { + # Blackman - Harris + w = 0.422323 + (0.49755 * cos(x)) + \ + (0.07922 * cos(2 * x)); + } else if (beta == Z_WINDOW_BLACKMAN) { + # Blackman + w = 0.42 + (0.50 * cos(x)) + \ + (0.08 * cos(2 * x)); + } else if (beta == Z_WINDOW_HAMMING) { + # Hamming + w = 0.54 + (0.46 * cos(x)); + } else if (beta == Z_WINDOW_HANN) { + # Hann + w = 0.50 + (0.50 * cos(x)); + } else { + # What The Hell !?!? + w = 0.0; + } + } + imp[i] *= w; + } + + imp["impulse_length"] = j; + imp["rolloff"] = rolloff; +} + +function makeFilter(imp, nmult, rolloff, beta, num, \ + nwing, mwing, nrolloff, i, dcgain, v, quality) +{ + nwing = floor((nmult * num) / 2) + 1; + + lpf(imp, nwing, rolloff, beta, num); + + mwing = imp["impulse_length"]; + nrolloff = imp["rolloff"]; + quality = imp["quality"]; + + dcgain = 0.0; + for (i = num; i < mwing; i += num) + dcgain += imp[i]; + dcgain *= 2.0; + dcgain += imp[0]; + + for (i = 0; i < nwing; i++) + imp[i] /= dcgain; + + if (quality > 2) + printf("\n"); + printf("/*\n"); + printf(" * quality = %d\n", quality); + printf(" * window = %s\n", wname(beta)); + if (beta >= Z_WINDOW_KAISER) { + printf(" * beta: %.2f\n", beta); + printf(" * stop: -%.2f dB\n", \ + kaiserBeta2Attn(beta)); + } + printf(" * length = %d\n", nmult); + printf(" * bandwidth = %.2f%%", rolloff * 100.0); + if (rolloff != nrolloff) { + printf(" + %.2f%% = %.2f%% (nyquist hover: %.2f%%)", \ + (nrolloff - rolloff) * 100.0, nrolloff * 100.0, \ + Z_NYQUIST_HOVER * 100.0); + } + printf("\n"); + printf(" * drift = %d\n", num); + printf(" * width = %d\n", mwing); + printf(" */\n"); + printf("static int32_t z_coeff_q%d[%d] = {", \ + quality, nwing + (Z_COEFF_OFFSET * 2)); + for (i = 0; i < (nwing + (Z_COEFF_OFFSET * 2)); i++) { + if ((i % 5) == 0) + printf("\n "); + if (i < Z_COEFF_OFFSET) + v = fx_floor(imp[Z_COEFF_OFFSET - i], Z_COEFF_ONE); + else if ((i - Z_COEFF_OFFSET) >= nwing) + v = fx_floor( \ + imp[nwing + nwing - i + Z_COEFF_OFFSET - 1],\ + Z_COEFF_ONE); + else + v = fx_floor(imp[i - Z_COEFF_OFFSET], Z_COEFF_ONE); + printf(" %s0x%08x,", (v < 0) ? "-" : " ", abs(v)); + } + printf("\n};\n\n"); + printf("/*\n"); + printf(" * interpolated q%d differences.\n", quality); + printf(" */\n"); + printf("static int32_t z_dcoeff_q%d[%d] = {", quality, nwing); + for (i = 1; i <= nwing; i++) { + if ((i % 5) == 1) + printf("\n "); + v = -imp[i - 1]; + if (i != nwing) + v += imp[i]; + v = fx_floor(v, Z_INTERP_COEFF_ONE); + if (abs(v) > abs(largest_interp)) + largest_interp = v; + printf(" %s0x%08x,", (v < 0) ? "-" : " ", abs(v)); + } + printf("\n};\n"); + + return (nwing); +} + +function filter_parse(s, a, i, attn, alen) +{ + split(s, a, ":"); + alen = length(a); + + if (alen == 1 || alen == 2) { + if (a[1] == "nyquist_hover") { + i = 1.0 * a[2]; + Z_NYQUIST_HOVER = (i > 0.0 && i < 1.0) ? i : 0.0; + return (-1); + } + i = 1; + if (alen == 1) { + attn = Z_KAISER_ATTN_DEFAULT; + Popts["beta"] = Z_KAISER_BETA_DEFAULT; + } else if (Z_WINDOWS[a[1]] < Z_WINDOW_KAISER) { + Popts["beta"] = Z_WINDOWS[a[1]]; + i = tap_round(a[2]); + Popts["nmult"] = i; + if (i < 28) + i = 28; + i = 1.0 - (6.44 / i); + Popts["rolloff"] = rolloff_round(i); + return (0); + } else { + attn = 1.0 * a[i++]; + Popts["beta"] = kaiserAttn2Beta(attn); + } + i = tap_round(a[i]); + Popts["nmult"] = i; + if (i > 7 && i < 28) + i = 27; + i = kaiserRolloff(i, attn); + Popts["rolloff"] = rolloff_round(i); + + return (0); + } + + if (!(alen == 3 || alen == 4)) + return (-1); + + i = 2; + + if (a[1] == "kaiser") { + if (alen > 2) + Popts["beta"] = 1.0 * a[i++]; + else + Popts["beta"] = Z_KAISER_BETA_DEFAULT; + } else if (Z_WINDOWS[a[1]] < Z_WINDOW_KAISER) + Popts["beta"] = Z_WINDOWS[a[1]]; + else if (1.0 * a[1] < Z_WINDOW_KAISER) + return (-1); + else + Popts["beta"] = kaiserAttn2Beta(1.0 * a[1]); + Popts["nmult"] = tap_round(a[i++]); + if (a[1] == "kaiser" && alen == 3) + i = kaiserRolloff(Popts["nmult"], \ + kaiserBeta2Attn(Popts["beta"])); + else + i = 1.0 * a[i]; + Popts["rolloff"] = rolloff_round(i); + + return (0); +} + +function genscale(bit, s1, s2, scale) +{ + s1 = Z_COEFF_SHIFT - (32 - bit); + s2 = Z_SHIFT + (32 - bit); + + if (s1 == 0) + scale = "v"; + else if (s1 < 0) + scale = sprintf("(v) << %d", abs(s1)); + else + scale = sprintf("(v) >> %d", s1); + + scale = sprintf("(%s) * Z_SCALE_CAST(s)", scale); + + if (s2 != 0) + scale = sprintf("(%s) >> %d", scale, s2); + + printf("#define Z_SCALE_%d(v, s)\t%s(%s)\n", \ + bit, (bit < 10) ? "\t" : "", scale); +} + +function genlerp(bit, use64, lerp) +{ + if ((bit + Z_LINEAR_SHIFT) <= 32) { + lerp = sprintf("(((y) - (x)) * (z)) >> %d", Z_LINEAR_SHIFT); + } else if (use64 != 0) { + if ((bit + Z_LINEAR_SHIFT) <= 64) { + lerp = sprintf( \ + "(((int64_t)(y) - (x)) * (z)) " \ + ">> %d", \ + Z_LINEAR_SHIFT); + } else { + lerp = sprintf( \ + "((int64_t)((y) >> %d) - ((x) >> %d)) * ", \ + "(z)" \ + bit + Z_LINEAR_SHIFT - 64, \ + bit + Z_LINEAR_SHIFT - 64); + if ((64 - bit) != 0) + lerp = sprintf("(%s) >> %d", lerp, 64 - bit); + } + } else { + lerp = sprintf( \ + "(((y) >> %d) - ((x) >> %d)) * (z)", \ + bit + Z_LINEAR_SHIFT - 32, \ + bit + Z_LINEAR_SHIFT - 32); + if ((32 - bit) != 0) + lerp = sprintf("(%s) >> %d", lerp, 32 - bit); + } + + printf("#define Z_LINEAR_INTERPOLATE_%d(z, x, y)" \ + "\t\t\t\t%s\\\n\t((x) + (%s))\n", \ + bit, (bit < 10) ? "\t" : "", lerp); +} + +BEGIN { + I0_EPSILON = 1e-21; + M_PI = atan2(0.0, -1.0); + + INT32_MAX = 1 + ((shl(1, 30) - 1) * 2); + INT32_MIN = -1 - INT32_MAX; + + Z_COEFF_OFFSET = 5; + + Z_FULL_SHIFT = 30; + Z_FULL_ONE = shl(1, Z_FULL_SHIFT); + + Z_COEFF_SHIFT = 28; + Z_COEFF_ONE = shl(1, Z_COEFF_SHIFT); + + Z_INTERP_COEFF_SHIFT = 24; + Z_INTERP_COEFF_ONE = shl(1, Z_INTERP_COEFF_SHIFT); + + # + # Filter oversampling factor. + # + # 6, 7, or 8 depending on how much you can trade off between memory + # consumption (due to large tables) and precision / quality. + # + Z_DRIFT_SHIFT = 7; + Z_DRIFT_ONE = shl(1, Z_DRIFT_SHIFT); + + Z_SHIFT = Z_FULL_SHIFT - Z_DRIFT_SHIFT; + Z_ONE = shl(1, Z_SHIFT); + Z_MASK = Z_ONE - 1; + + Z_LINEAR_FULL_SHIFT = Z_FULL_SHIFT; + Z_LINEAR_FULL_ONE = shl(1, Z_LINEAR_FULL_SHIFT); + Z_LINEAR_SHIFT = 8; + Z_LINEAR_UNSHIFT = Z_LINEAR_FULL_SHIFT - Z_LINEAR_SHIFT; + Z_LINEAR_ONE = shl(1, Z_LINEAR_SHIFT) + + # meehhhh... let it overflow... + #Z_SCALE_SHIFT = 31; + #Z_SCALE_ONE = shl(1, Z_SCALE_SHIFT); + + Z_WINDOW_KAISER = 0.0; + Z_WINDOW_BLACKMAN_NUTTALL = -1.0; + Z_WINDOW_NUTTALL = -2.0; + Z_WINDOW_BLACKMAN_HARRIS = -3.0; + Z_WINDOW_BLACKMAN = -4.0; + Z_WINDOW_HAMMING = -5.0; + Z_WINDOW_HANN = -6.0; + + Z_WINDOWS["blackman_nuttall"] = Z_WINDOW_BLACKMAN_NUTTALL; + Z_WINDOWS["nuttall"] = Z_WINDOW_NUTTALL; + Z_WINDOWS["blackman_harris"] = Z_WINDOW_BLACKMAN_HARRIS; + Z_WINDOWS["blackman"] = Z_WINDOW_BLACKMAN; + Z_WINDOWS["hamming"] = Z_WINDOW_HAMMING; + Z_WINDOWS["hann"] = Z_WINDOW_HANN; + + Z_KAISER_2_BLACKMAN_BETA = 8.568611; + Z_KAISER_2_BLACKMAN_NUTTALL_BETA = 11.98; + + Z_KAISER_ATTN_DEFAULT = 100; + Z_KAISER_BETA_DEFAULT = kaiserAttn2Beta(Z_KAISER_ATTN_DEFAULT); + + Z_KAISER_EPSILON = 1e-21; + + # + # This is practically a joke. + # + Z_NYQUIST_HOVER = 0.0; + + smallest = 10.000000; + largest = 0.000010; + largest_interp = 0; + + if (ARGC < 2) { + ARGC = 1; + ARGV[ARGC++] = "100:8:0.85"; + ARGV[ARGC++] = "100:36:0.90"; + ARGV[ARGC++] = "100:164:0.97"; + #ARGV[ARGC++] = "100:8"; + #ARGV[ARGC++] = "100:16"; + #ARGV[ARGC++] = "100:32:0.7929"; + #ARGV[ARGC++] = "100:64:0.8990"; + #ARGV[ARGC++] = "100:128:0.9499"; + } + + printf("#ifndef _FEEDER_RATE_GEN_H_\n"); + printf("#define _FEEDER_RATE_GEN_H_\n\n"); + printf("/*\n"); + printf(" * Generated using feeder_rate_mkfilter.awk, heaven, wind and awesome.\n"); + printf(" *\n"); + printf(" * DO NOT EDIT!\n"); + printf(" */\n\n"); + imp["quality"] = 2; + for (i = 1; i < ARGC; i++) { + if (filter_parse(ARGV[i]) == 0) { + beta = Popts["beta"]; + nmult = Popts["nmult"]; + rolloff = Popts["rolloff"]; + ztab[imp["quality"] - 2] = \ + makeFilter(imp, nmult, rolloff, beta, Z_DRIFT_ONE); + imp["quality"]++; + } + } + + printf("\n"); + # + # XXX + # + #if (length(ztab) > 0) { + # j = 0; + # for (i = 0; i < length(ztab); i++) { + # if (ztab[i] > j) + # j = ztab[i]; + # } + # printf("static int32_t z_coeff_zero[%d] = {", j); + # for (i = 0; i < j; i++) { + # if ((i % 19) == 0) + # printf("\n"); + # printf(" 0,"); + # } + # printf("\n};\n\n"); + #} + # + # XXX + # + printf("static const struct {\n"); + printf("\tint32_t len;\n"); + printf("\tint32_t *coeff;\n"); + printf("\tint32_t *dcoeff;\n"); + printf("} z_coeff_tab[] = {\n"); + if (length(ztab) > 0) { + j = 0; + for (i = 0; i < length(ztab); i++) { + if (ztab[i] > j) + j = ztab[i]; + } + j = length(sprintf("%d", j)); + lfmt = sprintf("%%%dd", j); + j = length(sprintf("z_coeff_q%d", length(ztab) + 1)); + zcfmt = sprintf("%%-%ds", j); + zdcfmt = sprintf("%%-%ds", j + 1); + + for (i = 0; i < length(ztab); i++) { + l = sprintf(lfmt, ztab[i]); + zc = sprintf("z_coeff_q%d", i + 2); + zc = sprintf(zcfmt, zc); + zdc = sprintf("z_dcoeff_q%d", i + 2); + zdc = sprintf(zdcfmt, zdc); + printf("\t{ %s, %s, %s },\n", l, zc, zdc); + } + } else + printf("\t{ 0, NULL, NULL }\n"); + printf("};\n\n"); + + #Z_UNSHIFT = 0; + #v = shr(Z_ONE - 1, Z_UNSHIFT) * abs(largest_interp); + #while (v < 0 || v > INT32_MAX) { + # Z_UNSHIFT += 1; + # v = shr(Z_ONE - 1, Z_UNSHIFT) * abs(largest_interp); + #} + v = ((Z_ONE - 1) * abs(largest_interp)) / INT32_MAX; + Z_UNSHIFT = ceil(log(v) / log(2.0)); + Z_INTERP_SHIFT = Z_SHIFT - Z_UNSHIFT + Z_INTERP_COEFF_SHIFT; + + Z_INTERP_UNSHIFT = (Z_SHIFT - Z_UNSHIFT) + Z_INTERP_COEFF_SHIFT \ + - Z_COEFF_SHIFT; + + printf("#define Z_COEFF_TAB_SIZE\t\t\t\t\t\t\\\n"); + printf("\t((int32_t)(sizeof(z_coeff_tab) /"); + printf(" sizeof(z_coeff_tab[0])))\n\n"); + printf("#define Z_COEFF_OFFSET\t\t%d\n\n", Z_COEFF_OFFSET); + printf("#define Z_RSHIFT(x, y)\t\t(((x) + " \ + "(1 << ((y) - 1))) >> (y))\n"); + printf("#define Z_RSHIFT_L(x, y)\t(((x) + " \ + "(1LL << ((y) - 1))) >> (y))\n\n"); + printf("#define Z_FULL_SHIFT\t\t%d\n", Z_FULL_SHIFT); + printf("#define Z_FULL_ONE\t\t0x%08x%s\n", Z_FULL_ONE, \ + (Z_FULL_ONE > INT32_MAX) ? "U" : ""); + printf("\n"); + printf("#define Z_DRIFT_SHIFT\t\t%d\n", Z_DRIFT_SHIFT); + #printf("#define Z_DRIFT_ONE\t\t0x%08x\n", Z_DRIFT_ONE); + printf("\n"); + printf("#define Z_SHIFT\t\t\t%d\n", Z_SHIFT); + printf("#define Z_ONE\t\t\t0x%08x\n", Z_ONE); + printf("#define Z_MASK\t\t\t0x%08x\n", Z_MASK); + printf("\n"); + printf("#define Z_COEFF_SHIFT\t\t%d\n", Z_COEFF_SHIFT); + zinterphp = "(z) * (d)"; + zinterpunshift = Z_SHIFT + Z_INTERP_COEFF_SHIFT - Z_COEFF_SHIFT; + if (zinterpunshift > 0) { + v = (Z_ONE - 1) * abs(largest_interp); + if (v < INT32_MIN || v > INT32_MAX) + zinterphp = sprintf("(int64_t)%s", zinterphp); + zinterphp = sprintf("(%s) >> %d", zinterphp, zinterpunshift); + } else if (zinterpunshift < 0) + zinterphp = sprintf("(%s) << %d", zinterphp, \ + abs(zinterpunshift)); + if (Z_UNSHIFT == 0) + zinterp = "z"; + else + zinterp = sprintf("(z) >> %d", Z_UNSHIFT); + zinterp = sprintf("(%s) * (d)", zinterp); + if (Z_INTERP_UNSHIFT < 0) + zinterp = sprintf("(%s) << %d", zinterp, \ + abs(Z_INTERP_UNSHIFT)); + else if (Z_INTERP_UNSHIFT > 0) + zinterp = sprintf("(%s) >> %d", zinterp, Z_INTERP_UNSHIFT); + if (zinterphp != zinterp) { + printf("\n#ifdef FEEDER_RATE_HP\n"); + printf("#define Z_COEFF_INTERPOLATE(z, c, d)" \ + "\t\t\t\t\t\\\n\t((c) + (%s))\n", zinterphp); + printf("#else\n"); + printf("#define Z_COEFF_INTERPOLATE(z, c, d)" \ + "\t\t\t\t\t\\\n\t((c) + (%s))\n", zinterp); + printf("#endif\n"); + } else + printf("#define Z_COEFF_INTERPOLATE(z, c, d)" \ + "\t\t\t\t\t\\\n\t((c) + (%s))\n", zinterp); + #printf("\n"); + #printf("#define Z_SCALE_SHIFT\t\t%d\n", Z_SCALE_SHIFT); + #printf("#define Z_SCALE_ONE\t\t0x%08x%s\n", Z_SCALE_ONE, \ + # (Z_SCALE_ONE > INT32_MAX) ? "U" : ""); + printf("\n"); + printf("#define Z_SCALE_CAST(s)\t\t((uint32_t)(s))\n"); + genscale(8); + genscale(16); + genscale(24); + genscale(32); + printf("\n"); + printf("#define Z_LINEAR_FULL_ONE\t0x%08xU\n", Z_LINEAR_FULL_ONE); + printf("#define Z_LINEAR_SHIFT\t\t%d\n", Z_LINEAR_SHIFT); + printf("#define Z_LINEAR_UNSHIFT\t%d\n", Z_LINEAR_UNSHIFT); + printf("#define Z_LINEAR_ONE\t\t0x%08x\n", Z_LINEAR_ONE); + printf("\n"); + printf("#ifdef PCM_USE_64BIT_ARITH\n"); + genlerp(8, 1); + genlerp(16, 1); + genlerp(24, 1); + genlerp(32, 1); + printf("#else\t/* !PCM_USE_64BIT_ARITH */\n"); + genlerp(8, 0); + genlerp(16, 0); + genlerp(24, 0); + genlerp(32, 0); + printf("#endif\t/* PCM_USE_64BIT_ARITH */\n"); + printf("\n"); + printf("#define Z_QUALITY_ZOH\t\t0\n"); + printf("#define Z_QUALITY_LINEAR\t1\n"); + printf("#define Z_QUALITY_SINC\t\t%d\n", \ + floor((length(ztab) - 1) / 2) + 2); + printf("\n"); + printf("#define Z_QUALITY_MIN\t\t0\n"); + printf("#define Z_QUALITY_MAX\t\t%d\n", length(ztab) + 1); + printf("\n/*\n * smallest: %.32f\n * largest: %.32f\n *\n", \ + smallest, largest); + printf(" * z_unshift=%d, z_interp_shift=%d\n *\n", \ + Z_UNSHIFT, Z_INTERP_SHIFT); + v = shr(Z_ONE - 1, Z_UNSHIFT) * abs(largest_interp); + printf(" * largest interpolation multiplication: %d\n */\n", v); + if (v < INT32_MIN || v > INT32_MAX) { + printf("\n#ifndef FEEDER_RATE_HP\n"); + printf("#error interpolation overflow, please reduce" \ + " Z_INTERP_SHIFT\n"); + printf("#endif\n"); + } + + printf("\n#endif /* !_FEEDER_RATE_GEN_H_ */\n"); +} --- sys/tools/snd_fxdiv_gen.awk.orig 1970-01-01 07:30:00.000000000 +07302008-07-03 10:57:51.000000000 +0800 +++ sys/tools/snd_fxdiv_gen.awk 2008-06-30 10:14:34.000000000 +0800 @@ -0,0 +1,142 @@ +#!/usr/bin/awk -f +# +# Copyright (c) 2008 Ariff Abdullah +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# $FreeBSD$ +# + +function floor(x, r) +{ + r = int(x); + if (r > x) + r--; + return (r + 0); +} + +function shl(x, y) +{ + while (y > 0) { + x *= 2; + y--; + } + return (x); +} + +function shr(x, y) +{ + while (y > 0 && x != 0) { + x = floor(x / 2); + y--; + } + return (x); +} + +function calcdiv(r, x, y, z) +{ + y = floor(FXONE / x); + z = FXSHIFT; + + while (shr((y * x), z) < 1) + y++; + + while ((y % 2) == 0 && z > 0) { + y = floor(y / 2); + z--; + } + + r["mul"] = y; + r["shift"] = z; +} + +BEGIN { + FXSHIFT = 16; + FXONE = shl(1, FXSHIFT); + + SND_CHN_MAX = 18; + + PCM_8_BPS = 1; + PCM_16_BPS = 2; + PCM_24_BPS = 3; + PCM_32_BPS = 4; + + SND_MAX_ALIGN = SND_CHN_MAX * PCM_32_BPS; + + for (i = 1; i <= SND_CHN_MAX; i++) { + aligns[PCM_8_BPS * i] = 1; + aligns[PCM_16_BPS * i] = 1; + aligns[PCM_24_BPS * i] = 1; + aligns[PCM_32_BPS * i] = 1; + } + + printf("#ifndef _SND_FXDIV_GEN_H_\n"); + printf("#define _SND_FXDIV_GEN_H_\n\n"); + + printf("/*\n"); + printf(" * Generated using snd_fxdiv_gen.awk, heaven, wind and awesome.\n"); + printf(" *\n"); + printf(" * DO NOT EDIT!\n"); + printf(" */\n\n"); + printf("#ifdef SND_USE_FXDIV\n\n"); + + printf("/*\n"); + printf(" * Fast unsigned 32bit integer division and rounding, accurate for\n"); + printf(" * x = 1 - %d. This table should be enough to handle possible\n", FXONE); + printf(" * division for 1 - 72 (more can be generated though..).\n"); + printf(" *\n"); + printf(" * 72 = SND_CHN_MAX * PCM_32_BPS, which is why....\n"); + printf(" */\n\n"); + + printf("static const uint32_t snd_fxdiv_table[][2] = {\n"); + + for (i = 1; i <= SND_MAX_ALIGN; i++) { + if (aligns[i] != 1) + continue; + calcdiv(r, i); + printf("\t[0x%02x] = { 0x%04x, 0x%02x },", \ + i, r["mul"], r["shift"]); + printf("\t/* x / %-2d = (x * %-5d) >> %-2d */\n", \ + i, r["mul"], r["shift"]); + } + + printf("};\n\n"); + + printf("#define SND_FXDIV_MAX\t\t0x%08x\n", FXONE); + printf("#define SND_FXDIV(x, y)\t\t(((uint32_t)(x) *\t\t\t\\\n"); + printf("\t\t\t\t snd_fxdiv_table[y][0]) >>\t\t\\\n"); + printf("\t\t\t\t snd_fxdiv_table[y][1])\n"); + printf("#define SND_FXROUND(x, y)\t(SND_FXDIV(x, y) * (y))\n"); + printf("#define SND_FXMOD(x, y)\t\t((x) - SND_FXROUND(x, y))\n\n"); + + printf("#else\t/* !SND_USE_FXDIV */\n\n"); + + printf("#define SND_FXDIV_MAX\t\t0x%08x\n", 131072); + printf("#define SND_FXDIV(x, y)\t\t((x) / (y))\n"); + printf("#define SND_FXROUND(x, y)\t((x) - ((x) %% (y)))\n"); + printf("#define SND_FXMOD(x, y)\t\t((x) %% (y))\n\n"); + + printf("#endif\t/* SND_USE_FXDIV */\n\n"); + + printf("#endif\t/* !_SND_FXDIV_GEN_H_ */\n"); +} --- sys/dev/sound/lpmap.c.orig 1970-01-01 07:30:00.000000000 +0730 +++ sys/dev/sound/lpmap.c 2008-06-30 10:14:34.000000000 +0800 @@ -0,0 +1,312 @@ +/*- + * Copyright (c) 2007 Ariff Abdullah + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + * + * "Little-pmap": Lost Technology for RELENG_5/RELENG_6. + * + */ + +#ifndef _LPMAP_C_ +#define _LPMAP_C_ + +#if !(defined(__i386__) || defined(__amd64__)) +#error "Not applicable for non-i386/non-amd64" +#endif + +/* For cpu_feature, VM_MAXUSER_ADDRESS */ +#include +#include +#include + +#ifndef PG_PTE_PAT +#define PG_PTE_PAT 0x0080 +#endif + +#ifndef PG_PDE_PAT +#define PG_PDE_PAT 0x1000 +#endif + +#ifndef PAT_UNCACHEABLE +#define PAT_UNCACHEABLE 0x00 +#endif + +#ifndef PAT_WRITE_COMBINING +#define PAT_WRITE_COMBINING 0x01 +#endif + +#ifndef PAT_WRITE_THROUGH +#define PAT_WRITE_THROUGH 0x04 +#endif + +#ifndef PAT_WRITE_PROTECTED +#define PAT_WRITE_PROTECTED 0x05 +#endif + +#ifndef PAT_WRITE_BACK +#define PAT_WRITE_BACK 0x06 +#endif + +#ifndef PAT_UNCACHED +#define PAT_UNCACHED 0x07 +#endif + +#define lpmap_p(x, y...) /*printf("%s(): "x, __func__, y)*/ + +#undef pmap_change_attr +#define pmap_change_attr lpmap_change_attr + +#if defined(__i386__) +#define lpmap_pde(m, v) (&((m)->pm_pdir[(vm_offset_t)(v) >> PDRSHIFT])) +#elif defined(__amd64__) +static __inline vm_pindex_t +lpmap_pde_index(vm_offset_t va) +{ + return ((va >> PDRSHIFT) & ((1UL << NPDEPGSHIFT) - 1)); +} + +static __inline vm_pindex_t +lpmap_pdpe_index(vm_offset_t va) +{ + return ((va >> PDPSHIFT) & ((1UL << NPDPEPGSHIFT) - 1)); +} + +static __inline vm_pindex_t +lpmap_pml4e_index(vm_offset_t va) +{ + return ((va >> PML4SHIFT) & ((1UL << NPML4EPGSHIFT) - 1)); +} + +static __inline pd_entry_t * +lpmap_pdpe_to_pde(pdp_entry_t *pdpe, vm_offset_t va) +{ + pd_entry_t *pde; + + pde = (pd_entry_t *)PHYS_TO_DMAP(*pdpe & PG_FRAME); + return (&pde[lpmap_pde_index(va)]); +} + +static __inline pml4_entry_t * +lpmap_pml4e(pmap_t pmap, vm_offset_t va) +{ + if (pmap == NULL) + return (NULL); + return (&pmap->pm_pml4[lpmap_pml4e_index(va)]); +} + +static __inline pdp_entry_t * +lpmap_pml4e_to_pdpe(pml4_entry_t *pml4e, vm_offset_t va) +{ + pdp_entry_t *pdpe; + + pdpe = (pdp_entry_t *)PHYS_TO_DMAP(*pml4e & PG_FRAME); + return (&pdpe[lpmap_pdpe_index(va)]); +} + +static __inline pdp_entry_t * +lpmap_pdpe(pmap_t pmap, vm_offset_t va) +{ + pml4_entry_t *pml4e; + + pml4e = lpmap_pml4e(pmap, va); + if (pml4e == NULL || (*pml4e & PG_V) == 0) + return (NULL); + return (lpmap_pml4e_to_pdpe(pml4e, va)); +} + +static __inline pd_entry_t * +lpmap_pde(pmap_t pmap, vm_offset_t va) +{ + pdp_entry_t *pdpe; + + pdpe = lpmap_pdpe(pmap, va); + if (pdpe == NULL || (*pdpe & PG_V) == 0) + return (NULL); + return (lpmap_pdpe_to_pde(pdpe, va)); +} +#endif + +static __inline int +lpmap_validate_range(vm_offset_t base, vm_size_t size) +{ + vm_offset_t va; + pt_entry_t *pte; + pd_entry_t *pde; + + if (base <= VM_MAXUSER_ADDRESS) { + lpmap_p("base <= VM_MAXUSER_ADDRESS : base=0x%jx\n", + (uintmax_t)base); + return (EINVAL); + } + + va = base; + while (va < (base + size)) { + pde = lpmap_pde(kernel_pmap, va); + if (pde == NULL || *pde == 0) { + lpmap_p("Failed: %s : va=0x%jx\n", + (pde == NULL) ? "pde == NULL" : "*pde == 0", + (uintmax_t)va); + return (EINVAL); + } + if (*pde & PG_PS) { +#if defined(__amd64__) + if (size < NBPDR) { + lpmap_p("Failed: size < NBPDR : va=0x%jx\n", + (uintmax_t)va); + return (EINVAL); + } + va += NBPDR; +#else + lpmap_p("Failed: (*pde & PG_PS) != 0 : va=0x%jx\n", + (uintmax_t)va); + return (EINVAL); +#endif + } else { + pte = vtopte(va); + if (pte == NULL || *pte == 0) { + lpmap_p("Failed: %s : va=0x%jx\n", + (pte == NULL) ? "pte == NULL" : "*pte == 0", + (uintmax_t)va); + return (EINVAL); + } + va += PAGE_SIZE; + } + } + + return (0); +} + +#if (__FreeBSD_version >= 800000 && __FreeBSD_version < 800005) || \ + __FreeBSD_version < 700055 +extern int osreldate; +#else +#include +#endif + +static int +lpmap_change_attr(vm_offset_t va, vm_size_t size, int mode) +{ + vm_offset_t base, offset; + pt_entry_t *pte; +#if defined(__amd64__) + pd_entry_t *pde; +#endif + u_int opxe, npxe, ptefl, pdefl, attr; + int err, flushtlb; + + switch (mode) { + case PAT_UNCACHED: + case PAT_WRITE_COMBINING: + case PAT_WRITE_PROTECTED: + if (!(cpu_feature & CPUID_PAT)) + mode = PAT_UNCACHEABLE; + break; + default: + break; + } + + attr = 0; + + switch (mode) { + case PAT_UNCACHED: + case PAT_UNCACHEABLE: + case PAT_WRITE_PROTECTED: + attr |= PG_NC_PCD; + case PAT_WRITE_THROUGH: + attr |= PG_NC_PWT; + break; + case PAT_WRITE_COMBINING: + attr |= PG_NC_PCD; + break; + case PAT_WRITE_BACK: + break; + default: + lpmap_p("Unsupported mode=0x%08x\n", mode); + return (EINVAL); + break; + } + + ptefl = PG_NC_PCD | PG_NC_PWT; + pdefl = PG_NC_PCD | PG_NC_PWT; + if (osreldate >= 602110) { + ptefl |= PG_PTE_PAT; + pdefl |= PG_PDE_PAT; + } + + base = va & PG_FRAME; + offset = va & PAGE_MASK; + size = roundup(offset + size, PAGE_SIZE); + + err = lpmap_validate_range(base, size); + if (err != 0) { + lpmap_p("Validation failed! " + "vm_offset_t=0x%jx vm_size_t=%ju attr=0x%04x\n", + (uintmax_t)va, (uintmax_t)size, attr); + return (err); + } + + lpmap_p("vm_offset_t=0x%jx vm_size_t=%ju attr=0x%04x\n", + (uintmax_t)va, (uintmax_t)size, attr); + + flushtlb = 0; + + while (size > 0) { +#if defined(__amd64__) + pde = lpmap_pde(kernel_pmap, base); + if (*pde & PG_PS) { + do { + opxe = *(u_int *)pde; + npxe = opxe & ~pdefl; + npxe |= attr; + } while (npxe != opxe && ++flushtlb && + !atomic_cmpset_int((u_int *)pde, opxe, npxe)); + size -= NBPDR; + base += NBPDR; + } else { +#endif + pte = vtopte(base); + do { + opxe = *(u_int *)pte; + npxe = opxe & ~ptefl; + npxe |= attr; + } while (npxe != opxe && ++flushtlb && + !atomic_cmpset_int((u_int *)pte, opxe, npxe)); + size -= PAGE_SIZE; + base += PAGE_SIZE; +#if defined(__amd64__) + } +#endif + } + + /* XXX Gross!! */ + if (flushtlb != 0) { + lpmap_p("flushtlb=%d\n", flushtlb); + pmap_invalidate_all(kernel_pmap); + } + + return (0); +} + +#endif /* !_LPMAP_C_ */ --- sys/dev/sound/midi/sequencer.c.orig 2008-05-22 10:10:04.000000000 +0800 +++ sys/dev/sound/midi/sequencer.c 2008-06-30 10:14:34.000000000 +0800 @@ -460,7 +460,12 @@ cv_broadcast(&scp->th_cv); mtx_unlock(&scp->seq_lock); SEQ_DEBUG(2, printf("seq_eventthread finished\n")); +#if __FreeBSD_version >= 800002 kproc_exit(0); +#else + mtx_lock(&Giant); + kthread_exit(0); +#endif } /* @@ -575,7 +580,13 @@ * TODO: Add to list of sequencers this module provides */ - ret = kproc_create(seq_eventthread, scp, NULL, RFHIGHPID, 0, + ret = +#if __FreeBSD_version >= 800002 + kproc_create +#else + kthread_create +#endif + (seq_eventthread, scp, NULL, RFHIGHPID, 0, "sequencer %02d", scp->unit); if (ret) --- sys/dev/sound/null.c.orig 1970-01-01 07:30:00.000000000 +0730 +++ sys/dev/sound/null.c 2008-06-30 10:14:34.000000000 +0800 @@ -0,0 +1,610 @@ +/*- + * Copyright (c) 2007 Ariff Abdullah + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHERIN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THEPOSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include +#include "mixer_if.h" + +SND_DECLARE_FILE("$FreeBSD$"); + +#define SNDNULL_DESC "NULL Audio" + +#define SNDNULL_RATE_MIN 4000 +#define SNDNULL_RATE_MAX 192000 + +#define SNDNULL_RATE_DEFAULT 48000 +#define SNDNULL_FMT_DEFAULT (AFMT_STEREO | AFMT_S16_LE) +#define SNDNULL_FMTSTR_DEFAULT "s16le" + +#define SNDNULL_NPCHAN 1 +#define SNDNULL_NRCHAN 1 +#define SNDNULL_MAXCHAN (SNDNULL_NPCHAN + SNDNULL_NRCHAN) + +#define SNDNULL_BUFSZ_MIN 4096 +#define SNDNULL_BUFSZ_MAX 65536 +#define SNDNULL_BUFSZ_DEFAULT 4096 + +#define SNDNULL_BLKCNT_MIN 2 +#define SNDNULL_BLKCNT_MAX 512 +#define SNDNULL_BLKCNT_DEFAULT SNDNULL_BLKCNT_MIN + +#define SNDNULL_LOCK(sc) snd_mtxlock((sc)->lock) +#define SNDNULL_UNLOCK(sc) snd_mtxunlock((sc)->lock) + +struct sndnull_info; + +struct sndnull_chinfo { + struct snd_dbuf *buffer; + struct pcm_channel *channel; + struct pcmchan_caps *caps; + struct sndnull_info *parent; + uint32_t ptr, intrcnt; + int dir, active; +}; + +struct sndnull_info { + device_t dev; + struct sndnull_chinfo ch[SNDNULL_MAXCHAN]; + struct pcmchan_caps caps; + uint32_t bufsz; + uint32_t blkcnt; + uint32_t fmtlist[2]; + struct mtx *lock; + uint8_t *ringbuffer; + int chnum; + + struct callout poll_timer; + int poll_ticks, polling; +}; + +static void * +sndnull_chan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, + struct pcm_channel *c, int dir) +{ + struct sndnull_info *sc = devinfo; + struct sndnull_chinfo *ch; + + SNDNULL_LOCK(sc); + + ch = &sc->ch[sc->chnum++]; + ch->buffer = b; + ch->parent = sc; + ch->channel = c; + ch->dir = dir; + ch->caps = &sc->caps; + + SNDNULL_UNLOCK(sc); + + if (sndbuf_setup(ch->buffer, sc->ringbuffer, sc->bufsz) == -1) + return (NULL); + + return (ch); +} + +static int +sndnull_chan_setformat(kobj_t obj, void *data, uint32_t format) +{ + struct sndnull_chinfo *ch = data; + + if (ch->caps->fmtlist[0] != format) + return (-1); + + return (0); +} + +static int +sndnull_chan_setspeed(kobj_t obj, void *data, uint32_t spd) +{ + struct sndnull_chinfo *ch = data; + + if (spd < ch->caps->minspeed) + spd = ch->caps->minspeed; + if (spd > ch->caps->maxspeed) + spd = ch->caps->maxspeed; + + return (spd); +} + +static int +sndnull_chan_setfragments(kobj_t obj, void *data, + uint32_t blksz, uint32_t blkcnt) +{ + struct sndnull_chinfo *ch = data; + struct sndnull_info *sc = ch->parent; + + blkcnt = sc->blkcnt; + blksz = sndbuf_getmaxsize(ch->buffer) / blkcnt; + blksz -= blksz % sndbuf_getbps(ch->buffer); + + if ((sndbuf_getblksz(ch->buffer) != blksz || + sndbuf_getblkcnt(ch->buffer) != blkcnt) && + sndbuf_resize(ch->buffer, blkcnt, blksz) != 0) + device_printf(sc->dev, "%s: failed blksz=%u blkcnt=%u\n", + __func__, blksz, blkcnt); + + return (1); +} + +static int +sndnull_chan_setblocksize(kobj_t obj, void *data, uint32_t blksz) +{ + struct sndnull_chinfo *ch = data; + struct sndnull_info *sc = ch->parent; + + sndnull_chan_setfragments(obj, data, blksz, sc->blkcnt); + + return (sndbuf_getblksz(ch->buffer)); +} + +#define SNDNULL_CHAN_ACTIVE(ch) ((ch)->active != 0) + +static __inline int +sndnull_anychan_active(struct sndnull_info *sc) +{ + int i; + + for (i = 0; i < sc->chnum; i++) { + if (SNDNULL_CHAN_ACTIVE(&sc->ch[i])) + return (1); + } + + return (0); +} + +static void +sndnull_poll_callback(void *arg) +{ + struct sndnull_info *sc = arg; + struct sndnull_chinfo *ch; + int i; + + if (sc == NULL) + return; + + SNDNULL_LOCK(sc); + + if (!sndnull_anychan_active(sc)) { + SNDNULL_UNLOCK(sc); + return; + } + + for (i = 0; i < sc->chnum; i++) { + ch = &sc->ch[i]; + if (SNDNULL_CHAN_ACTIVE(ch)) { + ch->ptr += sndbuf_getblksz(ch->buffer); + ch->intrcnt += 1; + SNDNULL_UNLOCK(sc); + chn_intr(ch->channel); + SNDNULL_LOCK(sc); + } + } + + callout_reset(&sc->poll_timer, sc->poll_ticks, + sndnull_poll_callback, sc); + + SNDNULL_UNLOCK(sc); +} + +static int +sndnull_chan_trigger(kobj_t obj, void *data, int go) +{ + struct sndnull_chinfo *ch = data; + struct sndnull_info *sc = ch->parent; + int pollticks; + + if (!PCMTRIG_COMMON(go)) + return (0); + + SNDNULL_LOCK(sc); + + switch (go) { + case PCMTRIG_START: + if (!sndnull_anychan_active(sc)) { + pollticks = ((uint64_t)hz * + sndbuf_getblksz(ch->buffer)) / + ((uint64_t)sndbuf_getbps(ch->buffer) * + sndbuf_getspd(ch->buffer)); + if (pollticks < 1) + pollticks = 1; + sc->poll_ticks = pollticks; + callout_reset(&sc->poll_timer, 1, + sndnull_poll_callback, sc); + if (bootverbose) + device_printf(sc->dev, + "PCMTRIG_START: pollticks=%d\n", + pollticks); + } + if (ch->dir == PCMDIR_REC) + memset(sc->ringbuffer, sndbuf_zerodata( + sndbuf_getfmt(ch->buffer)), + sndbuf_getmaxsize(ch->buffer)); + ch->ptr = 0; + ch->intrcnt = 0; + ch->active = 1; + break; + case PCMTRIG_STOP: + case PCMTRIG_ABORT: + ch->active = 0; + if (!sndnull_anychan_active(sc)) + callout_stop(&sc->poll_timer); + if (ch->dir == PCMDIR_PLAY) + memset(sc->ringbuffer, sndbuf_zerodata( + sndbuf_getfmt(ch->buffer)), + sndbuf_getmaxsize(ch->buffer)); + break; + default: + break; + } + + SNDNULL_UNLOCK(sc); + + return (0); +} + +static int +sndnull_chan_getptr(kobj_t obj, void *data) +{ + struct sndnull_chinfo *ch = data; + struct sndnull_info *sc = ch->parent; + uint32_t ptr; + + SNDNULL_LOCK(sc); + ptr = (SNDNULL_CHAN_ACTIVE(ch)) ? ch->ptr : 0; + SNDNULL_UNLOCK(sc); + + return (ptr); +} + +static struct pcmchan_caps * +sndnull_chan_getcaps(kobj_t obj, void *data) +{ + return (((struct sndnull_chinfo *)data)->caps); +} + +static kobj_method_t sndnull_chan_methods[] = { + KOBJMETHOD(channel_init, sndnull_chan_init), + KOBJMETHOD(channel_setformat, sndnull_chan_setformat), + KOBJMETHOD(channel_setspeed, sndnull_chan_setspeed), + KOBJMETHOD(channel_setblocksize, sndnull_chan_setblocksize), + KOBJMETHOD(channel_setfragments, sndnull_chan_setfragments), + KOBJMETHOD(channel_trigger, sndnull_chan_trigger), + KOBJMETHOD(channel_getptr, sndnull_chan_getptr), + KOBJMETHOD(channel_getcaps, sndnull_chan_getcaps), + { 0, 0 } +}; +CHANNEL_DECLARE(sndnull_chan); + +static const struct { + int ctl; + int rec; +} sndnull_mixer_ctls[SOUND_MIXER_NRDEVICES] = { + [SOUND_MIXER_VOLUME] = { 1, 0 }, + [SOUND_MIXER_BASS] = { 1, 0 }, + [SOUND_MIXER_TREBLE] = { 1, 0 }, + [SOUND_MIXER_SYNTH] = { 1, 1 }, + [SOUND_MIXER_PCM] = { 1, 1 }, + [SOUND_MIXER_SPEAKER] = { 1, 0 }, + [SOUND_MIXER_LINE] = { 1, 1 }, + [SOUND_MIXER_MIC] = { 1, 1 }, + [SOUND_MIXER_CD] = { 1, 1 }, + [SOUND_MIXER_IMIX] = { 1, 1 }, + [SOUND_MIXER_RECLEV] = { 1, 0 }, +}; + +static int +sndnull_mixer_init(struct snd_mixer *m) +{ + uint32_t mask, recmask; + int i; + + mask = 0; + recmask = 0; + for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { + if (sndnull_mixer_ctls[i].ctl != 0) + mask |= 1 << i; + if (sndnull_mixer_ctls[i].rec != 0) + recmask |= 1 << i; + } + + mix_setdevs(m, mask); + mix_setrecdevs(m, recmask); + + return (0); +} + +static int +sndnull_mixer_set(struct snd_mixer *m, unsigned dev, + unsigned left, unsigned right) +{ + if (!(dev < SOUND_MIXER_NRDEVICES && sndnull_mixer_ctls[dev].ctl != 0)) + return (-1); + + return (left | (right << 8)); +} + +static int +sndnull_mixer_setrecsrc(struct snd_mixer *m, uint32_t src) +{ + uint32_t recsrc; + int i; + + recsrc = src; + + if (recsrc & SOUND_MASK_IMIX) + recsrc &= SOUND_MASK_IMIX; + else { + for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { + if (sndnull_mixer_ctls[i].rec == 0) + recsrc &= ~(1 << i); + } + } + + return (recsrc); +} + +static kobj_method_t sndnull_mixer_methods[] = { + KOBJMETHOD(mixer_init, sndnull_mixer_init), + KOBJMETHOD(mixer_set, sndnull_mixer_set), + KOBJMETHOD(mixer_setrecsrc, sndnull_mixer_setrecsrc), + { 0, 0 } +}; +MIXER_DECLARE(sndnull_mixer); + +static int +sysctl_sndnull_rate(SYSCTL_HANDLER_ARGS) +{ + struct sndnull_info *sc; + device_t dev; + int err, val; + + dev = oidp->oid_arg1; + + sc = pcm_getdevinfo(dev); + if (sc == NULL) + return (EINVAL); + + SNDNULL_LOCK(sc); + val = sc->caps.maxspeed; + SNDNULL_UNLOCK(sc); + + err = sysctl_handle_int(oidp, &val, 0, req); + + if (err != 0 || req->newptr == NULL) + return (err); + + if (val < SNDNULL_RATE_MIN) + val = SNDNULL_RATE_MIN; + if (val > SNDNULL_RATE_MAX) + val = SNDNULL_RATE_MAX; + + SNDNULL_LOCK(sc); + if (sndnull_anychan_active(sc)) + err = EBUSY; + else { + sc->caps.minspeed = (uint32_t)val; + sc->caps.maxspeed = sc->caps.minspeed; + } + SNDNULL_UNLOCK(sc); + + return (err); +} + +static int +sysctl_sndnull_format(SYSCTL_HANDLER_ARGS) +{ + struct sndnull_info *sc; + device_t dev; + int err; + char fmtstr[AFMTSTR_MAXSZ]; + uint32_t fmt; + + dev = oidp->oid_arg1; + + sc = pcm_getdevinfo(dev); + if (sc == NULL) + return (EINVAL); + + SNDNULL_LOCK(sc); + fmt = sc->fmtlist[0]; + if (fmt != afmt2afmtstr(NULL, fmt, fmtstr, sizeof(fmtstr), + AFMTSTR_FULL, AFMTSTR_STEREO_RETURN)) + strlcpy(fmtstr, SNDNULL_FMTSTR_DEFAULT, sizeof(fmtstr)); + SNDNULL_UNLOCK(sc); + + err = sysctl_handle_string(oidp, fmtstr, sizeof(fmtstr), req); + + if (err != 0 || req->newptr == NULL) + return (err); + + fmt = afmtstr2afmt(NULL, fmtstr, AFMTSTR_STEREO_RETURN); + if (fmt == 0) + return (EINVAL); + + SNDNULL_LOCK(sc); + if (fmt != sc->fmtlist[0]) { + if (sndnull_anychan_active(sc)) + err = EBUSY; + else + sc->fmtlist[0] = fmt; + } + SNDNULL_UNLOCK(sc); + + return (err); +} + +static device_t sndnull_dev = NULL; + +static void +sndnull_dev_identify(driver_t *driver, device_t parent) +{ + if (sndnull_dev == NULL) + sndnull_dev = BUS_ADD_CHILD(parent, 0, "pcm", -1); +} + +static int +sndnull_dev_probe(device_t dev) +{ + if (dev != NULL && dev == sndnull_dev) { + device_set_desc(dev, SNDNULL_DESC); + return (BUS_PROBE_DEFAULT); + } + + return (ENXIO); +} + +static int +sndnull_dev_attach(device_t dev) +{ + struct sndnull_info *sc; + char status[SND_STATUSLEN]; + int i; + + sc = malloc(sizeof(*sc), M_DEVBUF, M_WAITOK | M_ZERO); + + sc->lock = snd_mtxcreate(device_get_nameunit(dev), "snd_null softc"); + sc->dev = dev; + + callout_init(&sc->poll_timer, CALLOUT_MPSAFE); + sc->poll_ticks = 1; + + sc->caps.minspeed = SNDNULL_RATE_DEFAULT; + sc->caps.maxspeed = SNDNULL_RATE_DEFAULT; + sc->fmtlist[0] = SNDNULL_FMT_DEFAULT; + sc->fmtlist[1] = 0; + sc->caps.fmtlist = sc->fmtlist; + + sc->bufsz = pcm_getbuffersize(dev, SNDNULL_BUFSZ_MIN, + SNDNULL_BUFSZ_DEFAULT, SNDNULL_BUFSZ_MAX); + sc->blkcnt = SNDNULL_BLKCNT_DEFAULT; + + sc->ringbuffer = malloc(sc->bufsz, M_DEVBUF, M_WAITOK | M_ZERO); + + if (mixer_init(dev, &sndnull_mixer_class, sc) != 0) + device_printf(dev, "mixer_init() failed\n"); + + if (pcm_register(dev, sc, SNDNULL_NPCHAN, SNDNULL_NRCHAN)) + return (ENXIO); + + for (i = 0; i < SNDNULL_NPCHAN; i++) + pcm_addchan(dev, PCMDIR_PLAY, &sndnull_chan_class, sc); + for (i = 0; i < SNDNULL_NRCHAN; i++) + pcm_addchan(dev, PCMDIR_REC, &sndnull_chan_class, sc); + + snprintf(status, SND_STATUSLEN, "at %s %s", + device_get_nameunit(device_get_parent(dev)), + PCM_KLDSTRING(snd_null)); + + pcm_setflags(dev, pcm_getflags(dev) | SD_F_MPSAFE); + pcm_setstatus(dev, status); + + SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), + SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, + "rate", CTLTYPE_INT | CTLFLAG_RW, dev, sizeof(dev), + sysctl_sndnull_rate, "I", "runtime rate"); + SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), + SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, + "format", CTLTYPE_STRING | CTLFLAG_RW, dev, sizeof(dev), + sysctl_sndnull_format, "A", "runtime format"); + + return (0); +} + +static void +sndnull_release_resources(struct sndnull_info *sc) +{ + if (sc == NULL) + return; + if (sc->chnum != 0) { + SNDNULL_LOCK(sc); + callout_stop(&sc->poll_timer); + SNDNULL_UNLOCK(sc); + callout_drain(&sc->poll_timer); + } + if (sc->ringbuffer != NULL) { + free(sc->ringbuffer, M_DEVBUF); + sc->ringbuffer = NULL; + } + if (sc->lock != NULL) { + snd_mtxfree(sc->lock); + sc->lock = NULL; + } + free(sc, M_DEVBUF); +} + +static int +sndnull_dev_detach(device_t dev) +{ + struct sndnull_info *sc; + int err; + + sc = pcm_getdevinfo(dev); + if (sc != NULL) { + err = pcm_unregister(dev); + if (err != 0) + return (err); + sndnull_release_resources(sc); + } + + return (0); +} + +static device_method_t sndnull_methods[] = { + DEVMETHOD(device_identify, sndnull_dev_identify), + DEVMETHOD(device_probe, sndnull_dev_probe), + DEVMETHOD(device_attach, sndnull_dev_attach), + DEVMETHOD(device_detach, sndnull_dev_detach), + { 0, 0 } +}; + +static driver_t sndnull_driver = { + "pcm", + sndnull_methods, + PCM_SOFTC_SIZE, +}; + +static int +sndnull_modevent(module_t mod, int type, void *data) +{ + switch (type) { + case MOD_UNLOAD: + if (sndnull_dev != NULL) + device_delete_child(device_get_parent(sndnull_dev), + sndnull_dev); + sndnull_dev = NULL; + case MOD_LOAD: + return (0); + break; + default: + break; + } + + return (EOPNOTSUPP); +} + +DRIVER_MODULE(snd_null, nexus, sndnull_driver, pcm_devclass, sndnull_modevent, + 0); +MODULE_DEPEND(snd_null, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER); +MODULE_VERSION(snd_null, 1); --- sys/dev/sound/pci/emu10kx.c.orig 2007-10-12 14:03:44.000000000 +0800 +++ sys/dev/sound/pci/emu10kx.c 2008-06-30 10:14:34.000000000 +0800 @@ -3193,7 +3193,11 @@ i = 0; sc->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &i, RF_ACTIVE | RF_SHAREABLE); - if ((sc->irq == NULL) || bus_setup_intr(dev, sc->irq, INTR_MPSAFE | INTR_TYPE_AV, NULL, emu_intr, sc, &sc->ih)) { + if ((sc->irq == NULL) || bus_setup_intr(dev, sc->irq, INTR_MPSAFE | INTR_TYPE_AV, +#if __FreeBSD_version >= 700031 + NULL, +#endif + emu_intr, sc, &sc->ih)) { device_printf(dev, "unable to map interrupt\n"); goto bad; } --- sys/dev/sound/pci/hda/hdac.c.orig 2008-05-22 19:46:51.000000000 +0800 +++ sys/dev/sound/pci/hda/hdac.c 2008-06-30 10:14:34.000000000 +0800 @@ -102,12 +102,33 @@ #define hdac_lockassert(sc) snd_mtxassert((sc)->lock) #define hdac_lockowned(sc) mtx_owned((sc)->lock) +#if defined(__i386__) || defined(__amd64__) +#if __FreeBSD_version < 602110 || defined(SND_USE_LPMAP) +#include +#endif +#endif + #undef HDAC_MSI_ENABLED #if __FreeBSD_version >= 700026 || \ - (__FreeBSD_version < 700000 && __FreeBSD_version >= 602106) + (__FreeBSD_version < 700000 && __FreeBSD_version >= 602106 && \ + !defined(_LPMAP_C_)) #define HDAC_MSI_ENABLED 1 #endif +#ifdef _LPMAP_C_ +#define HDAC_BUS_DMA_NOCACHE 0 +#define HDAC_DMA_ATTR(sc, v, s, attr) do { \ + vm_offset_t va = (vm_offset_t)(v); \ + vm_size_t sz = (vm_size_t)(s); \ + if ((sc) != NULL && ((sc)->flags & HDAC_F_DMA_NOCACHE) && \ + va != 0 && sz != 0) \ + (void)lpmap_change_attr(va, sz, (attr)); \ +} while(0) +#else +#define HDAC_BUS_DMA_NOCACHE BUS_DMA_NOCACHE +#define HDAC_DMA_ATTR(...) +#endif + #define HDA_FLAG_MATCH(fl, v) (((fl) & (v)) == (v)) #define HDA_DEV_MATCH(fl, v) ((fl) == (v) || \ (fl) == 0xffffffff || \ @@ -195,6 +216,7 @@ #define DELL_VENDORID 0x1028 #define DELL_D630_SUBVENDOR HDA_MODEL_CONSTRUCT(DELL, 0x01f9) #define DELL_D820_SUBVENDOR HDA_MODEL_CONSTRUCT(DELL, 0x01cc) +#define DELL_D830_SUBVENDOR HDA_MODEL_CONSTRUCT(DELL, 0x01fe) #define DELL_V1500_SUBVENDOR HDA_MODEL_CONSTRUCT(DELL, 0x0228) #define DELL_I1300_SUBVENDOR HDA_MODEL_CONSTRUCT(DELL, 0x01c9) #define DELL_XPSM1210_SUBVENDOR HDA_MODEL_CONSTRUCT(DELL, 0x01d7) @@ -691,6 +713,8 @@ 0, 0, -1, 6, { 5, 7, -1 }, -1 }, { DELL_D630_SUBVENDOR, HDA_CODEC_STAC9205, HDAC_HP_SWITCH_CTRL, 0, 0, -1, 10, { 13, -1 }, -1 }, + { DELL_D830_SUBVENDOR, HDA_CODEC_STAC9205, HDAC_HP_SWITCH_CTRL, + 0, 0, -1, 10, { 13, -1 }, -1 }, { DELL_V1500_SUBVENDOR, HDA_CODEC_STAC9205, HDAC_HP_SWITCH_CTRL, 0, 0, -1, 10, { 13, -1 }, -1 }, { APPLE_MB3_SUBVENDOR, HDA_CODEC_ALC885, HDAC_HP_SWITCH_CTL, @@ -1454,7 +1478,7 @@ */ result = bus_dmamem_alloc(dma->dma_tag, (void **)&dma->dma_vaddr, BUS_DMA_NOWAIT | BUS_DMA_ZERO | - ((sc->flags & HDAC_F_DMA_NOCACHE) ? BUS_DMA_NOCACHE : 0), + ((sc->flags & HDAC_F_DMA_NOCACHE) ? HDAC_BUS_DMA_NOCACHE : 0), &dma->dma_map); if (result != 0) { device_printf(sc->dev, "%s: bus_dmamem_alloc failed (%x)\n", @@ -1462,6 +1486,7 @@ goto hdac_dma_alloc_fail; } + HDAC_DMA_ATTR(sc, dma->dma_vaddr, roundsz, PAT_UNCACHEABLE); dma->dma_size = roundsz; /* @@ -1509,6 +1534,8 @@ bus_dmamap_unload(dma->dma_tag, dma->dma_map); } if (dma->dma_vaddr != NULL) { + HDAC_DMA_ATTR(sc, dma->dma_vaddr, dma->dma_size, + PAT_WRITE_BACK); bus_dmamem_free(dma->dma_tag, dma->dma_vaddr, dma->dma_map); dma->dma_vaddr = NULL; } @@ -3104,13 +3131,35 @@ } if (sndbuf_alloc(ch->b, sc->chan_dmat, - (sc->flags & HDAC_F_DMA_NOCACHE) ? BUS_DMA_NOCACHE : 0, + (sc->flags & HDAC_F_DMA_NOCACHE) ? HDAC_BUS_DMA_NOCACHE : 0, sc->chan_size) != 0) return (NULL); + HDAC_DMA_ATTR(sc, sndbuf_getbuf(ch->b), sndbuf_getmaxsize(ch->b), + PAT_UNCACHEABLE); + return (ch); } +#ifdef _LPMAP_C_ +static int +hdac_channel_free(kobj_t obj, void *data) +{ + struct hdac_softc *sc; + struct hdac_chan *ch; + + ch = (struct hdac_chan *)data; + sc = (ch != NULL && ch->devinfo != NULL && ch->devinfo->codec != NULL) ? + ch->devinfo->codec->sc : NULL; + if (ch != NULL && sc != NULL) { + HDAC_DMA_ATTR(sc, sndbuf_getbuf(ch->b), + sndbuf_getmaxsize(ch->b), PAT_WRITE_BACK); + } + + return (1); +} +#endif + static int hdac_channel_setformat(kobj_t obj, void *data, uint32_t format) { @@ -3351,6 +3400,9 @@ static kobj_method_t hdac_channel_methods[] = { KOBJMETHOD(channel_init, hdac_channel_init), +#ifdef _LPMAP_C_ + KOBJMETHOD(channel_free, hdac_channel_free), +#endif KOBJMETHOD(channel_setformat, hdac_channel_setformat), KOBJMETHOD(channel_setspeed, hdac_channel_setspeed), KOBJMETHOD(channel_setblocksize, hdac_channel_setblocksize), @@ -4260,6 +4312,8 @@ HDA_QUIRK_GPIO0 | HDA_QUIRK_GPIO1, 0 }, { DELL_D630_SUBVENDOR, HDA_CODEC_STAC9205, HDA_QUIRK_GPIO0, 0 }, + { DELL_D830_SUBVENDOR, HDA_CODEC_STAC9205, + HDA_QUIRK_GPIO1, 0 }, { DELL_V1500_SUBVENDOR, HDA_CODEC_STAC9205, HDA_QUIRK_GPIO0, 0 }, { HDA_MATCH_ALL, HDA_CODEC_AD1988, @@ -4374,6 +4428,31 @@ HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_SHIFT; strlcpy(w->name, "beep widget", sizeof(w->name)); } +#if 1 + ctl = hdac_audio_ctl_amp_get(devinfo, 16, 2, 1); + if (ctl != NULL) + ctl->muted = HDA_AMP_MUTE_ALL; + ctl = hdac_audio_ctl_amp_get(devinfo, 3, 0, 1); + if (ctl != NULL) { + struct hdac_softc *sc = devinfo->codec->sc; + hdac_unlock(sc); + if (resource_int_value( + device_get_name(sc->dev), + device_get_unit(sc->dev), "speaker_gain", + &i) == 0) { + if (i > 100) + i = 100; + else if (i < 1) + i = 1; + } else + i = 100; + hdac_lock(sc); + ctl->step = (i * ctl->step) / 100; + ctl->offset = ctl->step; + ctl->left = ctl->step; + ctl->right = ctl->step; + } +#endif } break; case HDA_CODEC_ALC861: --- sys/dev/sound/pci/ich.c.orig 2007-12-13 02:21:05.000000000 +0800 +++ sys/dev/sound/pci/ich.c 2008-06-30 10:14:34.000000000 +0800 @@ -92,6 +92,26 @@ #define ICH_DMA_NOCACHE (1 << 4) #define ICH_HIGH_LATENCY (1 << 5) +#if defined(__i386__) || defined(__amd64__) +#if __FreeBSD_version < 602110 || defined(SND_USE_LPMAP) +#include +#endif +#endif + +#ifdef _LPMAP_C_ +#define ICH_BUS_DMA_NOCACHE 0 +#define ICH_DMA_ATTR(sc, v, s, attr) do { \ + vm_offset_t va = (vm_offset_t)(v); \ + vm_size_t sz = (vm_size_t)(s); \ + if ((sc) != NULL && ((sc)->flags & ICH_DMA_NOCACHE) && \ + va != 0 && sz != 0) \ + (void)lpmap_change_attr(va, sz, (attr)); \ +} while(0) +#else +#define ICH_BUS_DMA_NOCACHE BUS_DMA_NOCACHE +#define ICH_DMA_ATTR(...) +#endif + static const struct ich_type { uint16_t vendor; uint16_t devid; @@ -412,10 +432,13 @@ ICH_UNLOCK(sc); if (sndbuf_alloc(ch->buffer, sc->chan_dmat, - ((sc->flags & ICH_DMA_NOCACHE) ? BUS_DMA_NOCACHE : 0), + ((sc->flags & ICH_DMA_NOCACHE) ? ICH_BUS_DMA_NOCACHE : 0), sc->bufsz) != 0) return (NULL); + ICH_DMA_ATTR(sc, sndbuf_getbuf(ch->buffer), + sndbuf_getmaxsize(ch->buffer), PAT_UNCACHEABLE); + ICH_LOCK(sc); ich_wr(sc, ch->regbase + ICH_REG_X_BDBAR, (uint32_t)(ch->desc_addr), 4); ICH_UNLOCK(sc); @@ -423,6 +446,24 @@ return (ch); } +#ifdef _LPMAP_C_ +static int +ichchan_free(kobj_t obj, void *data) +{ + struct sc_chinfo *ch; + struct sc_info *sc; + + ch = (struct sc_chinfo *)data; + sc = (ch != NULL) ? ch->parent : NULL; + if (ch != NULL && sc != NULL) { + ICH_DMA_ATTR(sc, sndbuf_getbuf(ch->buffer), + sndbuf_getmaxsize(ch->buffer), PAT_WRITE_BACK); + } + + return (1); +} +#endif + static int ichchan_setformat(kobj_t obj, void *data, uint32_t format) { @@ -577,6 +618,9 @@ static kobj_method_t ichchan_methods[] = { KOBJMETHOD(channel_init, ichchan_init), +#ifdef _LPMAP_C_ + KOBJMETHOD(channel_free, ichchan_free), +#endif KOBJMETHOD(channel_setformat, ichchan_setformat), KOBJMETHOD(channel_setspeed, ichchan_setspeed), KOBJMETHOD(channel_setblocksize, ichchan_setblocksize), @@ -1054,10 +1098,12 @@ } if (bus_dmamem_alloc(sc->dmat, (void **)&sc->dtbl, BUS_DMA_NOWAIT | - ((sc->flags & ICH_DMA_NOCACHE) ? BUS_DMA_NOCACHE : 0), + ((sc->flags & ICH_DMA_NOCACHE) ? ICH_BUS_DMA_NOCACHE : 0), &sc->dtmap)) goto bad; + ICH_DMA_ATTR(sc, sc->dtbl, sc->dtbl_size, PAT_UNCACHEABLE); + if (bus_dmamap_load(sc->dmat, sc->dtmap, sc->dtbl, sc->dtbl_size, ich_setmap, sc, 0)) goto bad; @@ -1102,8 +1148,10 @@ sc->nabmbarid, sc->nabmbar); if (sc->dtmap) bus_dmamap_unload(sc->dmat, sc->dtmap); - if (sc->dtbl) + if (sc->dtbl) { + ICH_DMA_ATTR(sc, sc->dtbl, sc->dtbl_size, PAT_WRITE_BACK); bus_dmamem_free(sc->dmat, sc->dtbl, sc->dtmap); + } if (sc->chan_dmat) bus_dma_tag_destroy(sc->chan_dmat); if (sc->dmat) @@ -1130,6 +1178,7 @@ bus_release_resource(dev, sc->regtype, sc->nambarid, sc->nambar); bus_release_resource(dev, sc->regtype, sc->nabmbarid, sc->nabmbar); bus_dmamap_unload(sc->dmat, sc->dtmap); + ICH_DMA_ATTR(sc, sc->dtbl, sc->dtbl_size, PAT_WRITE_BACK); bus_dmamem_free(sc->dmat, sc->dtbl, sc->dtmap); bus_dma_tag_destroy(sc->chan_dmat); bus_dma_tag_destroy(sc->dmat); --- sys/dev/sound/pcm/ac97.c.orig 2007-10-27 04:49:59.000000000 +0800 +++ sys/dev/sound/pcm/ac97.c 2008-06-30 10:14:34.000000000 +0800 @@ -822,7 +822,7 @@ ac97_create(device_t dev, void *devinfo, kobj_class_t cls) { struct ac97_info *codec; - int eapdinv; + int i; codec = malloc(sizeof(*codec), M_AC97, M_WAITOK | M_ZERO); snprintf(codec->name, sizeof(codec->name), "%s:ac97", @@ -832,11 +832,15 @@ codec->dev = dev; codec->devinfo = devinfo; codec->flags = 0; + if (resource_int_value(device_get_name(dev), device_get_unit(dev), - "eapdinv", &eapdinv) == 0) { - if (eapdinv != 0) - codec->flags |= AC97_F_EAPD_INV; - } + "eapdinv", &i) == 0 && i != 0) + codec->flags |= AC97_F_EAPD_INV; + + if (resource_int_value(device_get_name(dev), device_get_unit(dev), + "softpcmvol", &i) == 0 && i != 0) + pcm_setflags(dev, pcm_getflags(dev) | SD_F_SOFTPCMVOL); + return codec; } @@ -984,7 +988,6 @@ case 0x434d4978: /* CMI9761 */ case 0x434d4982: /* CMI9761 */ case 0x434d4983: /* CMI9761 */ - ac97_wrcd(codec, AC97_MIX_PCM, 0); bzero(&codec->mix[SOUND_MIXER_PCM], sizeof(codec->mix[SOUND_MIXER_PCM])); pcm_setflags(codec->dev, pcm_getflags(codec->dev) | @@ -995,6 +998,8 @@ break; } + if (pcm_getflags(codec->dev) & SD_F_SOFTPCMVOL) + ac97_wrcd(codec, AC97_MIX_PCM, 0); #if 0 /* XXX For the sake of debugging purposes */ mix_setparentchild(m, SOUND_MIXER_VOLUME, --- sys/dev/sound/pcm/buffer.c.orig 2007-06-16 11:37:27.000000000 +0800 +++ sys/dev/sound/pcm/buffer.c 2008-06-30 10:14:34.000000000 +0800 @@ -28,6 +28,9 @@ #include "feeder_if.h" +#define SND_USE_FXDIV +#include "snd_fxdiv_gen.h" + SND_DECLARE_FILE("$FreeBSD: src/sys/dev/sound/pcm/buffer.c,v 1.37 2007/06/16 03:37:27 ariff Exp $"); struct snd_dbuf * @@ -670,26 +673,55 @@ return 0; } +#ifdef SND_DIAGNOSTIC +static uint32_t snd_feeder_maxfeed = 0; +SYSCTL_INT(_hw_snd, OID_AUTO, feeder_maxfeed, CTLFLAG_RD, + &snd_feeder_maxfeed, 0, "maximum feeder count request"); + +static uint32_t snd_feeder_maxcycle = 0; +SYSCTL_INT(_hw_snd, OID_AUTO, feeder_maxcycle, CTLFLAG_RD, + &snd_feeder_maxcycle, 0, "maximum feeder cycle"); +#endif + /* count is number of bytes we want added to destination buffer */ int sndbuf_feed(struct snd_dbuf *from, struct snd_dbuf *to, struct pcm_channel *channel, struct pcm_feeder *feeder, unsigned int count) { - unsigned int cnt; + unsigned int cnt, maxfeed; +#ifdef SND_DIAGNOSTIC + unsigned int cycle; + + if (count > snd_feeder_maxfeed) + snd_feeder_maxfeed = count; + + cycle = 0; +#endif KASSERT(count > 0, ("can't feed 0 bytes")); if (sndbuf_getfree(to) < count) - return EINVAL; + return (EINVAL); + + maxfeed = SND_FXROUND(SND_FXDIV_MAX, sndbuf_getbps(to)); do { - cnt = FEEDER_FEED(feeder, channel, to->tmpbuf, count, from); - if (cnt) { - sndbuf_acquire(to, to->tmpbuf, cnt); - count -= cnt; - } - } while (count && cnt); + cnt = FEEDER_FEED(feeder, channel, to->tmpbuf, + min(count, maxfeed), from); + if (cnt == 0) + break; + sndbuf_acquire(to, to->tmpbuf, cnt); + count -= cnt; +#ifdef SND_DIAGNOSTIC + cycle++; +#endif + } while (count != 0); - return 0; +#ifdef SND_DIAGNOSTIC + if (cycle > snd_feeder_maxcycle) + snd_feeder_maxcycle = cycle; +#endif + + return (0); } /************************************************************/ --- sys/dev/sound/pcm/channel.c.orig 2007-12-03 22:26:56.000000000 +0800 +++ sys/dev/sound/pcm/channel.c 2008-06-30 10:14:34.000000000 +0800 @@ -107,6 +107,80 @@ "interrupt timeout (1 - 10) seconds"); #endif +static int chn_vpc_autoreset = 1; +TUNABLE_INT("hw.snd.vpc_autoreset", &chn_vpc_autoreset); +SYSCTL_INT(_hw_snd, OID_AUTO, vpc_autoreset, CTLFLAG_RW, + &chn_vpc_autoreset, 0, "automatically reset channels volume to 0db"); + +static int chn_vol_0db_pcm = SND_VOL_0DB_PCM; + +static void +chn_vpc_proc(int reset, int db) +{ + struct snddev_info *d; + struct pcm_channel *c; + int i; + + for (i = 0; pcm_devclass != NULL && + i < devclass_get_maxunit(pcm_devclass); i++) { + d = devclass_get_softc(pcm_devclass, i); + if (!PCM_REGISTERED(d)) + continue; + pcm_lock(d); + PCM_WAIT(d); + PCM_ACQUIRE(d); + CHN_FOREACH(c, d, channels.pcm) { + CHN_LOCK(c); + CHN_SETVOLUME(c, SND_VOL_C_PCM, SND_CHN_T_VOL_0DB, db); + if (reset != 0) + chn_vpc_reset(c, SND_VOL_C_PCM, 1); + CHN_UNLOCK(c); + } + PCM_RELEASE(d); + pcm_unlock(d); + } +} + +static int +sysctl_hw_snd_vpc_0db(SYSCTL_HANDLER_ARGS) +{ + int err, val; + + val = chn_vol_0db_pcm; + err = sysctl_handle_int(oidp, &val, 0, req); + if (err != 0 || req->newptr == NULL) + return (err); + if (val < SND_VOL_0DB_MIN || val > SND_VOL_0DB_MAX) + return (EINVAL); + + chn_vol_0db_pcm = val; + chn_vpc_proc(0, val); + + return (0); +} +SYSCTL_PROC(_hw_snd, OID_AUTO, vpc_0db, CTLTYPE_INT | CTLFLAG_RW, + 0, sizeof(int), sysctl_hw_snd_vpc_0db, "I", + "0db relative level"); + +static int +sysctl_hw_snd_vpc_reset(SYSCTL_HANDLER_ARGS) +{ + int err, val; + + val = 0; + err = sysctl_handle_int(oidp, &val, 0, req); + if (err != 0 || req->newptr == NULL || val == 0) + return (err); + + chn_vol_0db_pcm = SND_VOL_0DB_PCM; + chn_vpc_proc(1, SND_VOL_0DB_PCM); + + return (0); +} +SYSCTL_PROC(_hw_snd, OID_AUTO, vpc_reset, CTLTYPE_INT | CTLFLAG_RW, + 0, sizeof(int), sysctl_hw_snd_vpc_reset, "I", + "reset volume on all channels"); + static int chn_usefrags = 0; TUNABLE_INT("hw.snd.usefrags", &chn_usefrags); static int chn_syncdelay = -1; @@ -137,8 +211,6 @@ */ struct pcm_synclist snd_pcm_syncgroups = SLIST_HEAD_INITIALIZER(head); -static int chn_buildfeeder(struct pcm_channel *c); - static void chn_lockinit(struct pcm_channel *c, int dir) { @@ -621,6 +693,22 @@ } if (i >= j) { + if ((c->flags & CHN_F_VIRTUAL) && c->parentchannel != NULL && + (sndbuf_getfmt(c->bufhard) != c->parentchannel->format || + sndbuf_getspd(c->bufhard) != c->parentchannel->speed)) { + if (snd_verbose > 3) + printf("%s: vchan out of sync: " + "%u vs %u / 0x%08x vs 0x%08x\n", c->name, + sndbuf_getspd(c->bufhard), + c->parentchannel->speed, + sndbuf_getfmt(c->bufhard), + c->parentchannel->format); + sndbuf_setfmt(c->bufhard, c->parentchannel->format); + sndbuf_setspd(c->bufhard, c->parentchannel->speed); + err = feeder_build_chain(c); + if (err != 0) + return (err); + } c->flags |= CHN_F_TRIGGERED; sndbuf_setrun(b, 1); c->feedcount = (c->flags & CHN_F_CLOSING) ? 2 : 0; @@ -1009,9 +1097,9 @@ } int -chn_reset(struct pcm_channel *c, u_int32_t fmt) +chn_reset(struct pcm_channel *c, uint32_t fmt, uint32_t spd) { - int hwspd, r; + int r; CHN_LOCKASSERT(c); c->feedcount = 0; @@ -1020,27 +1108,14 @@ c->timeout = 1; c->xruns = 0; - r = CHANNEL_RESET(c->methods, c->devinfo); - if (fmt != 0) { -#if 0 - hwspd = DSP_DEFAULT_SPEED; - /* only do this on a record channel until feederbuilder works */ - if (c->direction == PCMDIR_REC) - RANGE(hwspd, chn_getcaps(c)->minspeed, chn_getcaps(c)->maxspeed); - c->speed = hwspd; -#endif - hwspd = chn_getcaps(c)->minspeed; - c->speed = hwspd; + c->flags |= (pcm_getflags(c->dev) & SD_F_BITPERFECT) ? + CHN_F_BITPERFECT : 0; - if (r == 0) - r = chn_setformat(c, fmt); - if (r == 0) - r = chn_setspeed(c, hwspd); -#if 0 - if (r == 0) - r = chn_setvolume(c, 100, 100); -#endif - } + r = CHANNEL_RESET(c->methods, c->devinfo); + if (r == 0 && fmt != 0) + r = chn_setformat(c, fmt); + if (r == 0 && spd != 0) + r = chn_setspeed(c, spd); if (r == 0) r = chn_setlatency(c, chn_latency); if (r == 0) { @@ -1102,6 +1177,19 @@ c->feederflags = 0; c->sm = NULL; + /* Only Front Left/Right, for now. */ + c->matrix[0] = SND_CHN_T_FL; + c->matrix[1] = SND_CHN_T_FR; + c->matrix[2] = SND_CHN_T_MAX; + + c->volume[SND_VOL_C_MASTER][SND_CHN_T_VOL_0DB] = SND_VOL_0DB_MASTER; + c->volume[SND_VOL_C_PCM][SND_CHN_T_VOL_0DB] = chn_vol_0db_pcm; + + c->volume[SND_VOL_C_MASTER][SND_CHN_T_FL] = SND_VOL_0DB_MASTER; + c->volume[SND_VOL_C_MASTER][SND_CHN_T_FR] = SND_VOL_0DB_MASTER; + + chn_vpc_reset(c, SND_VOL_C_PCM, 1); + ret = ENODEV; CHN_UNLOCK(c); /* XXX - Unlock for CHANNEL_INIT() malloc() call */ c->devinfo = CHANNEL_INIT(c->methods, devinfo, b, c, direction); @@ -1117,13 +1205,13 @@ if (ret) goto out; - ret = sndbuf_setfmt(b, AFMT_U8); - if (ret) - goto out; + c->format = AFMT_U8; + c->speed = DSP_DEFAULT_SPEED; - ret = sndbuf_setfmt(bs, AFMT_U8); - if (ret) - goto out; + sndbuf_setfmt(b, c->format); + sndbuf_setspd(b, c->speed); + sndbuf_setfmt(bs, c->format); + sndbuf_setspd(bs, c->speed); /** * @todo Should this be moved somewhere else? The primary buffer @@ -1202,21 +1290,94 @@ return r; } +/* XXX Obsolete. Use *_matrix() variant instead. */ int chn_setvolume(struct pcm_channel *c, int left, int right) { + int ret; + + ret = chn_setvolume_matrix(c, SND_VOL_C_MASTER, SND_CHN_T_FL, left); + ret |= chn_setvolume_matrix(c, SND_VOL_C_MASTER, SND_CHN_T_FR, + right) << 8; + + return (ret); +} + +int +chn_setvolume_matrix(struct pcm_channel *c, snd_volume_class_t vc, + snd_channel_t vt, int val) +{ + int i; + + KASSERT(c != NULL && vc >= SND_VOL_C_MASTER && vc < SND_VOL_C_MAX && + (vc == SND_VOL_C_MASTER || (vc & 1)) && + (vt == SND_CHN_T_VOL_0DB || (vt >= SND_CHN_T_BEGIN && + vt <= SND_CHN_T_END)) && (vt != SND_CHN_T_VOL_0DB || + (val >= SND_VOL_0DB_MIN && val <= SND_VOL_0DB_MAX)), + ("%s(): invalid volume matrix c=%p vc=%d vt=%d val=%d", + __func__, c, vc, vt, val)); CHN_LOCKASSERT(c); - /* should add a feeder for volume changing if channel returns -1 */ - if (left > 100) - left = 100; - if (left < 0) - left = 0; - if (right > 100) - right = 100; - if (right < 0) - right = 0; - c->volume = left | (right << 8); - return 0; + + if (val < 0) + val = 0; + if (val > 100) + val = 100; + + c->volume[vc][vt] = val; + + /* + * Do relative calculation here and store it into class + 1 + * to ease the job of feeder_volume. + */ + if (vc == SND_VOL_C_MASTER) { + for (vc = SND_VOL_C_BEGIN; vc <= SND_VOL_C_END; + vc += SND_VOL_C_STEP) + c->volume[SND_VOL_C_VAL(vc)][vt] = + SND_VOL_CALC_VAL(c->volume, vc, vt); + } else if (vc & 1) { + if (vt == SND_CHN_T_VOL_0DB) { + for (i = 0; c->matrix[i] != SND_CHN_T_MAX; i++) { + vt = c->matrix[i]; + c->volume[SND_VOL_C_VAL(vc)][vt] = + SND_VOL_CALC_VAL(c->volume, vc, vt); + } + } else + c->volume[SND_VOL_C_VAL(vc)][vt] = + SND_VOL_CALC_VAL(c->volume, vc, vt); + } + + return (val); +} + +int +chn_getvolume_matrix(struct pcm_channel *c, snd_volume_class_t vc, + snd_channel_t vt) +{ + KASSERT(c != NULL && vc >= SND_VOL_C_MASTER && vc < SND_VOL_C_MAX && + (vt == SND_CHN_T_VOL_0DB || + (vt >= SND_CHN_T_BEGIN && vt <= SND_CHN_T_END)), + ("%s(): invalid volume matrix c=%p vc=%d vt=%d", + __func__, c, vc, vt)); + CHN_LOCKASSERT(c); + + return (c->volume[vc][vt]); +} + +void +chn_vpc_reset(struct pcm_channel *c, snd_volume_class_t vc, int force) +{ + int i; + + KASSERT(c != NULL && vc >= SND_VOL_C_BEGIN && vc <= SND_VOL_C_END, + ("%s(): invalid reset c=%p vc=%d", __func__, c, vc)); + CHN_LOCKASSERT(c); + + if (force == 0 && chn_vpc_autoreset == 0) + return; + + for (i = 0; c->matrix[i] != SND_CHN_T_MAX; i++) + CHN_SETVOLUME(c, vc, c->matrix[i], + c->volume[vc][SND_CHN_T_VOL_0DB]); } static u_int32_t @@ -1633,7 +1794,7 @@ else sndbuf_setspd(bs, sndbuf_getspd(b)); - r = chn_buildfeeder(c); + r = feeder_build_chain(c); DEB(printf("r = %d\n", r)); if (r) goto out; @@ -1696,7 +1857,7 @@ if (CHN_STOPPED(c)) { DEB(printf("want format %d\n", fmt)); c->format = fmt; - r = chn_buildfeeder(c); + r = feeder_build_chain(c); if (r == 0) { sndbuf_setfmt(bs, c->format); chn_resetbuf(c); @@ -1725,6 +1886,102 @@ return r; } +void +chn_syncstate(struct pcm_channel *c) +{ + struct snddev_info *d; + struct snd_mixer *m; + + d = (c != NULL) ? c->parentsnddev : NULL; + m = (d != NULL && d->mixer_dev != NULL) ? d->mixer_dev->si_drv1 : + NULL; + + if (d == NULL || m == NULL) + return; + + CHN_LOCKASSERT(c); + + if (c->feederflags & (1 << FEEDER_VOLUME)) { + uint32_t parent; + int vol, pvol, left, right; + + CHN_UNLOCK(c); + vol = mix_get(m, SOUND_MIXER_PCM); + parent = mix_getparent(m, SOUND_MIXER_PCM); + if (parent != SOUND_MIXER_NONE) + pvol = mix_get(m, parent); + else + pvol = 100 | (100 << 8); + CHN_LOCK(c); + + if (vol == -1) { + device_printf(c->dev, + "Soft PCM Volume: Failed to read pcm " + "default value\n"); + vol = 100 | (100 << 8); + } + + if (pvol == -1) { + device_printf(c->dev, + "Soft PCM Volume: Failed to read parent " + "default value\n"); + pvol = 100 | (100 << 8); + } + + left = ((vol & 0x7f) * (pvol & 0x7f)) / 100; + right = (((vol >> 8) & 0x7f) * ((pvol >> 8) & 0x7f)) / 100; + + CHN_SETVOLUME(c, SND_VOL_C_MASTER, SND_CHN_T_FL, left); + CHN_SETVOLUME(c, SND_VOL_C_MASTER, SND_CHN_T_FR, right); + } + + if (c->feederflags & (1 << FEEDER_EQ)) { + struct pcm_feeder *f; + int treble, bass, state; + + CHN_UNLOCK(c); + treble = mix_get(m, SOUND_MIXER_TREBLE); + bass = mix_get(m, SOUND_MIXER_BASS); + CHN_LOCK(c); + + if (treble == -1) + treble = 50; + else + treble = ((treble & 0x7f) + + ((treble >> 8) & 0x7f)) >> 1; + + if (bass == -1) + bass = 50; + else + bass = ((bass & 0x7f) + ((bass >> 8) & 0x7f)) >> 1; + + f = chn_findfeeder(c, FEEDER_EQ); + if (f != NULL) { + if (FEEDER_SET(f, FEEDEQ_TREBLE, treble) != 0) + device_printf(c->dev, + "EQ: Failed to set treble -- %d\n", + treble); + if (FEEDER_SET(f, FEEDEQ_BASS, bass) != 0) + device_printf(c->dev, + "EQ: Failed to set bass -- %d\n", + bass); + if (FEEDER_SET(f, FEEDEQ_PREAMP, d->eqpreamp) != 0) + device_printf(c->dev, + "EQ: Failed to set preamp -- %d\n", + d->eqpreamp); + if (d->flags & SD_F_EQ_BYPASSED) + state = FEEDEQ_BYPASS; + else if (d->flags & SD_F_EQ_ENABLED) + state = FEEDEQ_ENABLE; + else + state = FEEDEQ_DISABLE; + if (FEEDER_SET(f, FEEDEQ_STATE, state) != 0) + device_printf(c->dev, + "EQ: Failed to set state -- %d\n", state); + } + } +} + int chn_trigger(struct pcm_channel *c, int go) { @@ -1763,6 +2020,7 @@ CHN_INSERT_HEAD(d, c, channels.pcm.busy); pcm_unlock(d); CHN_LOCK(c); + chn_syncstate(c); } break; case PCMTRIG_STOP: @@ -1828,7 +2086,7 @@ fmts |= fmtlist[i]; /* report software-supported formats */ - if (report_soft_formats) + if (!CHN_BITPERFECT(c) && report_soft_formats) fmts |= AFMT_MU_LAW|AFMT_A_LAW|AFMT_U32_LE|AFMT_U32_BE| AFMT_S32_LE|AFMT_S32_BE|AFMT_U24_LE|AFMT_U24_BE| AFMT_S24_LE|AFMT_S24_BE|AFMT_U16_LE|AFMT_U16_BE| @@ -1837,225 +2095,6 @@ return fmts; } -static int -chn_buildfeeder(struct pcm_channel *c) -{ - struct feeder_class *fc; - struct pcm_feederdesc desc; - struct snd_mixer *m; - u_int32_t tmp[2], type, flags, hwfmt, *fmtlist; - int err; - char fmtstr[AFMTSTR_MAXSZ]; - - CHN_LOCKASSERT(c); - while (chn_removefeeder(c) == 0) - ; - KASSERT((c->feeder == NULL), ("feeder chain not empty")); - - c->align = sndbuf_getalign(c->bufsoft); - - if (CHN_EMPTY(c, children) || c->direction == PCMDIR_REC) { - /* - * Virtual rec need this. - */ - fc = feeder_getclass(NULL); - KASSERT(fc != NULL, ("can't find root feeder")); - - err = chn_addfeeder(c, fc, NULL); - if (err) { - DEB(printf("can't add root feeder, err %d\n", err)); - - return err; - } - c->feeder->desc->out = c->format; - } else if (c->direction == PCMDIR_PLAY) { - if (c->flags & CHN_F_HAS_VCHAN) { - desc.type = FEEDER_MIXER; - desc.in = c->format; - } else { - DEB(printf("can't decide which feeder type to use!\n")); - return EOPNOTSUPP; - } - desc.out = c->format; - desc.flags = 0; - fc = feeder_getclass(&desc); - if (fc == NULL) { - DEB(printf("can't find vchan feeder\n")); - - return EOPNOTSUPP; - } - - err = chn_addfeeder(c, fc, &desc); - if (err) { - DEB(printf("can't add vchan feeder, err %d\n", err)); - - return err; - } - } else - return EOPNOTSUPP; - - /* XXX These are too much.. */ - if (c->parentsnddev != NULL && c->parentsnddev->mixer_dev != NULL && - c->parentsnddev->mixer_dev->si_drv1 != NULL) - m = c->parentsnddev->mixer_dev->si_drv1; - else - m = NULL; - - c->feederflags &= ~(1 << FEEDER_VOLUME); - if (c->direction == PCMDIR_PLAY && !(c->flags & CHN_F_VIRTUAL) && m && - (c->parentsnddev->flags & SD_F_SOFTPCMVOL)) - c->feederflags |= 1 << FEEDER_VOLUME; - - if (!(c->flags & CHN_F_VIRTUAL) && c->parentsnddev && - ((c->direction == PCMDIR_PLAY && - (c->parentsnddev->flags & SD_F_PSWAPLR)) || - (c->direction == PCMDIR_REC && - (c->parentsnddev->flags & SD_F_RSWAPLR)))) - c->feederflags |= 1 << FEEDER_SWAPLR; - - flags = c->feederflags; - fmtlist = chn_getcaps(c)->fmtlist; - - DEB(printf("feederflags %x\n", flags)); - - for (type = FEEDER_RATE; type < FEEDER_LAST; type++) { - if (flags & (1 << type)) { - desc.type = type; - desc.in = 0; - desc.out = 0; - desc.flags = 0; - DEB(printf("find feeder type %d, ", type)); - if (type == FEEDER_VOLUME || type == FEEDER_RATE) { - if (c->feeder->desc->out & AFMT_32BIT) - strlcpy(fmtstr,"s32le", sizeof(fmtstr)); - else if (c->feeder->desc->out & AFMT_24BIT) - strlcpy(fmtstr, "s24le", sizeof(fmtstr)); - else { - /* - * 8bit doesn't provide enough headroom - * for proper processing without - * creating too much noises. Force to - * 16bit instead. - */ - strlcpy(fmtstr, "s16le", sizeof(fmtstr)); - } - if (!(c->feeder->desc->out & AFMT_8BIT) && - c->feeder->desc->out & AFMT_BIGENDIAN) - afmtstr_swap_endian(fmtstr); - if (!(c->feeder->desc->out & (AFMT_A_LAW | AFMT_MU_LAW)) && - !(c->feeder->desc->out & AFMT_SIGNED)) - afmtstr_swap_sign(fmtstr); - desc.in = afmtstr2afmt(NULL, fmtstr, AFMTSTR_MONO_RETURN); - if (desc.in == 0) - desc.in = AFMT_S16_LE; - /* feeder_volume need stereo processing */ - if (type == FEEDER_VOLUME || - c->feeder->desc->out & AFMT_STEREO) - desc.in |= AFMT_STEREO; - desc.out = desc.in; - } else if (type == FEEDER_SWAPLR) { - desc.in = c->feeder->desc->out; - desc.in |= AFMT_STEREO; - desc.out = desc.in; - } - - fc = feeder_getclass(&desc); - DEB(printf("got %p\n", fc)); - if (fc == NULL) { - DEB(printf("can't find required feeder type %d\n", type)); - - return EOPNOTSUPP; - } - - if (desc.in == 0 || desc.out == 0) - desc = *fc->desc; - - DEB(printf("build fmtchain from 0x%08x to 0x%08x: ", c->feeder->desc->out, fc->desc->in)); - tmp[0] = desc.in; - tmp[1] = 0; - if (chn_fmtchain(c, tmp) == 0) { - DEB(printf("failed\n")); - - return ENODEV; - } - DEB(printf("ok\n")); - - err = chn_addfeeder(c, fc, &desc); - if (err) { - DEB(printf("can't add feeder %p, output 0x%x, err %d\n", fc, fc->desc->out, err)); - - return err; - } - DEB(printf("added feeder %p, output 0x%x\n", fc, c->feeder->desc->out)); - } - } - - if (c->direction == PCMDIR_REC) { - tmp[0] = c->format; - tmp[1] = 0; - hwfmt = chn_fmtchain(c, tmp); - } else - hwfmt = chn_fmtchain(c, fmtlist); - - if (hwfmt == 0 || !fmtvalid(hwfmt, fmtlist)) { - DEB(printf("Invalid hardware format: 0x%08x\n", hwfmt)); - return ENODEV; - } else if (c->direction == PCMDIR_REC && !CHN_EMPTY(c, children)) { - /* - * Kind of awkward. This whole "MIXER" concept need a - * rethinking, I guess :) . Recording is the inverse - * of Playback, which is why we push mixer vchan down here. - */ - if (c->flags & CHN_F_HAS_VCHAN) { - desc.type = FEEDER_MIXER; - desc.in = c->format; - } else - return EOPNOTSUPP; - desc.out = c->format; - desc.flags = 0; - fc = feeder_getclass(&desc); - if (fc == NULL) - return EOPNOTSUPP; - - err = chn_addfeeder(c, fc, &desc); - if (err != 0) - return err; - } - - sndbuf_setfmt(c->bufhard, hwfmt); - - if ((flags & (1 << FEEDER_VOLUME))) { - u_int32_t parent; - int vol, left, right; - - CHN_UNLOCK(c); - vol = mix_get(m, SOUND_MIXER_PCM); - if (vol == -1) { - device_printf(c->dev, - "Soft PCM Volume: Failed to read default value\n"); - vol = 100 | (100 << 8); - } - left = vol & 0x7f; - right = (vol >> 8) & 0x7f; - parent = mix_getparent(m, SOUND_MIXER_PCM); - if (parent != SOUND_MIXER_NONE) { - vol = mix_get(m, parent); - if (vol == -1) { - device_printf(c->dev, - "Soft Volume: Failed to read parent " - "default value\n"); - vol = 100 | (100 << 8); - } - left = (left * (vol & 0x7f)) / 100; - right = (right * ((vol >> 8) & 0x7f)) / 100; - } - CHN_LOCK(c); - chn_setvolume(c, left, right); - } - - return 0; -} - int chn_notify(struct pcm_channel *c, u_int32_t flags) { --- sys/dev/sound/pcm/channel.h.orig 2007-06-16 11:37:28.000000000 +0800 +++ sys/dev/sound/pcm/channel.h 2008-06-30 10:14:34.000000000 +0800 @@ -26,6 +26,8 @@ * $FreeBSD: src/sys/dev/sound/pcm/channel.h,v 1.37 2007/06/16 03:37:28 ariff Exp $ */ +#include + struct pcmchan_caps { u_int32_t minspeed, maxspeed; u_int32_t *fmtlist; @@ -63,7 +65,10 @@ struct pcm_channel *ch; }; -#define CHN_NAMELEN 32 +#define CHN_NAMELEN 32 +#define CHN_COMM_UNUSED "" +#define CHN_COMM_UNKNOWN "" + struct pcm_channel { kobj_t methods; @@ -72,7 +77,6 @@ struct pcm_feeder *feeder; u_int32_t align; - int volume; int latency; u_int32_t speed; u_int32_t format; @@ -90,6 +94,7 @@ device_t dev; int unit; char name[CHN_NAMELEN]; + char comm[MAXCOMLEN + 1]; struct mtx *lock; int trigger; /** @@ -139,9 +144,15 @@ struct { SLIST_ENTRY(pcm_channel) link; } busy; + struct { + SLIST_ENTRY(pcm_channel) link; + } opened; } pcm; } channels; + int volume[SND_VOL_C_MAX][SND_CHN_T_VOL_MAX]; + int matrix[SND_CHN_T_MAX + 1]; + void *data1, *data2; }; @@ -172,9 +183,8 @@ if (t == y) \ break; \ } \ - if (t != y) { \ + if (t != y) \ CHN_INSERT_HEAD(x, y, z); \ - } \ } while(0) #define CHN_INSERT_AFTER_SAFE(w, x, y, z) do { \ @@ -183,9 +193,8 @@ if (t == y) \ break; \ } \ - if (t != y) { \ + if (t != y) \ CHN_INSERT_AFTER(x, y, z); \ - } \ } while(0) #define CHN_REMOVE_SAFE(x, y, z) do { \ @@ -194,11 +203,27 @@ if (t == y) \ break; \ } \ - if (t == y) { \ + if (t == y) \ CHN_REMOVE(x, y, z); \ +} while(0) + +#define CHN_INSERT_SORT(w, x, y, z) do { \ + struct pcm_channel *t, *a = NULL; \ + CHN_FOREACH(t, x, z) { \ + if ((y)->unit w t->unit) \ + a = t; \ + else \ + break; \ } \ + if (a != NULL) \ + CHN_INSERT_AFTER(a, y, z); \ + else \ + CHN_INSERT_HEAD(x, y, z); \ } while(0) +#define CHN_INSERT_SORT_ASCEND(x, y, z) CHN_INSERT_SORT(>, x, y, z) +#define CHN_INSERT_SORT_DESCEND(x, y, z) CHN_INSERT_SORT(<, x, y, z) + #define CHN_UNIT(x) (snd_unit2u((x)->unit)) #define CHN_DEV(x) (snd_unit2d((x)->unit)) #define CHN_CHAN(x) (snd_unit2c((x)->unit)) @@ -226,12 +251,18 @@ int chn_init(struct pcm_channel *c, void *devinfo, int dir, int direction); int chn_kill(struct pcm_channel *c); int chn_setdir(struct pcm_channel *c, int dir); -int chn_reset(struct pcm_channel *c, u_int32_t fmt); +int chn_reset(struct pcm_channel *c, u_int32_t fmt, u_int32_t spd); int chn_setvolume(struct pcm_channel *c, int left, int right); +int chn_setvolume_matrix(struct pcm_channel *c, snd_volume_class_t vc, + snd_channel_t vt, int val); +int chn_getvolume_matrix(struct pcm_channel *c, snd_volume_class_t vc, + snd_channel_t vt); +void chn_vpc_reset(struct pcm_channel *c, snd_volume_class_t vc, int force); int chn_setspeed(struct pcm_channel *c, int speed); int chn_setformat(struct pcm_channel *c, u_int32_t fmt); int chn_setblocksize(struct pcm_channel *c, int blkcnt, int blksz); int chn_setlatency(struct pcm_channel *c, int latency); +void chn_syncstate(struct pcm_channel *c); int chn_trigger(struct pcm_channel *c, int go); int chn_getptr(struct pcm_channel *c); struct pcmchan_caps *chn_getcaps(struct pcm_channel *c); @@ -253,6 +284,13 @@ int chn_getrates(struct pcm_channel *c, int **rates); int chn_syncdestroy(struct pcm_channel *c); +#define CHN_SETVOLUME(x...) chn_setvolume_matrix(x) +#if defined(SND_DIAGNOSTIC) || defined(INVARIANTS) +#define CHN_GETVOLUME(x...) chn_getvolume_matrix(x) +#else +#define CHN_GETVOLUME(x, y, z) ((x)->volume[y][z]) +#endif + #ifdef OSSV4_EXPERIMENT int chn_getpeaks(struct pcm_channel *c, int *lpeak, int *rpeak); #endif @@ -326,6 +364,7 @@ #define CHN_F_BADSETTING 0x00040000 #define CHN_F_SETBLOCKSIZE 0x00080000 #define CHN_F_HAS_VCHAN 0x00100000 +#define CHN_F_BITPERFECT 0x00200000 #define CHN_F_VIRTUAL 0x10000000 /* not backed by hardware */ @@ -357,6 +396,7 @@ #define CHN_STOPPED(c) (!CHN_STARTED(c)) #define CHN_DIRSTR(c) (((c)->direction == PCMDIR_PLAY) ? \ "PCMDIR_PLAY" : "PCMDIR_REC") +#define CHN_BITPERFECT(c) ((c)->flags & CHN_F_BITPERFECT) #define CHN_TIMEOUT 5 #define CHN_TIMEOUT_MIN 1 --- sys/dev/sound/pcm/dsp.c.orig 2008-05-27 10:19:10.000000000 +0800 +++ sys/dev/sound/pcm/dsp.c 2008-06-30 10:14:34.000000000 +0800 @@ -35,12 +35,14 @@ struct dsp_cdevinfo { struct pcm_channel *rdch, *wrch; + struct pcm_channel *volch; int busy, simplex; TAILQ_ENTRY(dsp_cdevinfo) link; }; #define PCM_RDCH(x) (((struct dsp_cdevinfo *)(x)->si_drv1)->rdch) #define PCM_WRCH(x) (((struct dsp_cdevinfo *)(x)->si_drv1)->wrch) +#define PCM_VOLCH(x) (((struct dsp_cdevinfo *)(x)->si_drv1)->volch) #define PCM_SIMPLEX(x) (((struct dsp_cdevinfo *)(x)->si_drv1)->simplex) #define DSP_CDEVINFO_CACHESIZE 8 @@ -79,8 +81,8 @@ static int dsp_oss_syncgroup(struct pcm_channel *wrch, struct pcm_channel *rdch, oss_syncgroup *group); static int dsp_oss_syncstart(int sg_id); static int dsp_oss_policy(struct pcm_channel *wrch, struct pcm_channel *rdch, int policy); -#ifdef OSSV4_EXPERIMENT static int dsp_oss_cookedmode(struct pcm_channel *wrch, struct pcm_channel *rdch, int enabled); +#ifdef OSSV4_EXPERIMENT static int dsp_oss_getchnorder(struct pcm_channel *wrch, struct pcm_channel *rdch, unsigned long long *map); static int dsp_oss_setchnorder(struct pcm_channel *wrch, struct pcm_channel *rdch, unsigned long long *map); static int dsp_oss_getlabel(struct pcm_channel *wrch, struct pcm_channel *rdch, oss_label_t *label); @@ -200,7 +202,8 @@ static void dsp_cdevinfo_alloc(struct cdev *dev, - struct pcm_channel *rdch, struct pcm_channel *wrch) + struct pcm_channel *rdch, struct pcm_channel *wrch, + struct pcm_channel *volch) { struct snddev_info *d; struct dsp_cdevinfo *cdi; @@ -209,7 +212,7 @@ d = dsp_get_info(dev); KASSERT(PCM_REGISTERED(d) && dev != NULL && dev->si_drv1 == NULL && - rdch != wrch, + ((rdch == NULL && wrch == NULL) || rdch != wrch), ("bogus %s(), what are you trying to accomplish here?", __func__)); PCM_BUSYASSERT(d); mtx_assert(d->lock, MA_OWNED); @@ -225,6 +228,7 @@ break; cdi->rdch = rdch; cdi->wrch = wrch; + cdi->volch = volch; cdi->simplex = simplex; cdi->busy = 1; TAILQ_REMOVE(&d->dsp_cdevinfo_pool, cdi, link); @@ -237,6 +241,7 @@ pcm_lock(d); cdi->rdch = rdch; cdi->wrch = wrch; + cdi->volch = volch; cdi->simplex = simplex; cdi->busy = 1; TAILQ_INSERT_TAIL(&d->dsp_cdevinfo_pool, cdi, link); @@ -254,7 +259,8 @@ d = dsp_get_info(dev); KASSERT(PCM_REGISTERED(d) && dev != NULL && dev->si_drv1 != NULL && - PCM_RDCH(dev) == NULL && PCM_WRCH(dev) == NULL, + PCM_RDCH(dev) == NULL && PCM_WRCH(dev) == NULL && + PCM_VOLCH(dev) == NULL, ("bogus %s(), what are you trying to accomplish here?", __func__)); PCM_BUSYASSERT(d); mtx_assert(d->lock, MA_OWNED); @@ -263,6 +269,7 @@ dev->si_drv1 = NULL; cdi->rdch = NULL; cdi->wrch = NULL; + cdi->volch = NULL; cdi->simplex = 0; cdi->busy = 0; @@ -340,6 +347,12 @@ DSP_CDEV_TYPE_RDWR, /* duplex read, write, or both */ }; +enum { + DSP_CDEV_VOLCTL_NONE, + DSP_CDEV_VOLCTL_READ, + DSP_CDEV_VOLCTL_WRITE, +}; + #define DSP_F_VALID(x) ((x) & (FREAD | FWRITE)) #define DSP_F_DUPLEX(x) (((x) & (FREAD | FWRITE)) == (FREAD | FWRITE)) #define DSP_F_SIMPLEX(x) (!DSP_F_DUPLEX(x)) @@ -350,30 +363,41 @@ int type; char *name; char *sep; + char *alias; int use_sep; int hw; int max; + int volctl; uint32_t fmt, spd; int query; } dsp_cdevs[] = { - { SND_DEV_DSP, "dsp", ".", 0, 0, 0, + { SND_DEV_DSP, "dsp", ".", NULL, 0, 0, 0, 0, AFMT_U8, DSP_DEFAULT_SPEED, DSP_CDEV_TYPE_RDWR }, - { SND_DEV_AUDIO, "audio", ".", 0, 0, 0, + { SND_DEV_AUDIO, "audio", ".", NULL, 0, 0, 0, 0, AFMT_MU_LAW, DSP_DEFAULT_SPEED, DSP_CDEV_TYPE_RDWR }, - { SND_DEV_DSP16, "dspW", ".", 0, 0, 0, + { SND_DEV_DSP16, "dspW", ".", NULL, 0, 0, 0, 0, AFMT_S16_LE, DSP_DEFAULT_SPEED, DSP_CDEV_TYPE_RDWR }, - { SND_DEV_DSPHW_PLAY, "dsp", ".p", 1, 1, SND_MAXHWCHAN, + { SND_DEV_DSPHW_PLAY, "dsp", ".p", NULL, 1, 1, SND_MAXHWCHAN, 1, AFMT_S16_LE | AFMT_STEREO, 48000, DSP_CDEV_TYPE_WRONLY }, - { SND_DEV_DSPHW_VPLAY, "dsp", ".vp", 1, 1, SND_MAXVCHANS, + { SND_DEV_DSPHW_VPLAY, "dsp", ".vp", NULL, 1, 1, SND_MAXVCHANS, 1, AFMT_S16_LE | AFMT_STEREO, 48000, DSP_CDEV_TYPE_WRONLY }, - { SND_DEV_DSPHW_REC, "dsp", ".r", 1, 1, SND_MAXHWCHAN, + { SND_DEV_DSPHW_REC, "dsp", ".r", NULL, 1, 1, SND_MAXHWCHAN, 1, AFMT_S16_LE | AFMT_STEREO, 48000, DSP_CDEV_TYPE_RDONLY }, - { SND_DEV_DSPHW_VREC, "dsp", ".vr", 1, 1, SND_MAXVCHANS, + { SND_DEV_DSPHW_VREC, "dsp", ".vr", NULL, 1, 1, SND_MAXVCHANS, 1, AFMT_S16_LE | AFMT_STEREO, 48000, DSP_CDEV_TYPE_RDONLY }, - { SND_DEV_DSPHW_CD, "dspcd", ".", 0, 0, 0, + { SND_DEV_DSPHW_CD, "dspcd", ".", NULL, 0, 0, 0, 0, AFMT_S16_LE | AFMT_STEREO, 44100, DSP_CDEV_TYPE_RDWR }, - { SND_DEV_DSP_MMAP, "dsp_mmap", ".", 0, 0, 0, - AFMT_S16_LE | AFMT_STEREO, 48000, DSP_CDEV_TYPE_RDWR }, + /* Low priority, OSSv4 aliases. */ + { SND_DEV_DSP, "dsp_ac3", ".", "dsp", 0, 0, 0, 0, + AFMT_U8, DSP_DEFAULT_SPEED, DSP_CDEV_TYPE_RDWR }, + { SND_DEV_DSP, "dsp_mmap", ".", "dsp", 0, 0, 0, 0, + AFMT_U8, DSP_DEFAULT_SPEED, DSP_CDEV_TYPE_RDWR }, + { SND_DEV_DSP, "dsp_multich", ".", "dsp", 0, 0, 0, 0, + AFMT_U8, DSP_DEFAULT_SPEED, DSP_CDEV_TYPE_RDWR }, + { SND_DEV_DSP, "dsp_spdifout", ".", "dsp", 0, 0, 0, 0, + AFMT_U8, DSP_DEFAULT_SPEED, DSP_CDEV_TYPE_RDWR }, + { SND_DEV_DSP, "dsp_spdifin", ".", "dsp", 0, 0, 0, 0, + AFMT_U8, DSP_DEFAULT_SPEED, DSP_CDEV_TYPE_RDWR }, }; #define DSP_FIXUP_ERROR() do { \ @@ -397,7 +421,7 @@ { struct pcm_channel *rdch, *wrch; struct snddev_info *d; - uint32_t fmt, spd, prio; + uint32_t fmt, spd, prio, volctl; int i, error, rderror, wrerror, devtype, wdevunit, rdevunit; /* Kind of impossible.. */ @@ -449,27 +473,46 @@ rdevunit = -1; fmt = 0; spd = 0; + volctl = DSP_CDEV_VOLCTL_NONE; for (i = 0; i < (sizeof(dsp_cdevs) / sizeof(dsp_cdevs[0])); i++) { - if (devtype != dsp_cdevs[i].type) + if (devtype != dsp_cdevs[i].type || dsp_cdevs[i].alias != NULL) continue; - if (DSP_F_SIMPLEX(flags) && - ((dsp_cdevs[i].query == DSP_CDEV_TYPE_WRONLY && - DSP_F_READ(flags)) || - (dsp_cdevs[i].query == DSP_CDEV_TYPE_RDONLY && - DSP_F_WRITE(flags)))) { - /* - * simplex, opposite direction? Please be gone.. - */ - (void)snd_clone_release(i_dev); - PCM_RELEASE_QUICK(d); - PCM_GIANT_EXIT(d); - return (ENOTSUP); - } - if (dsp_cdevs[i].query == DSP_CDEV_TYPE_WRONLY) + /* + * Volume control only valid for DSPHW devices, + * and it must be opened in opposite direction be it + * simplex or duplex. Anything else will be handled + * as usual. + */ + if (dsp_cdevs[i].query == DSP_CDEV_TYPE_WRONLY) { + if (dsp_cdevs[i].volctl != 0 && + DSP_F_READ(flags)) { + volctl = DSP_CDEV_VOLCTL_WRITE; + flags &= ~FREAD; + flags |= FWRITE; + } + if (DSP_F_READ(flags)) { + (void)snd_clone_release(i_dev); + PCM_RELEASE_QUICK(d); + PCM_GIANT_EXIT(d); + return (ENOTSUP); + } wdevunit = dev2unit(i_dev); - else if (dsp_cdevs[i].query == DSP_CDEV_TYPE_RDONLY) + } else if (dsp_cdevs[i].query == DSP_CDEV_TYPE_RDONLY) { + if (dsp_cdevs[i].volctl != 0 && + DSP_F_WRITE(flags)) { + volctl = DSP_CDEV_VOLCTL_READ; + flags &= ~FWRITE; + flags |= FREAD; + } + if (DSP_F_WRITE(flags)) { + (void)snd_clone_release(i_dev); + PCM_RELEASE_QUICK(d); + PCM_GIANT_EXIT(d); + return (ENOTSUP); + } rdevunit = dev2unit(i_dev); + } fmt = dsp_cdevs[i].fmt; spd = dsp_cdevs[i].spd; break; @@ -493,12 +536,14 @@ if (DSP_F_READ(flags)) { /* open for read */ rderror = pcm_chnalloc(d, &rdch, PCMDIR_REC, - td->td_proc->p_pid, rdevunit); + td->td_proc->p_pid, td->td_proc->p_comm, rdevunit); - if (rderror == 0 && (chn_reset(rdch, fmt) != 0 || - (chn_setspeed(rdch, spd) != 0))) + if (rderror == 0 && chn_reset(rdch, fmt, spd) != 0) rderror = ENXIO; + if (volctl == DSP_CDEV_VOLCTL_READ) + rderror = 0; + if (rderror != 0) { if (rdch != NULL) pcm_chnrelease(rdch); @@ -509,10 +554,17 @@ return (rderror); } rdch = NULL; + } else if (volctl == DSP_CDEV_VOLCTL_READ) { + if (rdch != NULL) { + pcm_chnref(rdch, 1); + pcm_chnrelease(rdch); + } } else { if (flags & O_NONBLOCK) rdch->flags |= CHN_F_NBIO; pcm_chnref(rdch, 1); + if (volctl == DSP_CDEV_VOLCTL_NONE) + chn_vpc_reset(rdch, SND_VOL_C_PCM, 0); CHN_UNLOCK(rdch); } } @@ -520,12 +572,14 @@ if (DSP_F_WRITE(flags)) { /* open for write */ wrerror = pcm_chnalloc(d, &wrch, PCMDIR_PLAY, - td->td_proc->p_pid, wdevunit); + td->td_proc->p_pid, td->td_proc->p_comm, wdevunit); - if (wrerror == 0 && (chn_reset(wrch, fmt) != 0 || - (chn_setspeed(wrch, spd) != 0))) + if (wrerror == 0 && chn_reset(wrch, fmt, spd) != 0) wrerror = ENXIO; + if (volctl == DSP_CDEV_VOLCTL_WRITE) + wrerror = 0; + if (wrerror != 0) { if (wrch != NULL) pcm_chnrelease(wrch); @@ -545,27 +599,56 @@ return (wrerror); } wrch = NULL; + } else if (volctl == DSP_CDEV_VOLCTL_WRITE) { + if (wrch != NULL) { + pcm_chnref(wrch, 1); + pcm_chnrelease(wrch); + } } else { if (flags & O_NONBLOCK) wrch->flags |= CHN_F_NBIO; pcm_chnref(wrch, 1); + if (volctl == DSP_CDEV_VOLCTL_NONE) + chn_vpc_reset(wrch, SND_VOL_C_PCM, 0); CHN_UNLOCK(wrch); } } - if (rdch == NULL && wrch == NULL) { - (void)snd_clone_release(i_dev); - PCM_RELEASE_QUICK(d); - PCM_GIANT_EXIT(d); - return ((wrerror != 0) ? wrerror : rderror); - } pcm_lock(d); /* * We're done. Allocate channels information for this cdev. */ - dsp_cdevinfo_alloc(i_dev, rdch, wrch); + switch (volctl) { + case DSP_CDEV_VOLCTL_READ: + KASSERT(wrch == NULL, ("wrch=%p not null!", wrch)); + dsp_cdevinfo_alloc(i_dev, NULL, NULL, rdch); + break; + case DSP_CDEV_VOLCTL_WRITE: + KASSERT(rdch == NULL, ("rdch=%p not null!", rdch)); + dsp_cdevinfo_alloc(i_dev, NULL, NULL, wrch); + break; + case DSP_CDEV_VOLCTL_NONE: + default: + if (wrch == NULL && rdch == NULL) { + (void)snd_clone_release(i_dev); + PCM_RELEASE(d); + pcm_unlock(d); + PCM_GIANT_EXIT(d); + if (wrerror != 0) + return (wrerror); + if (rderror != 0) + return (rderror); + return (EINVAL); + } + dsp_cdevinfo_alloc(i_dev, rdch, wrch, NULL); + if (rdch != NULL) + CHN_INSERT_HEAD(d, rdch, channels.pcm.opened); + if (wrch != NULL) + CHN_INSERT_HEAD(d, wrch, channels.pcm.opened); + break; + } /* * Increase clone refcount for its automatic garbage collector. @@ -583,9 +666,9 @@ static int dsp_close(struct cdev *i_dev, int flags, int mode, struct thread *td) { - struct pcm_channel *rdch, *wrch; + struct pcm_channel *rdch, *wrch, *volch; struct snddev_info *d; - int sg_ids, refs; + int sg_ids, rdref, wdref; d = dsp_get_info(i_dev); if (!DSP_REGISTERED(d, i_dev)) @@ -595,24 +678,49 @@ pcm_lock(d); PCM_WAIT(d); + PCM_ACQUIRE(d); rdch = PCM_RDCH(i_dev); wrch = PCM_WRCH(i_dev); + volch = PCM_VOLCH(i_dev); - if (rdch || wrch) { - PCM_ACQUIRE(d); - pcm_unlock(d); + PCM_RDCH(i_dev) = NULL; + PCM_WRCH(i_dev) = NULL; + PCM_VOLCH(i_dev) = NULL; + + rdref = -1; + wdref = -1; + + if (volch != NULL) { + if (volch == rdch) + rdref--; + else if (volch == wrch) + wdref--; + else { + CHN_LOCK(volch); + pcm_chnref(volch, -1); + CHN_UNLOCK(volch); + } + } - refs = 0; - if (rdch) { + if (rdch != NULL) + CHN_REMOVE(d, rdch, channels.pcm.opened); + if (wrch != NULL) + CHN_REMOVE(d, wrch, channels.pcm.opened); + + if (rdch != NULL || wrch != NULL) { + pcm_unlock(d); + if (rdch != NULL) { /* * The channel itself need not be locked because: - * a) Adding a channel to a syncgroup happens only in dsp_ioctl(), - * which cannot run concurrently to dsp_close(). - * b) The syncmember pointer (sm) is protected by the global - * syncgroup list lock. - * c) A channel can't just disappear, invalidating pointers, - * unless it's closed/dereferenced first. + * a) Adding a channel to a syncgroup happens only + * in dsp_ioctl(), which cannot run concurrently + * to dsp_close(). + * b) The syncmember pointer (sm) is protected by + * the global syncgroup list lock. + * c) A channel can't just disappear, invalidating + * pointers, unless it's closed/dereferenced + * first. */ PCM_SG_LOCK(); sg_ids = chn_syncdestroy(rdch); @@ -621,14 +729,14 @@ free_unr(pcmsg_unrhdr, sg_ids); CHN_LOCK(rdch); - refs += pcm_chnref(rdch, -1); + pcm_chnref(rdch, rdref); chn_abort(rdch); /* won't sleep */ - rdch->flags &= ~(CHN_F_RUNNING | CHN_F_MAPPED | CHN_F_DEAD); - chn_reset(rdch, 0); + rdch->flags &= ~(CHN_F_RUNNING | CHN_F_MAPPED | + CHN_F_DEAD); + chn_reset(rdch, 0, 0); pcm_chnrelease(rdch); - PCM_RDCH(i_dev) = NULL; } - if (wrch) { + if (wrch != NULL) { /* * Please see block above. */ @@ -639,40 +747,33 @@ free_unr(pcmsg_unrhdr, sg_ids); CHN_LOCK(wrch); - refs += pcm_chnref(wrch, -1); + pcm_chnref(wrch, wdref); chn_flush(wrch); /* may sleep */ - wrch->flags &= ~(CHN_F_RUNNING | CHN_F_MAPPED | CHN_F_DEAD); - chn_reset(wrch, 0); + wrch->flags &= ~(CHN_F_RUNNING | CHN_F_MAPPED | + CHN_F_DEAD); + chn_reset(wrch, 0, 0); pcm_chnrelease(wrch); - PCM_WRCH(i_dev) = NULL; } - pcm_lock(d); - /* - * If there are no more references, release the channels. - */ - if (refs == 0 && PCM_RDCH(i_dev) == NULL && - PCM_WRCH(i_dev) == NULL) { - dsp_cdevinfo_free(i_dev); - /* - * Release clone busy state and unref it - * so the automatic garbage collector will - * get the hint and do the remaining cleanup - * process. - */ - (void)snd_clone_release(i_dev); - - /* - * destroy_dev() might sleep, so release pcm lock - * here and rely on pcm cv serialization. - */ - pcm_unlock(d); - (void)snd_clone_unref(i_dev); - pcm_lock(d); - } - PCM_RELEASE(d); } + dsp_cdevinfo_free(i_dev); + /* + * Release clone busy state and unref it so the automatic + * garbage collector will get the hint and do the remaining + * cleanup process. + */ + (void)snd_clone_release(i_dev); + + /* + * destroy_dev() might sleep, so release pcm lock + * here and rely on pcm cv serialization. + */ + pcm_unlock(d); + (void)snd_clone_unref(i_dev); + pcm_lock(d); + + PCM_RELEASE(d); pcm_unlock(d); PCM_GIANT_LEAVE(d); @@ -767,11 +868,172 @@ } static int -dsp_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, struct thread *td) +dsp_get_volume_channel(struct cdev *dev, struct pcm_channel **volch) +{ + struct snddev_info *d; + struct pcm_channel *c; + int unit; + + KASSERT(dev != NULL && volch != NULL, + ("%s(): NULL query dev=%p volch=%p", __func__, dev, volch)); + + d = dsp_get_info(dev); + if (!PCM_REGISTERED(d)) { + *volch = NULL; + return (EINVAL); + } + + mtx_assert(d->lock, MA_NOTOWNED); + + *volch = NULL; + + c = PCM_VOLCH(dev); + if (c != NULL) { + if (!(c->feederflags & (1 << FEEDER_VOLUME))) + return (-1); + *volch = c; + return (0); + } + + pcm_lock(d); + PCM_WAIT(d); + PCM_ACQUIRE(d); + + unit = dev2unit(dev); + + CHN_FOREACH(c, d, channels.pcm) { + CHN_LOCK(c); + if (c->unit != unit) { + CHN_UNLOCK(c); + continue; + } + *volch = c; + pcm_chnref(c, 1); + PCM_VOLCH(dev) = c; + CHN_UNLOCK(c); + PCM_RELEASE(d); + pcm_unlock(d); + return ((c->feederflags & (1 << FEEDER_VOLUME)) ? 0 : -1); + } + + PCM_RELEASE(d); + pcm_unlock(d); + + return (EINVAL); +} + +static int +dsp_ioctl_channel(struct cdev *dev, struct pcm_channel *volch, u_long cmd, + caddr_t arg) +{ + struct snddev_info *d; + struct pcm_channel *rdch, *wrch; + int j, devtype, ret; + + d = dsp_get_info(dev); + if (!PCM_REGISTERED(d) || !(d->flags & SD_F_VPC)) + return (-1); + + mtx_assert(d->lock, MA_NOTOWNED); + + j = cmd & 0xff; + + rdch = PCM_RDCH(dev); + wrch = PCM_WRCH(dev); + + /* No specific channel, look into cache */ + if (volch == NULL) + volch = PCM_VOLCH(dev); + + /* Look harder */ + if (volch == NULL) { + if (j == SOUND_MIXER_RECLEV && rdch != NULL) + volch = rdch; + else if (j == SOUND_MIXER_PCM && wrch != NULL) + volch = wrch; + } + + devtype = PCMDEV(dev); + + /* Look super harder */ + if (volch == NULL && + (devtype == SND_DEV_DSPHW_PLAY || devtype == SND_DEV_DSPHW_VPLAY || + devtype == SND_DEV_DSPHW_REC || devtype == SND_DEV_DSPHW_VREC)) { + ret = dsp_get_volume_channel(dev, &volch); + if (ret != 0) + return (ret); + if (volch == NULL) + return (EINVAL); + } + + /* Final validation */ + if (volch != NULL) { + CHN_LOCK(volch); + if (!(volch->feederflags & (1 << FEEDER_VOLUME))) { + CHN_UNLOCK(volch); + return (-1); + } + if (volch->direction == PCMDIR_PLAY) + wrch = volch; + else + rdch = volch; + } + + ret = EINVAL; + + if (volch != NULL && + ((j == SOUND_MIXER_PCM && volch->direction == PCMDIR_PLAY) || + (j == SOUND_MIXER_RECLEV && volch->direction == PCMDIR_REC))) { + if ((cmd & MIXER_WRITE(0)) == MIXER_WRITE(0)) { + CHN_SETVOLUME(volch, SND_VOL_C_PCM, SND_CHN_T_FL, + *(int *)arg & 0x7f); + CHN_SETVOLUME(volch, SND_VOL_C_PCM, SND_CHN_T_FR, + (*(int *)arg >> 8) & 0x7f); + } else if ((cmd & MIXER_READ(0)) == MIXER_READ(0)) { + *(int *)arg = CHN_GETVOLUME(volch, + SND_VOL_C_PCM, SND_CHN_T_FL); + *(int *)arg |= CHN_GETVOLUME(volch, + SND_VOL_C_PCM, SND_CHN_T_FR) << 8; + } + ret = 0; + } else if (rdch != NULL || wrch != NULL) { + switch (j) { + case SOUND_MIXER_DEVMASK: + case SOUND_MIXER_CAPS: + case SOUND_MIXER_STEREODEVS: + if ((cmd & MIXER_READ(0)) == MIXER_READ(0)) { + *(int *)arg = 0; + if (rdch != NULL) + *(int *)arg |= SOUND_MASK_RECLEV; + if (wrch != NULL) + *(int *)arg |= SOUND_MASK_PCM; + } + ret = 0; + break; + case SOUND_MIXER_RECMASK: + case SOUND_MIXER_RECSRC: + if ((cmd & MIXER_READ(0)) == MIXER_READ(0)) + *(int *)arg = 0; + ret = 0; + break; + default: + break; + } + } + + if (volch != NULL) + CHN_UNLOCK(volch); + + return (ret); +} + +static int +dsp_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, + struct thread *td) { struct pcm_channel *chn, *rdch, *wrch; struct snddev_info *d; - int *arg_i, ret, kill, tmp, xcmd; + int *arg_i, ret, tmp, xcmd; d = dsp_get_info(i_dev); if (!DSP_REGISTERED(d, i_dev)) @@ -782,15 +1044,15 @@ arg_i = (int *)arg; ret = 0; xcmd = 0; + chn = NULL; - /* - * this is an evil hack to allow broken apps to perform mixer ioctls - * on dsp devices. - */ if (IOCGROUP(cmd) == 'M') { - /* - * This is at least, a bug to bug compatible with OSS. - */ + ret = dsp_ioctl_channel(i_dev, PCM_VOLCH(i_dev), cmd, arg); + if (ret != -1) { + PCM_GIANT_EXIT(d); + return (ret); + } + if (d->mixer_dev != NULL) { PCM_ACQUIRE_QUICK(d); ret = mixer_ioctl_cmd(d->mixer_dev, cmd, arg, -1, td, @@ -830,23 +1092,12 @@ getchns(i_dev, &rdch, &wrch, 0); - kill = 0; - if (wrch && (wrch->flags & CHN_F_DEAD)) - kill |= 1; - if (rdch && (rdch->flags & CHN_F_DEAD)) - kill |= 2; - if (kill == 3) { - relchns(i_dev, rdch, wrch, 0); - PCM_GIANT_EXIT(d); - return (EINVAL); - } - if (kill & 1) + if (wrch != NULL && (wrch->flags & CHN_F_DEAD)) wrch = NULL; - if (kill & 2) + if (rdch != NULL && (rdch->flags & CHN_F_DEAD)) rdch = NULL; if (wrch == NULL && rdch == NULL) { - relchns(i_dev, rdch, wrch, 0); PCM_GIANT_EXIT(d); return (EINVAL); } @@ -1478,20 +1729,34 @@ * command". */ case SNDCTL_DSP_GETRECVOL: - if (xcmd == 0) + if (xcmd == 0) { xcmd = SOUND_MIXER_READ_RECLEV; + chn = rdch; + } /* FALLTHROUGH */ case SNDCTL_DSP_SETRECVOL: - if (xcmd == 0) + if (xcmd == 0) { xcmd = SOUND_MIXER_WRITE_RECLEV; + chn = rdch; + } /* FALLTHROUGH */ case SNDCTL_DSP_GETPLAYVOL: - if (xcmd == 0) + if (xcmd == 0) { xcmd = SOUND_MIXER_READ_PCM; + chn = wrch; + } /* FALLTHROUGH */ case SNDCTL_DSP_SETPLAYVOL: - if (xcmd == 0) + if (xcmd == 0) { xcmd = SOUND_MIXER_WRITE_PCM; + chn = wrch; + } + + ret = dsp_ioctl_channel(i_dev, chn, xcmd, arg); + if (ret != -1) { + PCM_GIANT_EXIT(d); + return (ret); + } if (d->mixer_dev != NULL) { PCM_ACQUIRE_QUICK(d); @@ -1500,6 +1765,7 @@ PCM_RELEASE_QUICK(d); } else ret = ENOTSUP; + break; case SNDCTL_DSP_GET_RECSRC_NAMES: @@ -1714,6 +1980,11 @@ PCM_RELEASE_QUICK(d); break; + case SNDCTL_DSP_COOKEDMODE: + PCM_ACQUIRE_QUICK(d); + ret = dsp_oss_cookedmode(wrch, rdch, *arg_i); + PCM_RELEASE_QUICK(d); + break; #ifdef OSSV4_EXPERIMENT /* * XXX The following ioctls are not yet supported and just return @@ -1744,9 +2015,6 @@ * XXX Once implemented, revisit this for proper cv protection * (if necessary). */ - case SNDCTL_DSP_COOKEDMODE: - ret = dsp_oss_cookedmode(wrch, rdch, *arg_i); - break; case SNDCTL_DSP_GET_CHNORDER: ret = dsp_oss_getchnorder(wrch, rdch, (unsigned long long *)arg); break; @@ -1809,8 +2077,6 @@ break; } - relchns(i_dev, rdch, wrch, 0); - PCM_GIANT_LEAVE(d); return (ret); @@ -1982,7 +2248,7 @@ struct snd_clone_entry *ce; struct pcm_channel *c; int i, unit, udcmask, cunit, devtype, devhw, devcmax, tumax; - char *devname, *devsep; + char *devname, *devcmp, *devsep; KASSERT(dsp_umax >= 0 && dsp_cmax >= 0, ("Uninitialized unit!")); @@ -2001,13 +2267,16 @@ for (i = 0; unit == -1 && i < (sizeof(dsp_cdevs) / sizeof(dsp_cdevs[0])); i++) { devtype = dsp_cdevs[i].type; - devname = dsp_cdevs[i].name; + devcmp = dsp_cdevs[i].name; devsep = dsp_cdevs[i].sep; + devname = dsp_cdevs[i].alias; + if (devname == NULL) + devname = devcmp; devhw = dsp_cdevs[i].hw; devcmax = dsp_cdevs[i].max - 1; - if (strcmp(name, devname) == 0) + if (strcmp(name, devcmp) == 0) unit = snd_unit; - else if (dsp_stdclone(name, devname, devsep, + else if (dsp_stdclone(name, devcmp, devsep, dsp_cdevs[i].use_sep, &unit, &cunit) != 0) { unit = -1; cunit = -1; @@ -2144,7 +2413,7 @@ dtype = snd_unit2d(unit); for (i = 0; i < (sizeof(dsp_cdevs) / sizeof(dsp_cdevs[0])); i++) { - if (dtype != dsp_cdevs[i].type) + if (dtype != dsp_cdevs[i].type || dsp_cdevs[i].alias != NULL) continue; snprintf(buf, len, "%s%d%s%d", dsp_cdevs[i].name, snd_unit2u(unit), dsp_cdevs[i].sep, snd_unit2c(unit)); @@ -2718,7 +2987,6 @@ return (ret); } -#ifdef OSSV4_EXPERIMENT /** * @brief Enable or disable "cooked" mode * @@ -2735,10 +3003,6 @@ * See @c http://manuals.opensound.com/developer/SNDCTL_DSP_COOKEDMODE.html * for more details. * - * @note Currently, this function is just a stub that always returns EINVAL. - * - * @todo Figure out how to and actually implement this. - * * @param wrch playback channel (optional; may be NULL) * @param rdch recording channel (optional; may be NULL) * @param enabled 0 = raw mode, 1 = cooked mode @@ -2748,9 +3012,41 @@ static int dsp_oss_cookedmode(struct pcm_channel *wrch, struct pcm_channel *rdch, int enabled) { - return (EINVAL); + + /* + * XXX I just don't get it. Why don't they call it + * "BITPERFECT" ~ SNDCTL_DSP_BITPERFECT !?!?. + * This is just plain so confusing, incoherent, + * . + */ + if (!(enabled == 1 || enabled == 0)) + return (EINVAL); + + /* + * I won't give in. I'm inverting its logic here and now. + * Brag all you want, but "BITPERFECT" should be the better + * term here. + */ + enabled ^= 0x00000001; + + if (wrch != NULL) { + CHN_LOCK(wrch); + wrch->flags &= ~CHN_F_BITPERFECT; + wrch->flags |= (enabled != 0) ? CHN_F_BITPERFECT : 0x00000000; + CHN_UNLOCK(wrch); + } + + if (rdch != NULL) { + CHN_LOCK(rdch); + rdch->flags &= ~CHN_F_BITPERFECT; + wrch->flags |= (enabled != 0) ? CHN_F_BITPERFECT : 0x00000000; + CHN_UNLOCK(rdch); + } + + return (0); } +#ifdef OSSV4_EXPERIMENT /** * @brief Retrieve channel interleaving order * --- sys/dev/sound/pcm/feeder.c.orig 2007-06-17 23:53:11.000000000 +0800 +++ sys/dev/sound/pcm/feeder.c 2008-06-30 10:14:34.000000000 +0800 @@ -35,43 +35,6 @@ #define MAXFEEDERS 256 #undef FEEDER_DEBUG -int feeder_buffersize = FEEDBUFSZ; -TUNABLE_INT("hw.snd.feeder_buffersize", &feeder_buffersize); - -#ifdef SND_DEBUG -static int -sysctl_hw_snd_feeder_buffersize(SYSCTL_HANDLER_ARGS) -{ - int i, err, val; - - val = feeder_buffersize; - err = sysctl_handle_int(oidp, &val, 0, req); - - if (err != 0 || req->newptr == NULL) - return err; - - if (val < FEEDBUFSZ_MIN || val > FEEDBUFSZ_MAX) - return EINVAL; - - i = 0; - while (val >> i) - i++; - i = 1 << i; - if (i > val && (i >> 1) > 0 && (i >> 1) >= ((val * 3) >> 2)) - i >>= 1; - - feeder_buffersize = i; - - return err; -} -SYSCTL_PROC(_hw_snd, OID_AUTO, feeder_buffersize, CTLTYPE_INT | CTLFLAG_RW, - 0, sizeof(int), sysctl_hw_snd_feeder_buffersize, "I", - "feeder buffer size"); -#else -SYSCTL_INT(_hw_snd, OID_AUTO, feeder_buffersize, CTLFLAG_RD, - &feeder_buffersize, FEEDBUFSZ, "feeder buffer size"); -#endif - struct feedertab_entry { SLIST_ENTRY(feedertab_entry) link; struct feeder_class *feederclass; @@ -131,10 +94,6 @@ chn_latency_profile > CHN_LATENCY_PROFILE_MAX) chn_latency_profile = CHN_LATENCY_PROFILE_DEFAULT; - if (feeder_buffersize < FEEDBUFSZ_MIN || - feeder_buffersize > FEEDBUFSZ_MAX) - feeder_buffersize = FEEDBUFSZ; - if (feeder_rate_min < FEEDRATE_MIN || feeder_rate_max < FEEDRATE_MIN || feeder_rate_min > FEEDRATE_MAX || @@ -150,11 +109,11 @@ if (bootverbose) printf("%s: snd_unit=%d snd_maxautovchans=%d " - "latency=%d feeder_buffersize=%d " + "latency=%d " "feeder_rate_min=%d feeder_rate_max=%d " "feeder_rate_round=%d\n", __func__, snd_unit, snd_maxautovchans, - chn_latency, feeder_buffersize, + chn_latency, feeder_rate_min, feeder_rate_max, feeder_rate_round); @@ -321,39 +280,6 @@ return NULL; } -static int -chainok(struct pcm_feeder *test, struct pcm_feeder *stop) -{ - u_int32_t visited[MAXFEEDERS / 32]; - u_int32_t idx, mask; - - bzero(visited, sizeof(visited)); - while (test && (test != stop)) { - idx = test->desc->idx; - if (idx < 0) - panic("bad idx %d", idx); - if (idx >= MAXFEEDERS) - panic("bad idx %d", idx); - mask = 1 << (idx & 31); - idx >>= 5; - if (visited[idx] & mask) - return 0; - visited[idx] |= mask; - test = test->source; - } - - return 1; -} - -/* - * See feeder_fmtchain() for the mumbo-jumbo ridiculous explanation - * of what the heck is this FMT_Q_* - */ -#define FMT_Q_UP 1 -#define FMT_Q_DOWN 2 -#define FMT_Q_EQ 3 -#define FMT_Q_MULTI 4 - /* * 14bit format scoring * -------------------- @@ -505,309 +431,6 @@ return best2; } -static struct pcm_feeder * -feeder_fmtchain(u_int32_t *to, struct pcm_feeder *source, struct pcm_feeder *stop, int maxdepth) -{ - struct feedertab_entry *fte, *ftebest; - struct pcm_feeder *try, *ret; - uint32_t fl, qout, qsrc, qdst; - int qtype; - - if (to == NULL || to[0] == 0) - return NULL; - - DEB(printf("trying %s (0x%08x -> 0x%08x)...\n", source->class->name, source->desc->in, source->desc->out)); - if (fmtvalid(source->desc->out, to)) { - DEB(printf("got it\n")); - return source; - } - - if (maxdepth < 0) - return NULL; - - /* - * WARNING: THIS IS _NOT_ FOR THE FAINT HEART - * Disclaimer: I don't expect anybody could understand this - * without deep logical and mathematical analysis - * involving various unnamed probability theorem. - * - * This "Best Fit Random Chain Selection" (BLEHBLEHWHATEVER) algorithm - * is **extremely** difficult to digest especially when applied to - * large sets / numbers of random chains (feeders), each with - * unique characteristic providing different sets of in/out format. - * - * Basically, our FEEDER_FMT (see feeder_fmt.c) chains characteristic: - * 1) Format chains - * 1.1 "8bit to any, not to 8bit" - * 1.1.1 sign can remain consistent, e.g: u8 -> u16[le|be] - * 1.1.2 sign can be changed, e.g: u8 -> s16[le|be] - * 1.1.3 endian can be changed, e.g: u8 -> u16[le|be] - * 1.1.4 both can be changed, e.g: u8 -> [u|s]16[le|be] - * 1.2 "Any to 8bit, not from 8bit" - * 1.2.1 sign can remain consistent, e.g: s16le -> s8 - * 1.2.2 sign can be changed, e.g: s16le -> u8 - * 1.2.3 source endian can be anything e.g: s16[le|be] -> s8 - * 1.2.4 source endian / sign can be anything e.g: [u|s]16[le|be] -> u8 - * 1.3 "Any to any where BOTH input and output either 8bit or non-8bit" - * 1.3.1 endian MUST remain consistent - * 1.3.2 sign CAN be changed - * 1.4 "Long jump" is allowed, e.g: from 16bit to 32bit, excluding - * 16bit to 24bit . - * 2) Channel chains (mono <-> stereo) - * 2.1 Both endian and sign MUST remain consistent - * 3) Endian chains (big endian <-> little endian) - * 3.1 Channels and sign MUST remain consistent - * 4) Sign chains (signed <-> unsigned) - * 4.1 Channels and endian MUST remain consistent - * - * .. and the mother of all chaining rules: - * - * Rules 0: Source and destination MUST not contain multiple selections. - * (qtype != FMT_Q_MULTI) - * - * First of all, our caller ( chn_fmtchain() ) will reduce the possible - * multiple from/to formats to a single best format using chn_fmtbest(). - * Then, using chn_fmtscore(), we determine the chaining characteristic. - * Our main goal is to narrow it down until it reach FMT_Q_EQ chaining - * type while still adhering above chaining rules. - * - * The need for this complicated chaining procedures is inevitable, - * since currently we have more than 200 different types of FEEDER_FMT - * doing various unique format conversion. Without this (the old way), - * it is possible to generate broken chain since it doesn't do any - * sanity checking to ensure that the output format is "properly aligned" - * with the direction of conversion (quality up/down/equal). - * - * Conversion: s24le to s32le - * Possible chain: 1) s24le -> s32le (correct, optimized) - * 2) s24le -> s16le -> s32le - * (since we have feeder_24to16 and feeder_16to32) - * +-- obviously broken! - * - * Using scoring mechanisme, this will ensure that the chaining - * process do the right thing, or at least, give the best chain - * possible without causing quality (the 'Q') degradation. - */ - - qdst = chn_fmtscore(to[0]); - qsrc = chn_fmtscore(source->desc->out); - -#define score_q(s1) score_val(s1) -#define score_8bit(s1) ((s1) & 0x700) -#define score_non8bit(s1) (!score_8bit(s1)) -#define score_across8bit(s1, s2) ((score_8bit(s1) && score_non8bit(s2)) || \ - (score_8bit(s2) && score_non8bit(s1))) - -#define FMT_CHAIN_Q_UP(s1, s2) (score_q(s1) < score_q(s2)) -#define FMT_CHAIN_Q_DOWN(s1, s2) (score_q(s1) > score_q(s2)) -#define FMT_CHAIN_Q_EQ(s1, s2) (score_q(s1) == score_q(s2)) -#define FMT_Q_DOWN_FLAGS(s1, s2) (0x1 | (score_across8bit(s1, s2) ? \ - 0x2 : 0x0)) -#define FMT_Q_UP_FLAGS(s1, s2) FMT_Q_DOWN_FLAGS(s1, s2) -#define FMT_Q_EQ_FLAGS(s1, s2) (0x3ffc | \ - ((score_cheq(s1, s2) && \ - score_endianeq(s1, s2)) ? \ - 0x1 : 0x0) | \ - ((score_cheq(s1, s2) && \ - score_signeq(s1, s2)) ? \ - 0x2 : 0x0)) - - /* Determine chaining direction and set matching flag */ - fl = 0x3fff; - if (to[1] != 0) { - qtype = FMT_Q_MULTI; - printf("%s: WARNING: FMT_Q_MULTI chaining. Expect the unexpected.\n", __func__); - } else if (FMT_CHAIN_Q_DOWN(qsrc, qdst)) { - qtype = FMT_Q_DOWN; - fl = FMT_Q_DOWN_FLAGS(qsrc, qdst); - } else if (FMT_CHAIN_Q_UP(qsrc, qdst)) { - qtype = FMT_Q_UP; - fl = FMT_Q_UP_FLAGS(qsrc, qdst); - } else { - qtype = FMT_Q_EQ; - fl = FMT_Q_EQ_FLAGS(qsrc, qdst); - } - - ftebest = NULL; - - SLIST_FOREACH(fte, &feedertab, link) { - if (fte->desc == NULL) - continue; - if (fte->desc->type != FEEDER_FMT) - continue; - qout = ch