In case of a TLV dB range with all items having raw value ranges strictly within the main raw value range reported by the driver, snd_tlv_convert_from_dB() returned one of the main raw range boundaries, which was outside all dB range items.
E.g.: static const unsigned int bd3753x_vol_fader_gain_att_tlv[] = { TLV_DB_RANGE_HEAD(2), 0, 0, TLV_DB_SCALE_ITEM(TLV_DB_GAIN_MUTE, 0, 1), 48, 142, TLV_DB_SCALE_ITEM(-7900, 100, 0), }; SOC_SINGLE_TLV("Main Volume", BD3753X_VOL_GAIN, 0, 255, 1, bd3753x_vol_fader_gain_att_tlv),
snd_tlv_convert_from_dB(tlv, 0, 255, 1500, &value, 1) returned 255 instead of the expected 142.
or: static const unsigned int max9850_tlv[] = { TLV_DB_RANGE_HEAD(4), 0x18, 0x1f, TLV_DB_SCALE_ITEM(-7450, 400, 0), 0x20, 0x33, TLV_DB_SCALE_ITEM(-4150, 200, 0), 0x34, 0x37, TLV_DB_SCALE_ITEM(-150, 100, 0), 0x38, 0x3f, TLV_DB_SCALE_ITEM(250, 50, 0), }; SOC_SINGLE_TLV("Headphone Volume", MAX9850_VOLUME, 0, 0x3f, 1, max9850_tlv),
snd_tlv_convert_from_dB(tlv, 0, 0x3f, -7450, &value, 0) returned 0 instead of the expected 0x18.
Signed-off-by: Benoît Thébaudeau benoit.thebaudeau@advansee.com
--- alsa-lib/src/control/tlv.c +++ alsa-lib/src/control/tlv.c @@ -291,41 +291,37 @@ int snd_tlv_convert_from_dB(unsigned int { switch (tlv[0]) { case SND_CTL_TLVT_DB_RANGE: { - long dbmin, dbmax, prev_rangemax; + long dbmin, dbmax, prev_submax; unsigned int pos, len; len = int_index(tlv[1]); - if (len > MAX_TLV_RANGE_SIZE) - return -EINVAL; - if (snd_tlv_get_dB_range(tlv, rangemin, rangemax, - &dbmin, &dbmax)) + if (len < 6 || len > MAX_TLV_RANGE_SIZE) return -EINVAL; - if (db_gain <= dbmin) { - *value = rangemin; - return 0; - } else if (db_gain >= dbmax) { - *value = rangemax; - return 0; - } pos = 2; - prev_rangemax = 0; - while (pos + 4 <= len) { - rangemin = (int)tlv[pos]; - rangemax = (int)tlv[pos + 1]; + prev_submax = 0; + do { + long submin, submax; + submin = (int)tlv[pos]; + submax = (int)tlv[pos + 1]; + if (rangemax < submax) + submax = rangemax; if (!snd_tlv_get_dB_range(tlv + pos + 2, - rangemin, rangemax, + submin, submax, &dbmin, &dbmax) && db_gain >= dbmin && db_gain <= dbmax) return snd_tlv_convert_from_dB(tlv + pos + 2, - rangemin, rangemax, + submin, submax, db_gain, value, xdir); else if (db_gain < dbmin) { - *value = xdir ? rangemin : prev_rangemax; + *value = xdir || pos == 2 ? submin : prev_submax; return 0; } - prev_rangemax = rangemax; + prev_submax = submax; + if (rangemax == submax) + break; pos += int_index(tlv[pos + 3]) + 4; - } - return -EINVAL; + } while (pos + 4 <= len); + *value = prev_submax; + return 0; } case SND_CTL_TLVT_DB_SCALE: { int min, step, max;