mirror of
https://gitlab.freedesktop.org/upower/upower.git
synced 2026-05-01 02:07:57 +02:00
hidpp: validate messages, retry if invalid
This prevents matching the wrong response packet, for example when a mouse is moved while a packet is read. As a result, the reads are more reliable and log spam is reduced. Signed-off-by: Peter Wu <lekensteyn@gmail.com>
This commit is contained in:
parent
c830102ca7
commit
4a211e07c3
1 changed files with 78 additions and 34 deletions
|
|
@ -54,7 +54,7 @@
|
|||
#define HIDPP_READ_LONG_REGISTER_DEVICE_TYPE_GAMEPAD 0xb
|
||||
#define HIDPP_READ_LONG_REGISTER_DEVICE_TYPE_JOYSTICK 0xc
|
||||
|
||||
#define HIDPP_ERR_INVALID_SUBID 0x8f
|
||||
#define HIDPP_ERROR_MESSAGE 0x8f
|
||||
|
||||
/* HID++ 2.0 */
|
||||
|
||||
|
|
@ -145,6 +145,24 @@ typedef struct {
|
|||
G_DEFINE_TYPE (HidppDevice, hidpp_device, G_TYPE_OBJECT)
|
||||
#define HIDPP_DEVICE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), HIDPP_TYPE_DEVICE, HidppDevicePrivate))
|
||||
|
||||
/**
|
||||
* hidpp_is_error:
|
||||
*
|
||||
* Checks whether a message is a protocol-level error.
|
||||
*/
|
||||
static gboolean
|
||||
hidpp_is_error(HidppMessage *msg, guchar *error)
|
||||
{
|
||||
if (msg->type == HIDPP_MSG_TYPE_SHORT &&
|
||||
msg->feature_idx == HIDPP_ERROR_MESSAGE) {
|
||||
if (error)
|
||||
*error = msg->s.params[1];
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* hidpp_device_map_print:
|
||||
**/
|
||||
|
|
@ -266,6 +284,7 @@ hidpp_device_cmd (HidppDevice *device,
|
|||
gssize wrote;
|
||||
HidppMessage read_msg = { 0 };
|
||||
guint msg_len;
|
||||
guchar error_code;
|
||||
HidppDevicePrivate *priv = device->priv;
|
||||
GPollFD poll[] = {
|
||||
{
|
||||
|
|
@ -273,6 +292,8 @@ hidpp_device_cmd (HidppDevice *device,
|
|||
.events = G_IO_IN | G_IO_OUT | G_IO_ERR,
|
||||
},
|
||||
};
|
||||
guint64 begin_time;
|
||||
gint remaining_time;
|
||||
|
||||
g_assert (request->type == HIDPP_MSG_TYPE_SHORT ||
|
||||
request->type == HIDPP_MSG_TYPE_LONG);
|
||||
|
|
@ -292,41 +313,64 @@ hidpp_device_cmd (HidppDevice *device,
|
|||
}
|
||||
|
||||
/* read from the device */
|
||||
// TODO: keep reading until wanted message is found
|
||||
wrote = g_poll (poll, G_N_ELEMENTS(poll),
|
||||
HIDPP_DEVICE_READ_RESPONSE_TIMEOUT);
|
||||
if (wrote <= 0) {
|
||||
g_set_error (error, 1, 0,
|
||||
"Attempt to read response from device timed out: %" G_GSIZE_FORMAT,
|
||||
wrote);
|
||||
ret = FALSE;
|
||||
goto out;
|
||||
begin_time = g_get_monotonic_time () / 1000;
|
||||
remaining_time = HIDPP_DEVICE_READ_RESPONSE_TIMEOUT;
|
||||
for (;;) {
|
||||
wrote = g_poll (poll, G_N_ELEMENTS(poll), remaining_time);
|
||||
if (wrote <= 0) {
|
||||
g_set_error (error, 1, 0,
|
||||
"Attempt to read response from device timed out: %" G_GSIZE_FORMAT,
|
||||
wrote);
|
||||
ret = FALSE;
|
||||
goto out;
|
||||
}
|
||||
|
||||
wrote = read (priv->fd, &read_msg, sizeof (*response));
|
||||
if (wrote <= 0) {
|
||||
g_set_error (error, 1, 0,
|
||||
"Unable to read response from device: %" G_GSIZE_FORMAT,
|
||||
wrote);
|
||||
ret = FALSE;
|
||||
goto out;
|
||||
}
|
||||
|
||||
hidpp_device_print_buffer (device, response);
|
||||
|
||||
/* validate response */
|
||||
if (read_msg.type != HIDPP_MSG_TYPE_SHORT &&
|
||||
read_msg.type != HIDPP_MSG_TYPE_LONG) {
|
||||
/* ignore key presses, mouse motions, etc. */
|
||||
continue;
|
||||
}
|
||||
|
||||
if (read_msg.feature_idx == request->feature_idx &&
|
||||
read_msg.function_idx == request->function_idx) {
|
||||
break;
|
||||
}
|
||||
|
||||
/* recognize HID++ 1.0 errors */
|
||||
if (hidpp_is_error(&read_msg, &error_code) &&
|
||||
read_msg.function_idx == request->feature_idx &&
|
||||
read_msg.s.params[0] == request->function_idx) {
|
||||
g_set_error (error, 1, 0,
|
||||
"Unable to satisfy request, HID++ error %02x", error_code);
|
||||
ret = FALSE;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* avoid infinite loop when there is no response */
|
||||
remaining_time = HIDPP_DEVICE_READ_RESPONSE_TIMEOUT -
|
||||
(g_get_monotonic_time () / 1000 - begin_time);
|
||||
if (remaining_time <= 0) {
|
||||
g_set_error (error, 1, 0,
|
||||
"timeout while reading response");
|
||||
ret = FALSE;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* not our message, ignore it and try again */
|
||||
}
|
||||
|
||||
wrote = read (priv->fd, &read_msg, sizeof (*response));
|
||||
if (wrote <= 0) {
|
||||
g_set_error (error, 1, 0,
|
||||
"Unable to read response from device: %" G_GSIZE_FORMAT,
|
||||
wrote);
|
||||
ret = FALSE;
|
||||
goto out;
|
||||
}
|
||||
|
||||
hidpp_device_print_buffer (device, response);
|
||||
|
||||
// TODO: test for invalid reply
|
||||
#if 0
|
||||
if ((buf[0] != HIDPP_HEADER_REQUEST && buf[0] != HIDPP_HEADER_RESPONSE) ||
|
||||
buf[1] != device_idx ||
|
||||
buf[2] != feature_idx ||
|
||||
buf[3] != function_idx) {
|
||||
g_set_error (error, 1, 0,
|
||||
"invalid response from device: %" G_GSIZE_FORMAT,
|
||||
wrote);
|
||||
ret = FALSE;
|
||||
goto out;
|
||||
}
|
||||
#endif
|
||||
out:
|
||||
/* allow caller to check for protocol errors */
|
||||
memcpy (response, &read_msg, sizeof (read_msg));
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue