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 # replace the handle with a threaded one
self.receiver.handle = _ThreadedHandle(self, self.receiver.path, self.receiver.handle) 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) ihandle = int(self.receiver.handle)
if _log.isEnabledFor(_INFO): if _log.isEnabledFor(_INFO):
_log.info("started with %s (%d)", self.receiver, ihandle) _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) assert wpid == device.wpid, "%s wpid mismatch, got %s" % (device, wpid)
flags = ord(n.data[:1]) & 0xF0 flags = ord(n.data[:1]) & 0xF0
link_encrypyed = bool(flags & 0x20) link_encrypted = bool(flags & 0x20)
link_established = not (flags & 0x40) link_established = not (flags & 0x40)
if _log.isEnabledFor(_DEBUG): if _log.isEnabledFor(_DEBUG):
sw_present = bool(flags & 0x10) sw_present = bool(flags & 0x10)
has_payload = bool(flags & 0x80) has_payload = bool(flags & 0x80)
_log.debug("%s: %s connection notification: software=%s, encrypted=%s, link=%s, payload=%s", _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) device, protocol_name, sw_present, link_encrypted, link_established, has_payload)
status[_K.LINK_ENCRYPTED] = link_encrypyed status[_K.LINK_ENCRYPTED] = link_encrypted
status.changed(active=link_established) status.changed(active=link_established)
else: else:
_log.warn("%s: connection notification with unknown protocol %02X: %s", device.number, n.address, n) _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 device
assert changed_callback assert changed_callback
if device.kind is None: if not hasattr(device, 'status') or device.status is None:
device.status = ReceiverStatus(device, changed_callback) if device.kind is None:
else: device.status = ReceiverStatus(device, changed_callback)
device.status = DeviceStatus(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. ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
from __future__ import absolute_import, division, print_function, unicode_literals from __future__ import absolute_import, division, print_function, unicode_literals
import time
from logging import getLogger, INFO as _INFO from logging import getLogger, INFO as _INFO
_log = getLogger(__name__) _log = getLogger(__name__)
@ -181,11 +182,20 @@ class ReceiverListener(_listener.EventsListener):
return return
# a device notification # 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 already_known = n.devnumber in self.receiver
if n.sub_id == 0x41: # FIXME: hacky fix for kernel/hardware race condition
already_known = False # 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) dev = self.receiver.register_new_device(n.devnumber, n)
else: else:
dev = self.receiver[n.devnumber] 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) _log.warn("%s: received %s for invalid device %d: %r", self.receiver, n, n.devnumber, dev)
return return
if not already_known: # Apply settings every time the device connects
if n.sub_id == 0x41:
if _log.isEnabledFor(_INFO): if _log.isEnabledFor(_INFO):
_log.info("%s triggered new device %s (%s)", n, dev, dev.kind) _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. # 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): if _log.isEnabledFor(_INFO):
_log.info("%s: pairing detected new device", self.receiver) _log.info("%s: pairing detected new device", self.receiver)
self.receiver.status.new_device = dev self.receiver.status.new_device = dev
elif dev: elif dev.online is None:
if dev.online is None:
dev.ping() dev.ping()
def __str__(self): def __str__(self):