incipient support for the Nano receiver

This commit is contained in:
Daniel Pavel 2013-04-28 14:27:16 +02:00
parent 1a9be279c6
commit 079ef8d800
7 changed files with 98 additions and 43 deletions

View File

@ -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

View File

@ -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):

View File

@ -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])

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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 '^>> '