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 .base import NoReceiver, NoSuchDevice, DeviceUnreachable
|
||||
from .receiver import Receiver, PairedDevice, MAX_PAIRED_DEVICES
|
||||
from .receiver import Receiver, PairedDevice
|
||||
from .hidpp20 import FeatureNotSupported, FeatureCallError
|
||||
|
||||
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():
|
||||
"""List all the Linux devices exposed by the UR attached to the machine."""
|
||||
# (Vendor ID, Product ID) = ('Logitech', 'Unifying Receiver')
|
||||
# interface 2 if the actual receiver interface
|
||||
|
||||
for d in _hid.enumerate(0x046d, 0xc52b, 2):
|
||||
if d.driver == 'logitech-djreceiver':
|
||||
for d in _hid.enumerate(*DEVICE_UNIFYING_RECEIVER):
|
||||
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':
|
||||
for d in _hid.enumerate(*DEVICE_UNIFYING_RECEIVER_2):
|
||||
yield d
|
||||
#for d in _hid.enumerate(*DEVICE_NANO_RECEIVER):
|
||||
# yield d
|
||||
|
||||
|
||||
|
||||
|
||||
def open_path(path):
|
||||
|
|
|
@ -133,6 +133,13 @@ def get_firmware(device):
|
|||
fw = _FirmwareInfo(FIRMWARE_KIND.Firmware, '', fw_version, None)
|
||||
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)
|
||||
if reply:
|
||||
bl_version = _strhex(reply[1:3])
|
||||
|
|
|
@ -29,9 +29,10 @@ class PairedDevice(object):
|
|||
def __init__(self, receiver, number):
|
||||
assert 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._unifying = receiver.max_devices > 1
|
||||
self._protocol = None
|
||||
self._wpid = None
|
||||
self._power_switch = None
|
||||
|
@ -43,7 +44,7 @@ class PairedDevice(object):
|
|||
self._firmware = None
|
||||
self._keys = None
|
||||
|
||||
self.features = _hidpp20.FeaturesArray(self)
|
||||
self.features = _hidpp20.FeaturesArray(self) if self._unifying else None
|
||||
self._registers = None
|
||||
self._settings = None
|
||||
|
||||
|
@ -57,6 +58,7 @@ class PairedDevice(object):
|
|||
@property
|
||||
def wpid(self):
|
||||
if self._wpid is None:
|
||||
if self._unifying:
|
||||
pair_info = self.receiver.request(0x83B5, 0x20 + self.number - 1)
|
||||
if pair_info:
|
||||
self._wpid = _strhex(pair_info[3:5])
|
||||
|
@ -65,17 +67,20 @@ class PairedDevice(object):
|
|||
self._kind = _hidpp10.DEVICE_KIND[kind]
|
||||
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
|
||||
|
||||
@property
|
||||
def polling_rate(self):
|
||||
if self._polling_rate is None:
|
||||
if self._polling_rate is None and self._unifying:
|
||||
self.wpid, 0
|
||||
return self._polling_rate
|
||||
|
||||
@property
|
||||
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)
|
||||
if ps:
|
||||
ps = ord(ps[9:10]) & 0x0F
|
||||
|
@ -84,7 +89,7 @@ class PairedDevice(object):
|
|||
|
||||
@property
|
||||
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)
|
||||
if codename:
|
||||
self._codename = codename[2:].rstrip(b'\x00').decode('utf-8')
|
||||
|
@ -93,7 +98,7 @@ class PairedDevice(object):
|
|||
|
||||
@property
|
||||
def name(self):
|
||||
if self._name is None:
|
||||
if self._name is None and self._unifying:
|
||||
if self.codename in _descriptors.DEVICES:
|
||||
self._name, self._kind = _descriptors.DEVICES[self._codename][:2]
|
||||
elif self.protocol >= 2.0:
|
||||
|
@ -102,7 +107,7 @@ class PairedDevice(object):
|
|||
|
||||
@property
|
||||
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)
|
||||
if pair_info:
|
||||
kind = ord(pair_info[7:8]) & 0x0F
|
||||
|
@ -128,13 +133,13 @@ class PairedDevice(object):
|
|||
|
||||
@property
|
||||
def serial(self):
|
||||
if self._serial is None:
|
||||
if self._serial is None and self._unifying:
|
||||
self._serial = _hidpp10.get_serial(self)
|
||||
return self._serial or '?'
|
||||
|
||||
@property
|
||||
def keys(self):
|
||||
if self._keys is None:
|
||||
if self._keys is None and self._unifying:
|
||||
self._keys = _hidpp20.get_keys(self) or ()
|
||||
return self._keys
|
||||
|
||||
|
@ -165,6 +170,7 @@ class PairedDevice(object):
|
|||
return _base.request(self.receiver.handle, self.number, request_id, *params)
|
||||
|
||||
def feature_request(self, feature, function=0x00, *params):
|
||||
if self._unifying:
|
||||
return _hidpp20.feature_request(self, feature, function, *params)
|
||||
|
||||
def ping(self):
|
||||
|
@ -197,9 +203,7 @@ class Receiver(object):
|
|||
The paired devices are available through the sequence interface.
|
||||
"""
|
||||
number = 0xFF
|
||||
name = 'Unifying Receiver'
|
||||
kind = None
|
||||
max_devices = MAX_PAIRED_DEVICES
|
||||
|
||||
def __init__(self, handle, path=None):
|
||||
assert handle
|
||||
|
@ -207,7 +211,19 @@ class Receiver(object):
|
|||
assert 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._devices = {}
|
||||
|
||||
|
@ -221,8 +237,9 @@ class Receiver(object):
|
|||
|
||||
@property
|
||||
def serial(self):
|
||||
if self._serial is None and self.handle:
|
||||
self._serial = _hidpp10.get_serial(self)
|
||||
assert self._serial
|
||||
# if self._serial is None and self.handle:
|
||||
# self._serial = _hidpp10.get_serial(self)
|
||||
return self._serial
|
||||
|
||||
@property
|
||||
|
@ -260,6 +277,15 @@ class Receiver(object):
|
|||
raise IndexError("device number %d already registered" % number)
|
||||
dev = PairedDevice(self, number)
|
||||
# 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:
|
||||
_log.info("found device %d (%s)", number, dev.wpid)
|
||||
self._devices[number] = dev
|
||||
|
@ -283,7 +309,7 @@ class Receiver(object):
|
|||
return _base.request(self.handle, 0xFF, request_id, *params)
|
||||
|
||||
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:
|
||||
dev = self._devices[number]
|
||||
else:
|
||||
|
@ -301,7 +327,7 @@ class Receiver(object):
|
|||
|
||||
if type(key) != int:
|
||||
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)
|
||||
|
||||
return self.register_new_device(key)
|
||||
|
@ -329,7 +355,7 @@ class Receiver(object):
|
|||
return self.__contains__(dev.number)
|
||||
|
||||
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__
|
||||
|
||||
__bool__ = __nonzero__ = lambda self: self.handle is not None
|
||||
|
|
|
@ -12,7 +12,7 @@ from logging import getLogger, DEBUG as _DEBUG
|
|||
_log = getLogger('LUR.status')
|
||||
del getLogger
|
||||
|
||||
from .common import NamedInts as _NamedInts
|
||||
from .common import NamedInts as _NamedInts, strhex as _strhex
|
||||
from . import hidpp10 as _hidpp10
|
||||
from . import hidpp20 as _hidpp20
|
||||
|
||||
|
@ -223,8 +223,24 @@ class DeviceStatus(dict):
|
|||
self[ENCRYPTED] = link_encrypyed
|
||||
self._changed(link_established)
|
||||
|
||||
elif n.address == 0x03:
|
||||
_log.warn("%s: connection notification with eQuad protocol, ignored: %s", self._device.number, n)
|
||||
elif n.address == 0x03: # eQuad protocol
|
||||
# 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:
|
||||
_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
|
||||
# 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"
|
||||
|
||||
# HIDAPI/hidraw for Logitech Nano Receiver
|
||||
#ACTION=="add", KERNEL=="hidraw*", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c526", GROUP="plugdev", MODE="0660"
|
||||
|
||||
# vim: ft=udevrules
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#!/bin/sh
|
||||
|
||||
if test -z "$1"; then
|
||||
echo "Use: $0 <device number 1..6>"
|
||||
echo "Use: $0 <device number 1..6> [<receiver device>]"
|
||||
exit 2
|
||||
fi
|
||||
|
||||
|
@ -14,10 +14,10 @@ for x in $z; do
|
|||
for y in $z; do
|
||||
echo "10 0${1} 81${x}${y} 000000"
|
||||
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 y in $z; do
|
||||
echo "10 0${1} 83${x}${y} 000000"
|
||||
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