Record Centurion sub-device feature versions + version-gate AutoSleep
The Centurion sub-device discovery ignored the type/version bytes in
the per-index getFeatureId response and defaulted every feature's
version to 0. That made version-gated settings (sidetone, auto-sleep)
send V0 payload formats on features that may actually be V2/V3/V4 —
which the G522 rejects with OUT_OF_RANGE (error 0x03).
Evidence from tester's log (version acfd02ab):
bridge sub-device error: orig_feat_idx=13 orig_func=0x1B error=0x03
bridge sub-device error: orig_feat_idx=20 orig_func=0x1F error=0x03
feat_idx 13 is HeadsetAudioSidetone (0x0604); 20 is CenturionAutoSleep
(0x0108). Both have version-gated payload formats per the protocol doc.
Fixes:
1. In _discover_sub_device_features, extract response[3] (type) and
response[4] (version) and store them in self.version / self.flags,
so get_feature_version() returns the real version for downstream
callers.
2. Add HeadsetAutoSleep.build() that picks byte_count and max_value
based on the reported version (V<3=1 byte, V=3=2 bytes, V>=4=3).
HeadsetSidetone.build() already had version gating — it just wasn't
getting the real version before.
Does NOT address the mic-mute (error 0x0A) and mic-gain (error 0x0B)
rejections — those are non-standard error codes likely meaning the G522
either gates those behind the physical mic button (mic-mute) or rejects
writes for other reasons (mic-gain). Needs separate investigation once
the simpler version fix lands.
This commit is contained in:
parent
acfd02abe8
commit
4cc8ac0d46
|
|
@ -242,7 +242,11 @@ class FeaturesArray(dict):
|
|||
total_count = count_resp[0]
|
||||
logger.info("Centurion sub-device: FeatureSet reports %d features", total_count)
|
||||
|
||||
# Per-index query: GetFeatureId (function 1 = 0x10). Response: [remaining, feat_hi, feat_lo, type, flags].
|
||||
# Per-index query: GetFeatureId (function 1 = 0x10).
|
||||
# Response: [remaining, feat_hi, feat_lo, type, version].
|
||||
# We now also record `type` (flags) and `version` for each feature so
|
||||
# version-gated settings (sidetone, auto-sleep, etc.) can use the
|
||||
# correct payload format instead of defaulting to V0.
|
||||
sub_feat_idx = 0
|
||||
for idx in range(total_count):
|
||||
response = self.device.centurion_bridge_request(sub_fs_index, 0x10, idx)
|
||||
|
|
@ -250,6 +254,8 @@ class FeaturesArray(dict):
|
|||
logger.debug("Centurion sub-device: no response at index %d", idx)
|
||||
continue
|
||||
feat_id = struct.unpack("!H", response[1:3])[0]
|
||||
feat_type = response[3] if len(response) > 3 else 0
|
||||
feat_version = response[4] if len(response) > 4 else 0
|
||||
try:
|
||||
feature = SupportedFeature(feat_id)
|
||||
except ValueError:
|
||||
|
|
@ -259,8 +265,18 @@ class FeaturesArray(dict):
|
|||
dict.__setitem__(self, feature, sub_feat_idx)
|
||||
self.device._centurion_sub_features.add(feature)
|
||||
self.sub_inverse[sub_feat_idx] = feature
|
||||
if logger.isEnabledFor(logging.DEBUG):
|
||||
logger.debug("Centurion sub-device feature: %s at sub-index %d", feature, sub_feat_idx)
|
||||
# Record version/flags so downstream settings can version-gate their
|
||||
# payload format. get_feature_version(feature) reads self.version[feature].
|
||||
self.version[feature] = feat_version
|
||||
self.flags[feature] = feat_type
|
||||
if feat_version > 0 and logger.isEnabledFor(logging.DEBUG):
|
||||
logger.debug(
|
||||
"Centurion sub-device feature: %s at sub-index %d, version=%d, flags=0x%02X",
|
||||
feature,
|
||||
sub_feat_idx,
|
||||
feat_version,
|
||||
feat_type,
|
||||
)
|
||||
sub_feat_idx += 1
|
||||
self._sub_feature_count = sub_feat_idx
|
||||
logger.info("Centurion sub-device: discovered %d features total", sub_feat_idx)
|
||||
|
|
|
|||
|
|
@ -1690,9 +1690,27 @@ class HeadsetAutoSleep(settings.Setting):
|
|||
rw_options = {"read_fnid": 0x00, "write_fnid": 0x10}
|
||||
validator_class = settings_validator.RangeValidator
|
||||
min_value = 0
|
||||
max_value = 255
|
||||
# Timer byte count depends on feature version:
|
||||
# V<3 : 1 byte (0-255)
|
||||
# V=3 : 2 bytes (0-65535)
|
||||
# V>=4: 3 bytes (0-16777215)
|
||||
# build() picks the correct width based on the device's reported version.
|
||||
max_value = 0xFFFFFF
|
||||
validator_options = {"byte_count": 1}
|
||||
|
||||
@classmethod
|
||||
def build(cls, device):
|
||||
version = device.features.get_feature_version(cls.feature) or 0
|
||||
if version >= 4:
|
||||
byte_count, max_value = 3, 0xFFFFFF
|
||||
elif version >= 3:
|
||||
byte_count, max_value = 2, 0xFFFF
|
||||
else:
|
||||
byte_count, max_value = 1, 0xFF
|
||||
rw = settings.FeatureRW(cls.feature, **cls.rw_options)
|
||||
validator = settings_validator.RangeValidator(min_value=0, max_value=max_value, byte_count=byte_count)
|
||||
return cls(device, rw, validator)
|
||||
|
||||
|
||||
class HeadsetOnboardEQ(settings.RangeFieldSetting):
|
||||
name = "headset-onboard-eq"
|
||||
|
|
|
|||
Loading…
Reference in New Issue