receiver: add support for EX100 keyboard/mouse and receiver (046d:c517)
docs: add EX100 keyboard/mouse combo device: add EX100 keyboard and mouse hidpp10: fix notification parsing, add device features flags, fix documentation links notifications: fix wpid processing for 27 MHz protocol cli: add display of device features flags for HID++ 1.0 devices
This commit is contained in:
parent
4dfa55c96c
commit
e436b1bd1d
|
@ -49,7 +49,7 @@ a tuple of known feature settings (from lib/logitech/settings_templates.py).
|
|||
|
||||
| USB ID | Kind | Max Paired Devices |
|
||||
------------|------------|--------------------|
|
||||
| 046d:c517 | Nano | 1 |
|
||||
| 046d:c517 | 27Mhz old | 2-4? |
|
||||
| 046d:c518 | Nano | 1 |
|
||||
| 046d:c51a | Nano | 1 |
|
||||
| 046d:c51b | Nano | 1 |
|
||||
|
@ -68,7 +68,8 @@ a tuple of known feature settings (from lib/logitech/settings_templates.py).
|
|||
| 064d:c53f | Lightspeed | 1 |
|
||||
| 17ef:6042 | Nano | 1 |
|
||||
|
||||
|
||||
* The receiver with usb Id 046d:c517 is old, 27 MHz receiver, supporting only
|
||||
subset of HID++ 1.0 protocol. Only hardware pairing supported.
|
||||
|
||||
### Keyboards (Unifying):
|
||||
|
||||
|
@ -215,7 +216,10 @@ 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 | |
|
||||
|
||||
* The EX100 is old, pre-unifying set, supporting only part of HID++ 1.0 features
|
||||
|
||||
[solaar]: https://github.com/pwr-Solaar/Solaar
|
||||
[logitech]: https://www.logitech.com
|
||||
|
|
|
@ -0,0 +1,120 @@
|
|||
./scan-registers.sh ff /dev/hidraw4
|
||||
# Old notification flags: 000100
|
||||
>> ( 0.035) [10 FF 8100 000100] '\x10\xff\x81\x00\x00\x01\x00'
|
||||
<< ( 0.015) [10 FF 8101 000000] '\x10\xff\x81\x01\x00\x00\x00'
|
||||
>> ( 0.020) [10 FF 8101 000200] '\x10\xff\x81\x01\x00\x02\x00'
|
||||
<< ( 0.030) [10 FF 8102 000000] '\x10\xff\x81\x02\x00\x00\x00'
|
||||
>> ( 0.036) [10 FF 8102 000200] '\x10\xff\x81\x02\x00\x02\x00'
|
||||
--
|
||||
<< ( 0.142) [10 FF 8109 000000] '\x10\xff\x81\t\x00\x00\x00'
|
||||
>> ( 0.148) [10 FF 8109 010000] '\x10\xff\x81\t\x01\x00\x00'
|
||||
--
|
||||
<< ( 1.790) [10 FF 8170 000000] '\x10\xff\x81p\x00\x00\x00'
|
||||
>> ( 1.796) [10 FF 8170 012100] '\x10\xff\x81p\x01!\x00'
|
||||
<< ( 1.806) [10 FF 8171 000000] '\x10\xff\x81q\x00\x00\x00'
|
||||
>> ( 1.812) [10 FF 8171 011200] '\x10\xff\x81q\x01\x12\x00'
|
||||
--
|
||||
<< ( 1.838) [10 FF 8173 000000] '\x10\xff\x81s\x00\x00\x00'
|
||||
>> ( 1.844) [10 FF 8173 643F00] '\x10\xff\x81sd?\x00'
|
||||
--
|
||||
<< ( 2.046) [10 FF 8180 000000] '\x10\xff\x81\x80\x00\x00\x00'
|
||||
>> ( 2.052) [10 FF 8180 030000] '\x10\xff\x81\x80\x03\x00\x00'
|
||||
--
|
||||
<< ( 3.326) [10 FF 81D0 000000] '\x10\xff\x81\xd0\x00\x00\x00'
|
||||
>> ( 3.332) [10 FF 81D0 000000] '\x10\xff\x81\xd0\x00\x00\x00'
|
||||
|
||||
devices
|
||||
01 mouse
|
||||
Red button pressed
|
||||
>> ( 1676.106) [10 01 0810 000000] '\x10\x01\x08\x10\x00\x00\x00'
|
||||
>> ( 1676.114) [10 01 4600 000021] '\x10\x01F\x00\x00\x00!'
|
||||
>> ( 1676.122) [10 FF 4600 211100] '\x10\xffF\x00!\x11\x00'
|
||||
|
||||
Power lewel?
|
||||
?? Input: 10 01 81 07 00 00 00
|
||||
<< ( 1739.032) [10 01 8107 000000] '\x10\x01\x81\x07\x00\x00\x00'
|
||||
>> ( 1739.040) [10 01 8107 030000] '\x10\x01\x81\x07\x03\x00\x00'
|
||||
[10 01 8107 070000] '\x10\x01\x81\x07\x07\x00\x00'
|
||||
|
||||
power change
|
||||
>> ( 2441.563) [10 01 0703 000000] '\x10\x01\x07\x03\x00\x00\x00'
|
||||
>> ( 100.159) [10 01 0707 000000] '\x10\x01\x07\x07\x00\x00\x00'
|
||||
|
||||
enable power event
|
||||
<< ( 59.190) [10 01 8000 100000] '\x10\x01\x80\x00\x10\x00\x00'
|
||||
>> ( 59.193) [10 01 8000 000000] '\x10\x01\x80\x00\x00\x00\x00'
|
||||
|
||||
|
||||
03 keyboard
|
||||
|
||||
Power level?
|
||||
?? Input: 10 03 81 07 00 00 00
|
||||
<< ( 1777.961) [10 03 8107 000000] '\x10\x03\x81\x07\x00\x00\x00'
|
||||
>> ( 1777.967) [10 03 8107 070000] '\x10\x03\x81\x07\x07\x00\x00'
|
||||
|
||||
power on
|
||||
>> ( 1571.805) [10 03 0810 000000] '\x10\x03\x08\x10\x00\x00\x00'
|
||||
>> ( 1574.709) [10 03 0800 000000] '\x10\x03\x08\x00\x00\x00\x00'
|
||||
|
||||
red button pressed
|
||||
>> ( 1619.043) [10 03 0810 000000] '\x10\x03\x08\x10\x00\x00\x00'
|
||||
>> ( 1619.051) [10 03 4600 000011] '\x10\x03F\x00\x00\x00\x11'
|
||||
>> ( 1619.059) [10 FF 4600 221100] '\x10\xffF\x00"\x11\x00'
|
||||
>> ( 1621.747) [10 03 0800 000000] '\x10\x03\x08\x00\x00\x00\x00'
|
||||
|
||||
Fn pressed
|
||||
>> ( 1651.715) [10 03 032C 100000] '\x10\x03\x03,\x10\x00\x00'
|
||||
>> ( 1652.170) [10 03 0300 000000] '\x10\x03\x03\x00\x00\x00\x00'
|
||||
|
||||
|
||||
$ bin/solaar probe
|
||||
Nano Receiver
|
||||
Device path : /dev/hidraw3
|
||||
USB id : 046d:c517
|
||||
Serial : None
|
||||
Has 2 paired device(s) out of a maximum of 6.
|
||||
Notifications: wireless (0x000100)
|
||||
Register Dump
|
||||
Notification Register 0x00: 0x000100
|
||||
Connection State 0x02: 0x000200
|
||||
Device Activity 0xb3: None
|
||||
Pairing Register 0xb5 0x00: None
|
||||
Pairing Register 0xb5 0x10: None
|
||||
Pairing Register 0xb5 0x20: None
|
||||
Pairing Register 0xb5 0x30: None
|
||||
Pairing Name 0xb5 0x40: None
|
||||
Pairing Register 0xb5 0x01: None
|
||||
Pairing Register 0xb5 0x11: None
|
||||
Pairing Register 0xb5 0x21: None
|
||||
Pairing Register 0xb5 0x31: None
|
||||
Pairing Name 0xb5 0x41: None
|
||||
Pairing Register 0xb5 0x02: None
|
||||
Pairing Register 0xb5 0x12: None
|
||||
Pairing Register 0xb5 0x22: None
|
||||
Pairing Register 0xb5 0x32: None
|
||||
Pairing Name 0xb5 0x42: None
|
||||
Pairing Register 0xb5 0x03: None
|
||||
Pairing Register 0xb5 0x13: None
|
||||
Pairing Register 0xb5 0x23: None
|
||||
Pairing Register 0xb5 0x33: None
|
||||
Pairing Name 0xb5 0x43: None
|
||||
Pairing Register 0xb5 0x04: None
|
||||
Pairing Register 0xb5 0x14: None
|
||||
Pairing Register 0xb5 0x24: None
|
||||
Pairing Register 0xb5 0x34: None
|
||||
Pairing Name 0xb5 0x44: None
|
||||
Pairing Register 0xb5 0x05: None
|
||||
Pairing Register 0xb5 0x15: None
|
||||
Pairing Register 0xb5 0x25: None
|
||||
Pairing Register 0xb5 0x35: None
|
||||
Pairing Name 0xb5 0x45: None
|
||||
Firmware 0xf1 0x00: None
|
||||
Firmware 0xf1 0x01: None
|
||||
Firmware 0xf1 0x02: None
|
||||
Firmware 0xf1 0x03: None
|
||||
Firmware 0xf1 0x04: None
|
||||
|
||||
Battery status:
|
||||
1.9V critical
|
||||
2.3V low
|
||||
2.5V full
|
|
@ -27,6 +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
|
||||
|
||||
_DRIVER = ('hid-generic', 'generic-usb', 'logitech-djreceiver')
|
||||
|
||||
|
@ -86,6 +87,18 @@ _lightspeed_receiver = lambda product_id: {
|
|||
'name': 'Lightspeed Receiver'
|
||||
}
|
||||
|
||||
_ex100_receiver = lambda product_id: {
|
||||
'vendor_id': 0x046d,
|
||||
'product_id': product_id,
|
||||
'usb_interface': 1,
|
||||
'hid_driver': _DRIVER, # noqa: F821
|
||||
'name': 'EX100 Receiver 27 Mhz',
|
||||
'max_devices': 4,
|
||||
'may_unpair': False,
|
||||
're_pairs': True,
|
||||
'ex100_wpid_fix': True
|
||||
}
|
||||
|
||||
# standard Unifying receivers (marked with the orange Unifying logo)
|
||||
UNIFYING_RECEIVER_C52B = _unifying_receiver(0xc52b)
|
||||
UNIFYING_RECEIVER_C532 = _unifying_receiver(0xc532)
|
||||
|
@ -93,8 +106,10 @@ UNIFYING_RECEIVER_C532 = _unifying_receiver(0xc532)
|
|||
# Nano receviers that support the Unifying protocol
|
||||
NANO_RECEIVER_ADVANCED = _nano_receiver(0xc52f)
|
||||
|
||||
# ex100 old style receiver pre-unifyimg protocol
|
||||
EX100_27MHZ_RECEIVER_C517 = _ex100_receiver(0xc517)
|
||||
|
||||
# Nano receivers that don't support the Unifying protocol
|
||||
NANO_RECEIVER_C517 = _nano_receiver_maxn(0xc517, 6)
|
||||
NANO_RECEIVER_C518 = _nano_receiver(0xc518)
|
||||
NANO_RECEIVER_C51A = _nano_receiver(0xc51a)
|
||||
NANO_RECEIVER_C51B = _nano_receiver(0xc51b)
|
||||
|
@ -119,7 +134,7 @@ ALL = (
|
|||
UNIFYING_RECEIVER_C52B,
|
||||
UNIFYING_RECEIVER_C532,
|
||||
NANO_RECEIVER_ADVANCED,
|
||||
NANO_RECEIVER_C517,
|
||||
EX100_27MHZ_RECEIVER_C517,
|
||||
NANO_RECEIVER_C518,
|
||||
NANO_RECEIVER_C51A,
|
||||
NANO_RECEIVER_C51B,
|
||||
|
|
|
@ -293,6 +293,13 @@ _D(
|
|||
wpid='3622',
|
||||
registers=(_R.battery_status, ),
|
||||
)
|
||||
_D(
|
||||
'Wireless Keyboard EX100',
|
||||
codename='EX100',
|
||||
protocol=1.0,
|
||||
wpid='6500',
|
||||
registers=(_R.battery_status, ),
|
||||
)
|
||||
|
||||
# Mice
|
||||
|
||||
|
@ -523,6 +530,14 @@ _D(
|
|||
wpid='6822',
|
||||
registers=(_R.battery_status, ),
|
||||
)
|
||||
_D(
|
||||
'Wireless Mouse EX100',
|
||||
codename='EX100m',
|
||||
protocol=1.0,
|
||||
wpid='3F00',
|
||||
registers=(_R.battery_status, ),
|
||||
# settings=[ _RS.smooth_scroll(), ], # command accepted, but no change in whell action
|
||||
)
|
||||
|
||||
# Trackballs
|
||||
|
||||
|
|
|
@ -64,14 +64,26 @@ POWER_SWITCH_LOCATION = _NamedInts(
|
|||
# - the rest work only on devices as far as we can tell right now
|
||||
# In the future would be useful to have separate enums for receiver and device notification flags,
|
||||
# but right now we don't know enough.
|
||||
# additional flags taken from https://drive.google.com/file/d/0BxbRzx7vEV7eNDBheWY0UHM5dEU/view?usp=sharing
|
||||
NOTIFICATION_FLAG = _NamedInts(
|
||||
numpad_numerical_keys=0x800000,
|
||||
f_lock_status=0x400000,
|
||||
roller_H=0x200000,
|
||||
battery_status=0x100000, # send battery charge notifications (0x07 or 0x0D)
|
||||
mouse_extra_buttons=0x080000,
|
||||
roller_V=0x040000,
|
||||
keyboard_sleep_raw=0x020000, # system control keys such as Sleep
|
||||
keyboard_multimedia_raw=0x010000, # consumer controls such as Mute and Calculator
|
||||
# reserved_r1b4= 0x001000, # unknown, seen on a unifying receiver
|
||||
reserved5=0x008000,
|
||||
reserved4=0x004000,
|
||||
reserved3=0x002000,
|
||||
reserved2=0x001000,
|
||||
software_present=0x000800, # .. no idea
|
||||
reserved1=0x000400,
|
||||
keyboard_illumination=0x000200, # illumination brightness level changes (by pressing keys)
|
||||
wireless=0x000100, # notify when the device wireless goes on/off-line
|
||||
mx_air_3d_gesture=0x000001,
|
||||
)
|
||||
|
||||
ERROR = _NamedInts(
|
||||
|
@ -116,6 +128,25 @@ REGISTERS = _NamedInts(
|
|||
notifications=0x00,
|
||||
firmware=0xF1,
|
||||
)
|
||||
# Flags taken from https://drive.google.com/file/d/0BxbRzx7vEV7eNDBheWY0UHM5dEU/view?usp=sharing
|
||||
DEVICE_FEATURES = _NamedInts(
|
||||
reserved1=0x010000,
|
||||
special_buttons=0x020000,
|
||||
enhanced_key_usage=0x040000,
|
||||
fast_fw_rev=0x080000,
|
||||
reserved2=0x100000,
|
||||
reserved3=0x200000,
|
||||
scroll_accel=0x400000,
|
||||
buttons_control_resolution=0x800000,
|
||||
inhibit_lock_key_sound=0x000001,
|
||||
reserved4=0x000002,
|
||||
mx_air_3d_engine=0x000004,
|
||||
host_control_leds=0x000008,
|
||||
reserved5=0x000010,
|
||||
reserved6=0x000020,
|
||||
reserved7=0x000040,
|
||||
reserved8=0x000080,
|
||||
)
|
||||
|
||||
#
|
||||
# functions
|
||||
|
@ -316,3 +347,19 @@ def set_notification_flags(device, *flag_bits):
|
|||
assert flag_bits & 0x00FFFFFF == flag_bits
|
||||
result = write_register(device, REGISTERS.notifications, _int2bytes(flag_bits, 3))
|
||||
return result is not None
|
||||
|
||||
|
||||
def get_device_features(device):
|
||||
assert device
|
||||
|
||||
# Avoid a call if the device is not online,
|
||||
# or the device does not support registers.
|
||||
if device.kind is not None:
|
||||
# peripherals with protocol >= 2.0 don't support registers
|
||||
if device.protocol and device.protocol >= 2.0:
|
||||
return
|
||||
|
||||
flags = read_register(device, REGISTERS.mouse_button_flags)
|
||||
if flags is not None:
|
||||
assert len(flags) == 3
|
||||
return _bytes2int(flags)
|
||||
|
|
|
@ -160,8 +160,8 @@ def _process_hidpp10_custom_notification(device, status, n):
|
|||
# message layout: 10 ix <register> <xx> <yy> <zz> <00>
|
||||
assert n.data[-1:] == b'\x00'
|
||||
data = chr(n.address).encode() + n.data
|
||||
charge, status_text = _hidpp10.parse_battery_status(n.sub_id, data)
|
||||
status.set_battery_info(charge, status_text, None)
|
||||
charge, status_text, next_charge = _hidpp10.parse_battery_status(n.sub_id, data)
|
||||
status.set_battery_info(charge, status_text, next_charge)
|
||||
return True
|
||||
|
||||
if n.sub_id == _R.keyboard_illumination:
|
||||
|
@ -201,6 +201,9 @@ def _process_hidpp10_notification(device, status, n):
|
|||
if protocol_name:
|
||||
if _log.isEnabledFor(_DEBUG):
|
||||
wpid = _strhex(n.data[2:3] + n.data[1:2])
|
||||
# workaround for short EX100 wpids
|
||||
if protocol_name == '27 MHz':
|
||||
wpid = _strhex(n.data[2:3]) + '00'
|
||||
assert wpid == device.wpid, '%s wpid mismatch, got %s' % (device, wpid)
|
||||
|
||||
flags = ord(n.data[:1]) & 0xF0
|
||||
|
|
|
@ -88,6 +88,14 @@ class PairedDevice(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
|
||||
# fix EX100 wpid
|
||||
if receiver.ex100_wpid_fix: # EX100 receiver
|
||||
self.wpid = _strhex(link_notification.data[2:3]) + '00'
|
||||
# workaround for EX100 switched kind
|
||||
if self.wpid == '3F00':
|
||||
kind = 2
|
||||
if self.wpid == '6500':
|
||||
kind = 1
|
||||
self._kind = _hidpp10.DEVICE_KIND[kind]
|
||||
else:
|
||||
# force a reading of the wpid
|
||||
|
@ -97,6 +105,20 @@ class PairedDevice(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')
|
||||
else:
|
||||
# unifying protocol not supported, must be a Nano receiver
|
||||
device_info = self.receiver.read_register(_R.receiver_info, 0x04)
|
||||
|
@ -365,6 +387,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._firmware = None
|
||||
self._devices = {}
|
||||
|
|
|
@ -96,6 +96,13 @@ def _print_device(dev):
|
|||
print(' Notifications: %s (0x%06X).' % (', '.join(notification_names), notification_flags))
|
||||
else:
|
||||
print(' Notifications: (none).')
|
||||
device_features = _hidpp10.get_device_features(dev)
|
||||
if device_features is not None:
|
||||
if device_features:
|
||||
device_features_names = _hidpp10.DEVICE_FEATURES.flag_names(device_features)
|
||||
print(' Features: %s (0x%06X)' % (', '.join(device_features_names), device_features))
|
||||
else:
|
||||
print(' Features: (none)')
|
||||
|
||||
if dev.online and dev.features:
|
||||
print(' Supports %d HID++ 2.0 features:' % len(dev.features))
|
||||
|
|
Loading…
Reference in New Issue