incipient support for the Nano receiver
This commit is contained in:
parent
1a9be279c6
commit
079ef8d800
|
@ -27,7 +27,7 @@ del logging
|
||||||
|
|
||||||
from .common import strhex
|
from .common import strhex
|
||||||
from .base import NoReceiver, NoSuchDevice, DeviceUnreachable
|
from .base import NoReceiver, NoSuchDevice, DeviceUnreachable
|
||||||
from .receiver import Receiver, PairedDevice, MAX_PAIRED_DEVICES
|
from .receiver import Receiver, PairedDevice
|
||||||
from .hidpp20 import FeatureNotSupported, FeatureCallError
|
from .hidpp20 import FeatureNotSupported, FeatureCallError
|
||||||
|
|
||||||
from . import listener
|
from . import listener
|
||||||
|
|
|
@ -58,19 +58,22 @@ class DeviceUnreachable(_KwException):
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
|
|
||||||
|
# vendor_id, product_id, interface number, driver
|
||||||
|
DEVICE_UNIFYING_RECEIVER = (0x046d, 0xc52b, 2, 'logitech-djreceiver')
|
||||||
|
DEVICE_UNIFYING_RECEIVER_2 = (0x046d, 0xc532, 2, 'logitech-djreceiver')
|
||||||
|
#DEVICE_NANO_RECEIVER = (0x046d, 0xc526, 1, 'generic-usb')
|
||||||
|
|
||||||
|
|
||||||
def receivers():
|
def receivers():
|
||||||
"""List all the Linux devices exposed by the UR attached to the machine."""
|
"""List all the Linux devices exposed by the UR attached to the machine."""
|
||||||
# (Vendor ID, Product ID) = ('Logitech', 'Unifying Receiver')
|
for d in _hid.enumerate(*DEVICE_UNIFYING_RECEIVER):
|
||||||
# interface 2 if the actual receiver interface
|
yield d
|
||||||
|
for d in _hid.enumerate(*DEVICE_UNIFYING_RECEIVER_2):
|
||||||
|
yield d
|
||||||
|
#for d in _hid.enumerate(*DEVICE_NANO_RECEIVER):
|
||||||
|
# yield d
|
||||||
|
|
||||||
for d in _hid.enumerate(0x046d, 0xc52b, 2):
|
|
||||||
if d.driver == 'logitech-djreceiver':
|
|
||||||
yield d
|
|
||||||
|
|
||||||
# apparently there are TWO product ids possible for the UR?
|
|
||||||
for d in _hid.enumerate(0x046d, 0xc532, 2):
|
|
||||||
if d.driver == 'logitech-djreceiver':
|
|
||||||
yield d
|
|
||||||
|
|
||||||
|
|
||||||
def open_path(path):
|
def open_path(path):
|
||||||
|
|
|
@ -133,6 +133,13 @@ def get_firmware(device):
|
||||||
fw = _FirmwareInfo(FIRMWARE_KIND.Firmware, '', fw_version, None)
|
fw = _FirmwareInfo(FIRMWARE_KIND.Firmware, '', fw_version, None)
|
||||||
firmware.append(fw)
|
firmware.append(fw)
|
||||||
|
|
||||||
|
if device.kind is None and device.max_devices == 1:
|
||||||
|
# Nano receiver
|
||||||
|
return firmware
|
||||||
|
if device.kind is not None and not device._unifying:
|
||||||
|
# Nano device
|
||||||
|
return firmware
|
||||||
|
|
||||||
reply = device.request(0x81F1, 0x04)
|
reply = device.request(0x81F1, 0x04)
|
||||||
if reply:
|
if reply:
|
||||||
bl_version = _strhex(reply[1:3])
|
bl_version = _strhex(reply[1:3])
|
||||||
|
|
|
@ -29,9 +29,10 @@ class PairedDevice(object):
|
||||||
def __init__(self, receiver, number):
|
def __init__(self, receiver, number):
|
||||||
assert receiver
|
assert receiver
|
||||||
self.receiver = _proxy(receiver)
|
self.receiver = _proxy(receiver)
|
||||||
assert number > 0 and number <= MAX_PAIRED_DEVICES
|
assert number > 0 and number <= receiver.max_devices
|
||||||
self.number = number
|
self.number = number
|
||||||
|
|
||||||
|
self._unifying = receiver.max_devices > 1
|
||||||
self._protocol = None
|
self._protocol = None
|
||||||
self._wpid = None
|
self._wpid = None
|
||||||
self._power_switch = None
|
self._power_switch = None
|
||||||
|
@ -43,7 +44,7 @@ class PairedDevice(object):
|
||||||
self._firmware = None
|
self._firmware = None
|
||||||
self._keys = None
|
self._keys = None
|
||||||
|
|
||||||
self.features = _hidpp20.FeaturesArray(self)
|
self.features = _hidpp20.FeaturesArray(self) if self._unifying else None
|
||||||
self._registers = None
|
self._registers = None
|
||||||
self._settings = None
|
self._settings = None
|
||||||
|
|
||||||
|
@ -57,25 +58,29 @@ class PairedDevice(object):
|
||||||
@property
|
@property
|
||||||
def wpid(self):
|
def wpid(self):
|
||||||
if self._wpid is None:
|
if self._wpid is None:
|
||||||
pair_info = self.receiver.request(0x83B5, 0x20 + self.number - 1)
|
if self._unifying:
|
||||||
if pair_info:
|
pair_info = self.receiver.request(0x83B5, 0x20 + self.number - 1)
|
||||||
self._wpid = _strhex(pair_info[3:5])
|
if pair_info:
|
||||||
if self._kind is None:
|
self._wpid = _strhex(pair_info[3:5])
|
||||||
kind = ord(pair_info[7:8]) & 0x0F
|
if self._kind is None:
|
||||||
self._kind = _hidpp10.DEVICE_KIND[kind]
|
kind = ord(pair_info[7:8]) & 0x0F
|
||||||
if self._polling_rate is None:
|
self._kind = _hidpp10.DEVICE_KIND[kind]
|
||||||
self._polling_rate = ord(pair_info[2:3])
|
if self._polling_rate is None:
|
||||||
|
self._polling_rate = ord(pair_info[2:3])
|
||||||
|
# else:
|
||||||
|
# device_info = self.receiver.request(0x83B5, 0x04)
|
||||||
|
# self.wpid = _strhex(device_info[3:5])
|
||||||
return self._wpid
|
return self._wpid
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def polling_rate(self):
|
def polling_rate(self):
|
||||||
if self._polling_rate is None:
|
if self._polling_rate is None and self._unifying:
|
||||||
self.wpid, 0
|
self.wpid, 0
|
||||||
return self._polling_rate
|
return self._polling_rate
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def power_switch_location(self):
|
def power_switch_location(self):
|
||||||
if self._power_switch is None:
|
if self._power_switch is None and self._unifying:
|
||||||
ps = self.receiver.request(0x83B5, 0x30 + self.number - 1)
|
ps = self.receiver.request(0x83B5, 0x30 + self.number - 1)
|
||||||
if ps:
|
if ps:
|
||||||
ps = ord(ps[9:10]) & 0x0F
|
ps = ord(ps[9:10]) & 0x0F
|
||||||
|
@ -84,7 +89,7 @@ class PairedDevice(object):
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def codename(self):
|
def codename(self):
|
||||||
if self._codename is None:
|
if self._codename is None and self._unifying:
|
||||||
codename = self.receiver.request(0x83B5, 0x40 + self.number - 1)
|
codename = self.receiver.request(0x83B5, 0x40 + self.number - 1)
|
||||||
if codename:
|
if codename:
|
||||||
self._codename = codename[2:].rstrip(b'\x00').decode('utf-8')
|
self._codename = codename[2:].rstrip(b'\x00').decode('utf-8')
|
||||||
|
@ -93,7 +98,7 @@ class PairedDevice(object):
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self):
|
def name(self):
|
||||||
if self._name is None:
|
if self._name is None and self._unifying:
|
||||||
if self.codename in _descriptors.DEVICES:
|
if self.codename in _descriptors.DEVICES:
|
||||||
self._name, self._kind = _descriptors.DEVICES[self._codename][:2]
|
self._name, self._kind = _descriptors.DEVICES[self._codename][:2]
|
||||||
elif self.protocol >= 2.0:
|
elif self.protocol >= 2.0:
|
||||||
|
@ -102,7 +107,7 @@ class PairedDevice(object):
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def kind(self):
|
def kind(self):
|
||||||
if self._kind is None:
|
if self._kind is None and self._unifying:
|
||||||
pair_info = self.receiver.request(0x83B5, 0x20 + self.number - 1)
|
pair_info = self.receiver.request(0x83B5, 0x20 + self.number - 1)
|
||||||
if pair_info:
|
if pair_info:
|
||||||
kind = ord(pair_info[7:8]) & 0x0F
|
kind = ord(pair_info[7:8]) & 0x0F
|
||||||
|
@ -128,13 +133,13 @@ class PairedDevice(object):
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def serial(self):
|
def serial(self):
|
||||||
if self._serial is None:
|
if self._serial is None and self._unifying:
|
||||||
self._serial = _hidpp10.get_serial(self)
|
self._serial = _hidpp10.get_serial(self)
|
||||||
return self._serial or '?'
|
return self._serial or '?'
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def keys(self):
|
def keys(self):
|
||||||
if self._keys is None:
|
if self._keys is None and self._unifying:
|
||||||
self._keys = _hidpp20.get_keys(self) or ()
|
self._keys = _hidpp20.get_keys(self) or ()
|
||||||
return self._keys
|
return self._keys
|
||||||
|
|
||||||
|
@ -165,7 +170,8 @@ class PairedDevice(object):
|
||||||
return _base.request(self.receiver.handle, self.number, request_id, *params)
|
return _base.request(self.receiver.handle, self.number, request_id, *params)
|
||||||
|
|
||||||
def feature_request(self, feature, function=0x00, *params):
|
def feature_request(self, feature, function=0x00, *params):
|
||||||
return _hidpp20.feature_request(self, feature, function, *params)
|
if self._unifying:
|
||||||
|
return _hidpp20.feature_request(self, feature, function, *params)
|
||||||
|
|
||||||
def ping(self):
|
def ping(self):
|
||||||
return _base.ping(self.receiver.handle, self.number) is not None
|
return _base.ping(self.receiver.handle, self.number) is not None
|
||||||
|
@ -197,9 +203,7 @@ class Receiver(object):
|
||||||
The paired devices are available through the sequence interface.
|
The paired devices are available through the sequence interface.
|
||||||
"""
|
"""
|
||||||
number = 0xFF
|
number = 0xFF
|
||||||
name = 'Unifying Receiver'
|
|
||||||
kind = None
|
kind = None
|
||||||
max_devices = MAX_PAIRED_DEVICES
|
|
||||||
|
|
||||||
def __init__(self, handle, path=None):
|
def __init__(self, handle, path=None):
|
||||||
assert handle
|
assert handle
|
||||||
|
@ -207,7 +211,19 @@ class Receiver(object):
|
||||||
assert path
|
assert path
|
||||||
self.path = path
|
self.path = path
|
||||||
|
|
||||||
self._serial = None
|
serial_reply = self.request(0x83B5, 0x03)
|
||||||
|
assert serial_reply
|
||||||
|
self._serial = _strhex(serial_reply[1:5])
|
||||||
|
self.max_devices = ord(serial_reply[6:7][0])
|
||||||
|
|
||||||
|
if self.max_devices == 1:
|
||||||
|
self.name = 'Nano Receiver'
|
||||||
|
elif self.max_devices == 6:
|
||||||
|
self.name = 'Unifying Receiver'
|
||||||
|
else:
|
||||||
|
raise Exception("unknown receiver type")
|
||||||
|
self._str = '<%s(%s,%s%s)>' % (self.name.replace(' ', ''), self.path, '' if type(self.handle) == int else 'T', self.handle)
|
||||||
|
|
||||||
self._firmware = None
|
self._firmware = None
|
||||||
self._devices = {}
|
self._devices = {}
|
||||||
|
|
||||||
|
@ -221,8 +237,9 @@ class Receiver(object):
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def serial(self):
|
def serial(self):
|
||||||
if self._serial is None and self.handle:
|
assert self._serial
|
||||||
self._serial = _hidpp10.get_serial(self)
|
# if self._serial is None and self.handle:
|
||||||
|
# self._serial = _hidpp10.get_serial(self)
|
||||||
return self._serial
|
return self._serial
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
@ -260,6 +277,15 @@ class Receiver(object):
|
||||||
raise IndexError("device number %d already registered" % number)
|
raise IndexError("device number %d already registered" % number)
|
||||||
dev = PairedDevice(self, number)
|
dev = PairedDevice(self, number)
|
||||||
# create a device object, but only use it if the receiver knows about it
|
# create a device object, but only use it if the receiver knows about it
|
||||||
|
|
||||||
|
# Nano receiver
|
||||||
|
#if self.max_devices == 1 and number == 1:
|
||||||
|
# # the Nano receiver does not provide the wpid
|
||||||
|
# _log.info("%s: found Nano device %d (%s)", self, number, dev.serial)
|
||||||
|
# # dev._wpid = self.serial + ':1'
|
||||||
|
# self._devices[number] = dev
|
||||||
|
# return dev
|
||||||
|
|
||||||
if dev.wpid:
|
if dev.wpid:
|
||||||
_log.info("found device %d (%s)", number, dev.wpid)
|
_log.info("found device %d (%s)", number, dev.wpid)
|
||||||
self._devices[number] = dev
|
self._devices[number] = dev
|
||||||
|
@ -283,7 +309,7 @@ class Receiver(object):
|
||||||
return _base.request(self.handle, 0xFF, request_id, *params)
|
return _base.request(self.handle, 0xFF, request_id, *params)
|
||||||
|
|
||||||
def __iter__(self):
|
def __iter__(self):
|
||||||
for number in range(1, 1 + MAX_PAIRED_DEVICES):
|
for number in range(1, 1 + self.max_devices):
|
||||||
if number in self._devices:
|
if number in self._devices:
|
||||||
dev = self._devices[number]
|
dev = self._devices[number]
|
||||||
else:
|
else:
|
||||||
|
@ -301,7 +327,7 @@ class Receiver(object):
|
||||||
|
|
||||||
if type(key) != int:
|
if type(key) != int:
|
||||||
raise TypeError('key must be an integer')
|
raise TypeError('key must be an integer')
|
||||||
if key < 1 or key > MAX_PAIRED_DEVICES:
|
if key < 1 or key > self.max_devices:
|
||||||
raise IndexError(key)
|
raise IndexError(key)
|
||||||
|
|
||||||
return self.register_new_device(key)
|
return self.register_new_device(key)
|
||||||
|
@ -329,7 +355,7 @@ class Receiver(object):
|
||||||
return self.__contains__(dev.number)
|
return self.__contains__(dev.number)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return '<Receiver(%s,%s%s)>' % (self.path, '' if type(self.handle) == int else 'T', self.handle)
|
return self._str
|
||||||
__unicode__ = __repr__ = __str__
|
__unicode__ = __repr__ = __str__
|
||||||
|
|
||||||
__bool__ = __nonzero__ = lambda self: self.handle is not None
|
__bool__ = __nonzero__ = lambda self: self.handle is not None
|
||||||
|
|
|
@ -12,7 +12,7 @@ from logging import getLogger, DEBUG as _DEBUG
|
||||||
_log = getLogger('LUR.status')
|
_log = getLogger('LUR.status')
|
||||||
del getLogger
|
del getLogger
|
||||||
|
|
||||||
from .common import NamedInts as _NamedInts
|
from .common import NamedInts as _NamedInts, strhex as _strhex
|
||||||
from . import hidpp10 as _hidpp10
|
from . import hidpp10 as _hidpp10
|
||||||
from . import hidpp20 as _hidpp20
|
from . import hidpp20 as _hidpp20
|
||||||
|
|
||||||
|
@ -223,8 +223,24 @@ class DeviceStatus(dict):
|
||||||
self[ENCRYPTED] = link_encrypyed
|
self[ENCRYPTED] = link_encrypyed
|
||||||
self._changed(link_established)
|
self._changed(link_established)
|
||||||
|
|
||||||
elif n.address == 0x03:
|
elif n.address == 0x03: # eQuad protocol
|
||||||
_log.warn("%s: connection notification with eQuad protocol, ignored: %s", self._device.number, n)
|
# Nano devices might not have been initialized fully
|
||||||
|
if self._device._kind is None:
|
||||||
|
kind = ord(n.data[:1]) & 0x0F
|
||||||
|
self._device._kind = _hidpp10.DEVICE_KIND[kind]
|
||||||
|
if self._device._wpid is None:
|
||||||
|
self._device._wpid = _strhex(n.data[2:3] + n.data[1:2])
|
||||||
|
|
||||||
|
flags = ord(n.data[:1]) & 0xF0
|
||||||
|
link_encrypyed = bool(flags & 0x20)
|
||||||
|
link_established = not (flags & 0x40)
|
||||||
|
if _log.isEnabledFor(_DEBUG):
|
||||||
|
sw_present = bool(flags & 0x10)
|
||||||
|
has_payload = bool(flags & 0x80)
|
||||||
|
_log.debug("%s: eQuad connection notification: software=%s, encrypted=%s, link=%s, payload=%s",
|
||||||
|
self._device, sw_present, link_encrypyed, link_established, has_payload)
|
||||||
|
self[ENCRYPTED] = link_encrypyed
|
||||||
|
self._changed(link_established)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
_log.warn("%s: connection notification with unknown protocol %02X: %s", self._device.number, n.address, n)
|
_log.warn("%s: connection notification with unknown protocol %02X: %s", self._device.number, n.address, n)
|
||||||
|
|
|
@ -6,7 +6,10 @@
|
||||||
# Make sure the plugdev group exists on your system and your user is a member
|
# Make sure the plugdev group exists on your system and your user is a member
|
||||||
# before applying these rules.
|
# before applying these rules.
|
||||||
|
|
||||||
# HIDAPI/hidraw
|
# HIDAPI/hidraw for Logitech Unifying Receiver
|
||||||
ACTION=="add", KERNEL=="hidraw*", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c52b", GROUP="plugdev", MODE="0660"
|
ACTION=="add", KERNEL=="hidraw*", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c52b", GROUP="plugdev", MODE="0660"
|
||||||
|
|
||||||
|
# HIDAPI/hidraw for Logitech Nano Receiver
|
||||||
|
#ACTION=="add", KERNEL=="hidraw*", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c526", GROUP="plugdev", MODE="0660"
|
||||||
|
|
||||||
# vim: ft=udevrules
|
# vim: ft=udevrules
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
|
|
||||||
if test -z "$1"; then
|
if test -z "$1"; then
|
||||||
echo "Use: $0 <device number 1..6>"
|
echo "Use: $0 <device number 1..6> [<receiver device>]"
|
||||||
exit 2
|
exit 2
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
@ -14,10 +14,10 @@ for x in $z; do
|
||||||
for y in $z; do
|
for y in $z; do
|
||||||
echo "10 0${1} 81${x}${y} 000000"
|
echo "10 0${1} 81${x}${y} 000000"
|
||||||
done
|
done
|
||||||
done | "$HC" --hidpp | grep -v ' 8F.. ..0[12]' | grep -B 1 '^>> '
|
done | "$HC" --hidpp $2 | grep -v ' 8F.. ..0[12]' | grep -B 1 '^>> '
|
||||||
|
|
||||||
for x in $z; do
|
for x in $z; do
|
||||||
for y in $z; do
|
for y in $z; do
|
||||||
echo "10 0${1} 83${x}${y} 000000"
|
echo "10 0${1} 83${x}${y} 000000"
|
||||||
done
|
done
|
||||||
done | "$HC" --hidpp | grep -v ' 8F.. ..0[12]' | grep -B 1 '^>> '
|
done | "$HC" --hidpp $2 | grep -v ' 8F.. ..0[12]' | grep -B 1 '^>> '
|
||||||
|
|
Loading…
Reference in New Issue