Ejemplo n.º 1
0
Archivo: base.py Proyecto: Nek-/Solaar
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)
Ejemplo n.º 2
0
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)
Ejemplo n.º 3
0
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)
    )
Ejemplo n.º 4
0
Archivo: base.py Proyecto: 3v1n0/Solaar
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))