Fix issues with HID++ >= 2.0 devices (particularly related to sleep).

* Don't assume 0x41 messages only occur when a device is first paired
    (prevents errors when waking from sleep or turning a device on)
  * Delay reads/writes when a device is powered on, to prevent broken pipe
    errors (hacky solution).
  * Don't clear status when a device connects, preventing settings from being
    cleared when a device sleeps or is turned off.
  * Fix typos.
This commit is contained in:
Ben Wolsieffer 2018-08-10 23:05:34 -04:00
parent d021d87656
commit a59368f3e7
4 changed files with 25 additions and 14 deletions

View File

@ -150,7 +150,7 @@ class EventsListener(_threading.Thread):
# replace the handle with a threaded one
self.receiver.handle = _ThreadedHandle(self, self.receiver.path, self.receiver.handle)
# get the right low-level handle for this thead
# get the right low-level handle for this thread
ihandle = int(self.receiver.handle)
if _log.isEnabledFor(_INFO):
_log.info("started with %s (%d)", self.receiver, ihandle)

View File

@ -164,14 +164,14 @@ def _process_hidpp10_notification(device, status, n):
assert wpid == device.wpid, "%s wpid mismatch, got %s" % (device, wpid)
flags = ord(n.data[:1]) & 0xF0
link_encrypyed = bool(flags & 0x20)
link_encrypted = 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: %s connection notification: software=%s, encrypted=%s, link=%s, payload=%s",
device, protocol_name, sw_present, link_encrypyed, link_established, has_payload)
status[_K.LINK_ENCRYPTED] = link_encrypyed
device, protocol_name, sw_present, link_encrypted, link_established, has_payload)
status[_K.LINK_ENCRYPTED] = link_encrypted
status.changed(active=link_established)
else:
_log.warn("%s: connection notification with unknown protocol %02X: %s", device.number, n.address, n)

View File

@ -67,10 +67,11 @@ def attach_to(device, changed_callback):
assert device
assert changed_callback
if device.kind is None:
device.status = ReceiverStatus(device, changed_callback)
else:
device.status = DeviceStatus(device, changed_callback)
if not hasattr(device, 'status') or device.status is None:
if device.kind is None:
device.status = ReceiverStatus(device, changed_callback)
else:
device.status = DeviceStatus(device, changed_callback)
#
#

View File

@ -18,6 +18,7 @@
## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
from __future__ import absolute_import, division, print_function, unicode_literals
import time
from logging import getLogger, INFO as _INFO
_log = getLogger(__name__)
@ -181,11 +182,20 @@ class ReceiverListener(_listener.EventsListener):
return
# a device notification
assert n.devnumber > 0 and n.devnumber <= self.receiver.max_devices
assert 0 < n.devnumber <= self.receiver.max_devices
already_known = n.devnumber in self.receiver
if n.sub_id == 0x41:
already_known = False
# FIXME: hacky fix for kernel/hardware race condition
# If the device was just turned on or woken up from sleep, it may not
# be ready to receive commands. The "payload" bit of the wireless
# status notification seems to tell us this. If this is the case, we
# must wait a short amount of time to avoid causing a broken pipe
# error.
device_ready = not bool(ord(n.data[0:1]) & 0x80) or n.sub_id != 0x41
if not device_ready:
time.sleep(0.01)
if n.sub_id == 0x41 and not already_known:
dev = self.receiver.register_new_device(n.devnumber, n)
else:
dev = self.receiver[n.devnumber]
@ -194,7 +204,8 @@ class ReceiverListener(_listener.EventsListener):
_log.warn("%s: received %s for invalid device %d: %r", self.receiver, n, n.devnumber, dev)
return
if not already_known:
# Apply settings every time the device connects
if n.sub_id == 0x41:
if _log.isEnabledFor(_INFO):
_log.info("%s triggered new device %s (%s)", n, dev, dev.kind)
# If there are saved configs, bring the device's settings up-to-date.
@ -213,8 +224,7 @@ class ReceiverListener(_listener.EventsListener):
if _log.isEnabledFor(_INFO):
_log.info("%s: pairing detected new device", self.receiver)
self.receiver.status.new_device = dev
elif dev:
if dev.online is None:
elif dev.online is None:
dev.ping()
def __str__(self):