def ping(handle, devnumber): """Check if a device is connected to the UR. :returns: The HID protocol supported by the device, as a floating point number, if the device is active. """ if _log.isEnabledFor(_DEBUG): _log.debug("(%s) pinging device %d", handle, devnumber) # import inspect as _inspect # print ('\n '.join(str(s) for s in _inspect.stack())) # randomize the SoftwareId and mark byte to be able to identify the ping # reply, and set most significant (0x8) bit in SoftwareId so that the reply # is always distinguishable from notifications request_id = 0x0018 | _random_bits(3) request_data = _pack(b'!HBBB', request_id, 0, 0, _random_bits(8)) ihandle = int(handle) notifications_hook = getattr(handle, 'notifications_hook', None) _skip_incoming(handle, ihandle, notifications_hook) write(ihandle, devnumber, request_data) while True: now = _timestamp() reply = _read(handle, _PING_TIMEOUT) delta = _timestamp() - now if reply: report_id, reply_devnumber, reply_data = reply if reply_devnumber == devnumber: if reply_data[:2] == request_data[:2] and reply_data[4:5] == request_data[-1:]: # HID++ 2.0+ device, currently connected return ord(reply_data[2:3]) + ord(reply_data[3:4]) / 10.0 if report_id == 0x10 and reply_data[:1] == b'\x8F' and reply_data[1:3] == request_data[:2]: assert reply_data[-1:] == b'\x00' error = ord(reply_data[3:4]) if error == _hidpp10.ERROR.invalid_SubID__command: # a valid reply from a HID++ 1.0 device return 1.0 if error == _hidpp10.ERROR.resource_error: # device unreachable # raise DeviceUnreachable(number=devnumber, request=request_id) break if error == _hidpp10.ERROR.unknown_device: # no paired device with that number _log.error("(%s) device %d error on ping request: unknown device", handle, devnumber) raise NoSuchDevice(number=devnumber, request=request_id) if notifications_hook: n = make_notification(reply_devnumber, reply_data) if n: notifications_hook(n) if delta >= _PING_TIMEOUT: _log.warn("(%s) timeout on device %d ping", handle, devnumber)
def ping(handle, devnumber, long_message=False): """Check if a device is connected to the receiver. :returns: The HID protocol supported by the device, as a floating point number, if the device is active. """ if _log.isEnabledFor(_DEBUG): _log.debug('(%s) pinging device %d', handle, devnumber) # import inspect as _inspect # print ('\n '.join(str(s) for s in _inspect.stack())) assert devnumber != 0xFF assert devnumber >= 0x00 assert devnumber < 0x0F # randomize the SoftwareId and mark byte to be able to identify the ping # reply, and set most significant (0x8) bit in SoftwareId so that the reply # is always distinguishable from notifications request_id = 0x0018 | _random_bits(3) request_data = _pack('!HBBB', request_id, 0, 0, _random_bits(8)) ihandle = int(handle) notifications_hook = getattr(handle, 'notifications_hook', None) _skip_incoming(handle, ihandle, notifications_hook) write(ihandle, devnumber, request_data, long_message) # we consider timeout from this point request_started = _timestamp() delta = 0 while delta < _PING_TIMEOUT: reply = _read(handle, _PING_TIMEOUT) if reply: report_id, reply_devnumber, reply_data = reply if reply_devnumber == devnumber: if reply_data[:2] == request_data[:2] and reply_data[4:5] == request_data[-1:]: # HID++ 2.0+ device, currently connected return ord(reply_data[2:3]) + ord(reply_data[3:4]) / 10.0 if report_id == 0x10 and reply_data[:1] == b'\x8F' and reply_data[1:3] == request_data[:2]: assert reply_data[-1:] == b'\x00' error = ord(reply_data[3:4]) if error == _hidpp10.ERROR.invalid_SubID__command: # a valid reply from a HID++ 1.0 device return 1.0 if error == _hidpp10.ERROR.resource_error: # device unreachable return if error == _hidpp10.ERROR.unknown_device: # no paired device with that number _log.error('(%s) device %d error on ping request: unknown device', handle, devnumber) raise NoSuchDevice(number=devnumber, request=request_id) if notifications_hook: n = make_notification(reply_devnumber, reply_data) if n: notifications_hook(n) # elif _log.isEnabledFor(_DEBUG): # _log.debug("(%s) ignoring reply %02X [%s]", handle, reply_devnumber, _strhex(reply_data)) delta = _timestamp() - request_started _log.warn('(%s) timeout (%0.2f/%0.2f) on device %d ping', handle, delta, _PING_TIMEOUT, devnumber)
def request(handle, devnumber, request_id, *params, no_reply=False, return_error=False, long_message=False): """Makes a feature call to a device and waits for a matching reply. :param handle: an open UR handle. :param devnumber: attached device number. :param request_id: a 16-bit integer. :param params: parameters for the feature call, 3 to 16 bytes. :returns: the reply data, or ``None`` if some error occurred. or no reply expected """ # import inspect as _inspect # print ('\n '.join(str(s) for s in _inspect.stack())) assert isinstance(request_id, int) if devnumber != 0xFF and request_id < 0x8000: # For HID++ 2.0 feature requests, randomize the SoftwareId to make it # easier to recognize the reply for this request. also, always set the # most significant bit (8) in SoftwareId, to make notifications easier # to distinguish from request replies. # This only applies to peripheral requests, ofc. request_id = (request_id & 0xFFF0) | 0x08 | _random_bits(3) timeout = _RECEIVER_REQUEST_TIMEOUT if devnumber == 0xFF else _DEVICE_REQUEST_TIMEOUT # be extra patient on long register read if request_id & 0xFF00 == 0x8300: timeout *= 2 if params: params = b''.join(_pack('B', p) if isinstance(p, int) else p for p in params) else: params = b'' # if _log.isEnabledFor(_DEBUG): # _log.debug("(%s) device %d request_id {%04X} params [%s]", handle, devnumber, request_id, _strhex(params)) request_data = _pack('!H', request_id) + params ihandle = int(handle) notifications_hook = getattr(handle, 'notifications_hook', None) _skip_incoming(handle, ihandle, notifications_hook) write(ihandle, devnumber, request_data, long_message) if no_reply: return None # we consider timeout from this point request_started = _timestamp() delta = 0 while delta < timeout: reply = _read(handle, timeout) if reply: report_id, reply_devnumber, reply_data = reply if reply_devnumber == devnumber: if report_id == 0x10 and reply_data[:1] == b'\x8F' and reply_data[1:3] == request_data[:2]: error = ord(reply_data[3:4]) # if error == _hidpp10.ERROR.resource_error: # device unreachable # _log.warn("(%s) device %d error on request {%04X}: unknown device", handle, devnumber, request_id) # raise DeviceUnreachable(number=devnumber, request=request_id) # if error == _hidpp10.ERROR.unknown_device: # unknown device # _log.error("(%s) device %d error on request {%04X}: unknown device", handle, devnumber, request_id) # raise NoSuchDevice(number=devnumber, request=request_id) if _log.isEnabledFor(_DEBUG): _log.debug( '(%s) device 0x%02X error on request {%04X}: %d = %s', handle, devnumber, request_id, error, _hidpp10.ERROR[error] ) return _hidpp10.ERROR[error] if return_error else None if reply_data[:1] == b'\xFF' and reply_data[1:3] == request_data[:2]: # a HID++ 2.0 feature call returned with an error error = ord(reply_data[3:4]) _log.error( '(%s) device %d error on feature request {%04X}: %d = %s', handle, devnumber, request_id, error, _hidpp20.ERROR[error] ) raise _hidpp20.FeatureCallError(number=devnumber, request=request_id, error=error, params=params) if reply_data[:2] == request_data[:2]: if request_id & 0xFE00 == 0x8200: # long registry r/w should return a long reply assert report_id == 0x11 elif request_id & 0xFE00 == 0x8000: # short registry r/w should return a short reply assert report_id == 0x10 if devnumber == 0xFF: if request_id == 0x83B5 or request_id == 0x81F1: # these replies have to match the first parameter as well if reply_data[2:3] == params[:1]: return reply_data[2:] else: # hm, not matching my request, and certainly not a notification continue else: return reply_data[2:] else: return reply_data[2:] else: # a reply was received, but did not match our request in any way # reset the timeout starting point request_started = _timestamp() if notifications_hook: n = make_notification(reply_devnumber, reply_data) if n: notifications_hook(n) # elif _log.isEnabledFor(_DEBUG): # _log.debug("(%s) ignoring reply %02X [%s]", handle, reply_devnumber, _strhex(reply_data)) # elif _log.isEnabledFor(_DEBUG): # _log.debug("(%s) ignoring reply %02X [%s]", handle, reply_devnumber, _strhex(reply_data)) delta = _timestamp() - request_started # if _log.isEnabledFor(_DEBUG): # _log.debug("(%s) still waiting for reply, delta %f", handle, delta) _log.warn( 'timeout (%0.2f/%0.2f) on device %d request {%04X} params [%s]', delta, timeout, devnumber, request_id, _strhex(params) )
def request(handle, devnumber, request_id, *params): """Makes a feature call to a device and waits for a matching reply. This function will wait for a matching reply indefinitely. :param handle: an open UR handle. :param devnumber: attached device number. :param request_id: a 16-bit integer. :param params: parameters for the feature call, 3 to 16 bytes. :returns: the reply data, or ``None`` if some error occured. """ # import inspect as _inspect # print ('\n '.join(str(s) for s in _inspect.stack())) assert isinstance(request_id, int) if devnumber != 0xFF and request_id < 0x8000: # For HID++ 2.0 feature requests, randomize the SoftwareId to make it # easier to recognize the reply for this request. also, always set the # most significant bit (8) in SoftwareId, to make notifications easier # to distinguish from request replies. # This only applies to peripheral requests, ofc. request_id = (request_id & 0xFFF0) | 0x08 | _random_bits(3) timeout = _RECEIVER_REQUEST_TIMEOUT if devnumber == 0xFF else _DEVICE_REQUEST_TIMEOUT # be extra patient on long register read if request_id & 0xFF00 == 0x8300: timeout *= 2 if params: params = b''.join(_pack('B', p) if isinstance(p, int) else p for p in params) else: params = b'' # if _log.isEnabledFor(_DEBUG): # _log.debug("(%s) device %d request_id {%04X} params [%s]", handle, devnumber, request_id, _strhex(params)) request_data = _pack('!H', request_id) + params ihandle = int(handle) notifications_hook = getattr(handle, 'notifications_hook', None) _skip_incoming(handle, ihandle, notifications_hook) write(ihandle, devnumber, request_data) # we consider timeout from this point request_started = _timestamp() delta = 0 while delta < timeout: reply = _read(handle, timeout) if reply: report_id, reply_devnumber, reply_data = reply if reply_devnumber == devnumber: if report_id == 0x10 and reply_data[:1] == b'\x8F' and reply_data[1:3] == request_data[:2]: error = ord(reply_data[3:4]) # if error == _hidpp10.ERROR.resource_error: # device unreachable # _log.warn("(%s) device %d error on request {%04X}: unknown device", handle, devnumber, request_id) # raise DeviceUnreachable(number=devnumber, request=request_id) # if error == _hidpp10.ERROR.unknown_device: # unknown device # _log.error("(%s) device %d error on request {%04X}: unknown device", handle, devnumber, request_id) # raise NoSuchDevice(number=devnumber, request=request_id) if _log.isEnabledFor(_DEBUG): _log.debug("(%s) device 0x%02X error on request {%04X}: %d = %s", handle, devnumber, request_id, error, _hidpp10.ERROR[error]) return if reply_data[:1] == b'\xFF' and reply_data[1:3] == request_data[:2]: # a HID++ 2.0 feature call returned with an error error = ord(reply_data[3:4]) _log.error("(%s) device %d error on feature request {%04X}: %d = %s", handle, devnumber, request_id, error, _hidpp20.ERROR[error]) raise _hidpp20.FeatureCallError(number=devnumber, request=request_id, error=error, params=params) if reply_data[:2] == request_data[:2]: if request_id & 0xFE00 == 0x8200: # long registry r/w should return a long reply assert report_id == 0x11 elif request_id & 0xFE00 == 0x8000: # short registry r/w should return a short reply assert report_id == 0x10 if devnumber == 0xFF: if request_id == 0x83B5 or request_id == 0x81F1: # these replies have to match the first parameter as well if reply_data[2:3] == params[:1]: return reply_data[2:] else: # hm, not mathing my request, and certainly not a notification continue else: return reply_data[2:] else: return reply_data[2:] else: # a reply was received, but did not match our request in any way # reset the timeout starting point request_started = _timestamp() if notifications_hook: n = make_notification(reply_devnumber, reply_data) if n: notifications_hook(n) # elif _log.isEnabledFor(_DEBUG): # _log.debug("(%s) ignoring reply %02X [%s]", handle, reply_devnumber, _strhex(reply_data)) # elif _log.isEnabledFor(_DEBUG): # _log.debug("(%s) ignoring reply %02X [%s]", handle, reply_devnumber, _strhex(reply_data)) delta = _timestamp() - request_started # if _log.isEnabledFor(_DEBUG): # _log.debug("(%s) still waiting for reply, delta %f", handle, delta) _log.warn("timeout (%0.2f/%0.2f) on device %d request {%04X} params [%s]", delta, timeout, devnumber, request_id, _strhex(params))