diff --git a/docs/devices.md b/docs/devices.md index 435a6a33..10dfa13f 100644 --- a/docs/devices.md +++ b/docs/devices.md @@ -211,8 +211,8 @@ setting is useful only to disable smooth scrolling. | MK550 | | | | | | MK700 | 2008 | 1.0 | yes | FN swap, reprog keys | | MK710 | | 1.0 | yes | FN swap, reprog keys | -| EX100 keyboard | 6500 | 1.0 | yes | | -| EX100 mouse | 3f00 | 1.0 | yes | | +| EX100 keyboard | 0065 | 1.0 | yes | | +| EX100 mouse | 003f | 1.0 | yes | | * The EX100 is old, preunifying set, supporting only part of HID++ 1.0 features diff --git a/lib/hidapi/__init__.py b/lib/hidapi/__init__.py index 4202b676..3f0eaded 100644 --- a/lib/hidapi/__init__.py +++ b/lib/hidapi/__init__.py @@ -23,6 +23,7 @@ from __future__ import absolute_import, division, print_function, unicode_litera from hidapi.udev import close # noqa: F401 from hidapi.udev import enumerate # noqa: F401 from hidapi.udev import find_paired_node # noqa: F401 +from hidapi.udev import find_paired_node_wpid # noqa: F401 from hidapi.udev import get_manufacturer # noqa: F401 from hidapi.udev import get_product # noqa: F401 from hidapi.udev import get_serial # noqa: F401 diff --git a/lib/hidapi/udev.py b/lib/hidapi/udev.py index be7146bc..b5ace0fe 100644 --- a/lib/hidapi/udev.py +++ b/lib/hidapi/udev.py @@ -176,6 +176,27 @@ def find_paired_node(receiver_path, index, timeout): return None +def find_paired_node_wpid(receiver_path, index): + """Find the node of a device paired with a receiver, get wpid from udev""" + context = _Context() + receiver_phys = _Devices.from_device_file(context, receiver_path).find_parent('hid').get('HID_PHYS') + + if not receiver_phys: + return None + + phys = f'{receiver_phys}:{index}' + for dev in context.list_devices(subsystem='hidraw'): + dev_phys = dev.find_parent('hid').get('HID_PHYS') + if dev_phys and dev_phys == phys: + # get hid id like 0003:0000046D:00000065 + hid_id = dev.find_parent('hid').get('HID_ID') + # get wpid - last 4 symbols + udev_wpid = hid_id[-4:] + return udev_wpid + + return None + + def monitor_glib(callback, *device_filters): from gi.repository import GLib diff --git a/lib/logitech_receiver/base_usb.py b/lib/logitech_receiver/base_usb.py index edfbe0f2..1ff3b0c6 100644 --- a/lib/logitech_receiver/base_usb.py +++ b/lib/logitech_receiver/base_usb.py @@ -27,7 +27,7 @@ from __future__ import absolute_import, division, print_function, unicode_litera ## should this last be changed so that may_unpair is used for all receivers? writing to _R.receiver_pairing doesn't seem right # re_pairs determines whether a receiver pairs by replacing existing pairings, default to False ## currently only one receiver is so marked - should there be more? -# ex100_wpid_fix enable workarounds for EX100 and possible other old 27Mhz receivers +# ex100_27mhz_wpid_fix enable workarounds for EX100 and possible other old 27Mhz receivers _DRIVER = ('hid-generic', 'generic-usb', 'logitech-djreceiver') @@ -96,7 +96,7 @@ _ex100_receiver = lambda product_id: { 'max_devices': 4, 'may_unpair': False, 're_pairs': True, - 'ex100_wpid_fix': True + 'ex100_27mhz_wpid_fix': True } _wired_device = lambda product_id: {'vendor_id': 0x046d, 'product_id': product_id, 'usb_interface': 2} diff --git a/lib/logitech_receiver/descriptors.py b/lib/logitech_receiver/descriptors.py index 1e5164a2..3e7fca82 100644 --- a/lib/logitech_receiver/descriptors.py +++ b/lib/logitech_receiver/descriptors.py @@ -169,7 +169,7 @@ _D( _D( 'Wireless Keyboard MK300', protocol=1.0, - wpid='8521', + wpid='0068', registers=(_R.battery_status, ), ) @@ -292,14 +292,14 @@ _D( 'Wireless Keyboard S510', codename='S510', protocol=1.0, - wpid='3622', + wpid='0056', registers=(_R.battery_status, ), ) _D( 'Wireless Keyboard EX100', codename='EX100', protocol=1.0, - wpid='6500', + wpid='0065', registers=(_R.battery_status, ), ) @@ -523,21 +523,21 @@ _D( 'LX5 Cordless Mouse', codename='LX5', protocol=1.0, - wpid='5612', + wpid='0036', registers=(_R.battery_status, ), ) _D( 'Wireless Mouse M30', codename='M30', protocol=1.0, - wpid='6822', + wpid='0085', registers=(_R.battery_status, ), ) _D( 'Wireless Mouse EX100', codename='EX100m', protocol=1.0, - wpid='3F00', + wpid='003F', registers=(_R.battery_status, ), # settings=[ _RS.smooth_scroll(), ], # command accepted, but no change in whell action ) diff --git a/lib/logitech_receiver/device.py b/lib/logitech_receiver/device.py index bfa12672..d00738db 100644 --- a/lib/logitech_receiver/device.py +++ b/lib/logitech_receiver/device.py @@ -81,9 +81,13 @@ class Device(object): self.wpid = _strhex(link_notification.data[2:3] + link_notification.data[1:2]) # assert link_notification.address == (0x04 if unifying else 0x03) kind = ord(link_notification.data[0:1]) & 0x0F + # get 27Mhz wpid and set kind based on index + if receiver.ex100_27mhz_wpid_fix: # 27 Mhz receiver + self.wpid = '00' + _strhex(link_notification.data[2:3]) + kind = self.get_kind_from_index(number, receiver) self._kind = _hidpp10.DEVICE_KIND[kind] else: - # force a reading of the wpid + # Not a notification, force a reading of the wpid pair_info = self.receiver.read_register(_R.receiver_info, 0x20 + number - 1) if pair_info: # may be either a Unifying receiver, or an Unifying-ready @@ -91,20 +95,14 @@ class Device(object): self.wpid = _strhex(pair_info[3:5]) kind = ord(pair_info[7:8]) & 0x0F self._kind = _hidpp10.DEVICE_KIND[kind] - elif receiver.ex100_wpid_fix: - # ex100 receiver, fill fake device_info with known wpid's - # accordingly to drivers/hid/hid-logitech-dj.c - # index 1 or 2 always mouse, index 3 always the keyboard, - # index 4 is used for an optional separate numpad - if number == 1: # mouse - self.wpid = '3F00' - self._kind = _hidpp10.DEVICE_KIND[2] - elif number == 3: # keyboard - self.wpid = '6500' - self._kind = _hidpp10.DEVICE_KIND[1] - else: # unknown device number on EX100 - _log.error('failed to set fake EX100 wpid for device %d of %s', number, receiver) - raise _base.NoSuchDevice(number=number, receiver=receiver, error='Unknown EX100 device') + elif receiver.ex100_27mhz_wpid_fix: + # 27Mhz receiver, fill extracting WPID from udev path + self.wpid = _hid.find_paired_node_wpid(receiver.path, number) + if not self.wpid: + _log.error('Unable to get wpid from udev for device %d of %s', number, receiver) + raise _base.NoSuchDevice(number=number, receiver=receiver, error='Not present 27Mhz device') + kind = self.get_kind_from_index(number, receiver) + self._kind = _hidpp10.DEVICE_KIND[kind] else: # unifying protocol not supported, must be a Nano receiver device_info = self.receiver.read_register(_R.receiver_info, 0x04) @@ -289,6 +287,25 @@ class Device(object): self._feature_settings_checked = _check_feature_settings(self, self._settings) return self._settings + def get_kind_from_index(self, index, receiver): + """Get device kind from 27Mhz device index""" + # accordingly to drivers/hid/hid-logitech-dj.c + # index 1 or 2 always mouse, index 3 always the keyboard, + # index 4 is used for an optional separate numpad + + if index == 1: # mouse + kind = 2 + elif index == 2: # mouse + kind = 2 + elif index == 3: # keyboard + kind = 1 + elif index == 4: # numpad + kind = 3 + else: # unknown device number on 27Mhz receiver + _log.error('failed to calculate device kind for device %d of %s', index, receiver) + raise _base.NoSuchDevice(number=index, receiver=receiver, error='Unknown 27Mhz device number') + return kind + def enable_notifications(self, enable=True): """Enable or disable device (dis)connection notifications on this receiver.""" diff --git a/lib/logitech_receiver/notifications.py b/lib/logitech_receiver/notifications.py index 5baa69ea..a5dddefb 100644 --- a/lib/logitech_receiver/notifications.py +++ b/lib/logitech_receiver/notifications.py @@ -203,6 +203,9 @@ def _process_hidpp10_notification(device, status, n): ) if protocol_name: wpid = _strhex(n.data[2:3] + n.data[1:2]) + # workaround for short EX100 and other 27 MHz wpids + if protocol_name == '27 MHz': + wpid = '00' + _strhex(n.data[2:3]) if wpid != device.wpid: _log.warn('%s wpid mismatch, got %s', device, wpid) flags = ord(n.data[:1]) & 0xF0 diff --git a/lib/logitech_receiver/receiver.py b/lib/logitech_receiver/receiver.py index 4db61af2..f2ccce27 100644 --- a/lib/logitech_receiver/receiver.py +++ b/lib/logitech_receiver/receiver.py @@ -79,7 +79,7 @@ class Receiver(object): self._str = '<%s(%s,%s%s)>' % ( self.name.replace(' ', ''), self.path, '' if isinstance(self.handle, int) else 'T', self.handle ) - self.ex100_wpid_fix = product_info.get('ex100_wpid_fix', False) + self.ex100_27mhz_wpid_fix = product_info.get('ex100_27mhz_wpid_fix', False) self._firmware = None self._devices = {}