From 91d0cfaa0e5d4ae4689a373b6a286cde1e7c9be6 Mon Sep 17 00:00:00 2001 From: "Peter F. Patel-Schneider" Date: Sat, 27 Jan 2024 18:20:33 -0500 Subject: [PATCH] device: add extended report rate setting --- lib/logitech_receiver/device.py | 2 +- lib/logitech_receiver/hidpp20.py | 8 ++- lib/logitech_receiver/receiver.py | 2 +- lib/logitech_receiver/settings_templates.py | 62 ++++++++++++++++++--- lib/solaar/cli/show.py | 6 +- lib/solaar/ui/window.py | 7 +-- 6 files changed, 68 insertions(+), 19 deletions(-) diff --git a/lib/logitech_receiver/device.py b/lib/logitech_receiver/device.py index dc21c17815..e87582669c 100644 --- a/lib/logitech_receiver/device.py +++ b/lib/logitech_receiver/device.py @@ -236,7 +236,7 @@ def update_pairing_information(self): if not self._kind: self._kind = kind if not self._polling_rate: - self._polling_rate = polling_rate + self._polling_rate = str(polling_rate) + 'ms' def update_extended_pairing_information(self): if self.receiver: diff --git a/lib/logitech_receiver/hidpp20.py b/lib/logitech_receiver/hidpp20.py index 076dbc4d49..aaef773bf4 100644 --- a/lib/logitech_receiver/hidpp20.py +++ b/lib/logitech_receiver/hidpp20.py @@ -1555,7 +1555,13 @@ def get_polling_rate(device): state = feature_request(device, FEATURE.REPORT_RATE, 0x10) if state: rate = _unpack('!B', state[:1])[0] - return rate + return str(rate) + 'ms' + else: + rates = ['8ms', '4ms', '2ms', '1ms', '500us', '250us', '125us'] + state = feature_request(device, FEATURE.EXTENDED_ADJUSTABLE_REPORT_RATE, 0x20) + if state: + rate = _unpack('!B', state[:1])[0] + return rates[rate] def get_remaining_pairing(device): diff --git a/lib/logitech_receiver/receiver.py b/lib/logitech_receiver/receiver.py index c0ea3857fa..2f32747fb3 100644 --- a/lib/logitech_receiver/receiver.py +++ b/lib/logitech_receiver/receiver.py @@ -168,7 +168,7 @@ def device_pairing_information(self, n): if pair_info: # may be either a Unifying receiver, or an Unifying-ready receiver wpid = _strhex(pair_info[3:5]) kind = _hidpp10.DEVICE_KIND[ord(pair_info[7:8]) & 0x0F] - polling_rate = ord(pair_info[2:3]) + polling_rate = str(ord(pair_info[2:3])) + 'ms' elif self.receiver_kind == '27Mz': # 27Mhz receiver, fill extracting WPID from udev path wpid = _hid.find_paired_node_wpid(self.path, n) if not wpid: diff --git a/lib/logitech_receiver/settings_templates.py b/lib/logitech_receiver/settings_templates.py index 5ecc71e00f..6941c61660 100644 --- a/lib/logitech_receiver/settings_templates.py +++ b/lib/logitech_receiver/settings_templates.py @@ -327,14 +327,19 @@ class OnboardProfiles(_Setting): class ReportRate(_Setting): name = 'report_rate' - label = _('Polling Rate (ms)') - description = ( - _('Frequency of device polling, in milliseconds') + '\n' + - _('May need Onboard Profiles set to Disable to be effective.') - ) + label = _('Polling Rate') + description = (_('Frequency of device polling') + '\n' + _('May need Onboard Profiles set to Disable to be effective.')) feature = _F.REPORT_RATE rw_options = {'read_fnid': 0x10, 'write_fnid': 0x20} - choices_universe = _NamedInts.range(1, 8) + choices_universe = _NamedInts() + choices_universe[1] = '1ms' + choices_universe[2] = '2ms' + choices_universe[3] = '3ms' + choices_universe[4] = '4ms' + choices_universe[5] = '5ms' + choices_universe[6] = '6ms' + choices_universe[7] = '7ms' + choices_universe[8] = '8ms' class _rw_class(_FeatureRW): # no longer needed - set Onboard Profiles to disable @@ -356,7 +361,49 @@ def build(cls, setting_class, device): rate_flags = _bytes2int(reply[0:1]) for i in range(0, 8): if (rate_flags >> i) & 0x01: - rate_list.append(i + 1) + rate_list.append(setting_class.choices_universe[i + 1]) + return cls(choices=_NamedInts.list(rate_list), byte_count=1) if rate_list else None + + +class ExtendedReportRate(_Setting): + name = 'report_rate_extended' + label = _('Polling Frequency') + description = (_('Frequency of device polling') + '\n' + _('May need Onboard Profiles set to Disable to be effective.')) + feature = _F.EXTENDED_ADJUSTABLE_REPORT_RATE + rw_options = {'read_fnid': 0x20, 'write_fnid': 0x30} + choices_universe = _NamedInts() + choices_universe[0] = '8ms' + choices_universe[1] = '4ms' + choices_universe[2] = '2ms' + choices_universe[3] = '1ms' + choices_universe[4] = '500us' + choices_universe[5] = '250us' + choices_universe[6] = '125us' + + class _rw_class(_FeatureRW): + + def read(self, device, data_bytes=b''): + # need connection type from device to get actual report rate + self.read_prefix = b'\x00' if device.receiver else b'\x01' + super().read(device, data_bytes) + + def write(self, device, data_bytes): + # Host mode is required for report rate to be adjustable + if _hidpp20.get_onboard_mode(device) != _hidpp20.ONBOARD_MODES.MODE_HOST: + _hidpp20.set_onboard_mode(device, _hidpp20.ONBOARD_MODES.MODE_HOST) + return super().write(device, data_bytes) + + class validator_class(_ChoicesV): + + @classmethod + def build(cls, setting_class, device): + reply = device.feature_request(_F.EXTENDED_ADJUSTABLE_REPORT_RATE, 0x10) + assert reply, 'Oops, report rate choices cannot be retrieved!' + rate_list = [] + rate_flags = _bytes2int(reply[0:2]) + for i in range(0, 6): + if (rate_flags & (0x01 << i)): + rate_list.append(setting_class.choices_universe[i]) return cls(choices=_NamedInts.list(rate_list), byte_count=1) if rate_list else None @@ -1222,6 +1269,7 @@ class ADCPower(_Setting): ThumbMode, # working OnboardProfiles, ReportRate, # working + ExtendedReportRate, PointerSpeed, # simple AdjustableDpi, # working SpeedChange, diff --git a/lib/solaar/cli/show.py b/lib/solaar/cli/show.py index 9d4e1fd0c5..3b9eaee3fa 100644 --- a/lib/solaar/cli/show.py +++ b/lib/solaar/cli/show.py @@ -102,7 +102,7 @@ def _print_device(dev, num=None): else: print(' Protocol : unknown (device is offline)') if dev.polling_rate: - print(' Polling rate :', dev.polling_rate, 'ms (%dHz)' % (1000 // dev.polling_rate)) + print(' Polling rate :', dev.polling_rate) print(' Serial number:', dev.serial) if dev.modelId: print(' Model ID: ', dev.modelId) @@ -217,8 +217,8 @@ def _print_device(dev, num=None): if ids: unitId, modelId, tid_map = ids print(' Unit ID: %s Model ID: %s Transport IDs: %s' % (unitId, modelId, tid_map)) - elif feature == _hidpp20.FEATURE.REPORT_RATE: - print(' Polling Rate (ms): %d' % _hidpp20.get_polling_rate(dev)) + elif feature == _hidpp20.FEATURE.REPORT_RATE or feature == _hidpp20.FEATURE.EXTENDED_ADJUSTABLE_REPORT_RATE: + print(' Polling Rate: %s' % _hidpp20.get_polling_rate(dev)) elif feature == _hidpp20.FEATURE.REMAINING_PAIRING: print(' Remaining Pairings: %d' % _hidpp20.get_remaining_pairing(dev)) elif feature == _hidpp20.FEATURE.ONBOARD_PROFILES: diff --git a/lib/solaar/ui/window.py b/lib/solaar/ui/window.py index d7541640e8..512e9ac672 100644 --- a/lib/solaar/ui/window.py +++ b/lib/solaar/ui/window.py @@ -550,12 +550,7 @@ def _details_items(device, read_all=False): hid_version = device.protocol yield (_('Protocol'), 'HID++ %1.1f' % hid_version if hid_version else _('Unknown')) if read_all and device.polling_rate: - yield ( - _('Polling rate'), _('%(rate)d ms (%(rate_hz)dHz)') % { - 'rate': device.polling_rate, - 'rate_hz': 1000 // device.polling_rate - } - ) + yield (_('Polling rate'), device.polling_rate) if read_all or not device.online: yield (_('Serial'), device.serial)