fwupd/plugins/logitech-hidpp/fu-logitech-hidpp-hidpp-msg.c
2019-11-04 13:35:36 -06:00

399 lines
12 KiB
C

/*
* Copyright (C) 2017-2018 Richard Hughes <richard@hughsie.com>
*
* SPDX-License-Identifier: LGPL-2.1+
*/
#include "config.h"
#include <string.h>
#include "fu-logitech-hidpp-hidpp.h"
#include "fu-logitech-hidpp-hidpp-msg.h"
FuLogitechHidPpHidppMsg *
fu_logitech_hidpp_msg_new (void)
{
return g_new0 (FuLogitechHidPpHidppMsg, 1);
}
const gchar *
fu_logitech_hidpp_msg_dev_id_to_string (FuLogitechHidPpHidppMsg *msg)
{
g_return_val_if_fail (msg != NULL, NULL);
if (msg->device_id == HIDPP_DEVICE_ID_WIRED)
return "wired";
if (msg->device_id == HIDPP_DEVICE_ID_RECEIVER)
return "receiver";
if (msg->device_id == HIDPP_DEVICE_ID_UNSET)
return "unset";
return NULL;
}
const gchar *
fu_logitech_hidpp_msg_rpt_id_to_string (FuLogitechHidPpHidppMsg *msg)
{
g_return_val_if_fail (msg != NULL, NULL);
if (msg->report_id == HIDPP_REPORT_ID_SHORT)
return "short";
if (msg->report_id == HIDPP_REPORT_ID_LONG)
return "long";
if (msg->report_id == HIDPP_REPORT_ID_VERY_LONG)
return "very-long";
return NULL;
}
gsize
fu_logitech_hidpp_msg_get_payload_length (FuLogitechHidPpHidppMsg *msg)
{
if (msg->report_id == HIDPP_REPORT_ID_SHORT)
return 0x07;
if (msg->report_id == HIDPP_REPORT_ID_LONG)
return 0x14;
if (msg->report_id == HIDPP_REPORT_ID_VERY_LONG)
return 0x2f;
if (msg->report_id == HIDPP_REPORT_NOTIFICATION)
return 0x08;
return 0x0;
}
const gchar *
fu_logitech_hidpp_msg_fcn_id_to_string (FuLogitechHidPpHidppMsg *msg)
{
g_return_val_if_fail (msg != NULL, NULL);
switch (msg->sub_id) {
case HIDPP_SUBID_SET_REGISTER:
case HIDPP_SUBID_GET_REGISTER:
case HIDPP_SUBID_SET_LONG_REGISTER:
case HIDPP_SUBID_GET_LONG_REGISTER:
case HIDPP_SUBID_SET_VERY_LONG_REGISTER:
case HIDPP_SUBID_GET_VERY_LONG_REGISTER:
if (msg->function_id == HIDPP_REGISTER_HIDPP_NOTIFICATIONS)
return "hidpp-notifications";
if (msg->function_id == HIDPP_REGISTER_ENABLE_INDIVIDUAL_FEATURES)
return "individual-features";
if (msg->function_id == HIDPP_REGISTER_BATTERY_STATUS)
return "battery-status";
if (msg->function_id == HIDPP_REGISTER_BATTERY_MILEAGE)
return "battery-mileage";
if (msg->function_id == HIDPP_REGISTER_PROFILE)
return "profile";
if (msg->function_id == HIDPP_REGISTER_LED_STATUS)
return "led-status";
if (msg->function_id == HIDPP_REGISTER_LED_INTENSITY)
return "led-intensity";
if (msg->function_id == HIDPP_REGISTER_LED_COLOR)
return "led-color";
if (msg->function_id == HIDPP_REGISTER_OPTICAL_SENSOR_SETTINGS)
return "optical-sensor-settings";
if (msg->function_id == HIDPP_REGISTER_CURRENT_RESOLUTION)
return "current-resolution";
if (msg->function_id == HIDPP_REGISTER_USB_REFRESH_RATE)
return "usb-refresh-rate";
if (msg->function_id == HIDPP_REGISTER_GENERIC_MEMORY_MANAGEMENT)
return "generic-memory-management";
if (msg->function_id == HIDPP_REGISTER_HOT_CONTROL)
return "hot-control";
if (msg->function_id == HIDPP_REGISTER_READ_MEMORY)
return "read-memory";
if (msg->function_id == HIDPP_REGISTER_DEVICE_CONNECTION_DISCONNECTION)
return "device-connection-disconnection";
if (msg->function_id == HIDPP_REGISTER_PAIRING_INFORMATION)
return "pairing-information";
if (msg->function_id == HIDPP_REGISTER_DEVICE_FIRMWARE_UPDATE_MODE)
return "device-firmware-update-mode";
if (msg->function_id == HIDPP_REGISTER_DEVICE_FIRMWARE_INFORMATION)
return "device-firmware-information";
break;
default:
break;
}
return NULL;
}
const gchar *
fu_logitech_hidpp_msg_sub_id_to_string (FuLogitechHidPpHidppMsg *msg)
{
g_return_val_if_fail (msg != NULL, NULL);
if (msg->sub_id == HIDPP_SUBID_VENDOR_SPECIFIC_KEYS)
return "vendor-specific-keys";
if (msg->sub_id == HIDPP_SUBID_POWER_KEYS)
return "power-keys";
if (msg->sub_id == HIDPP_SUBID_ROLLER)
return "roller";
if (msg->sub_id == HIDPP_SUBID_MOUSE_EXTRA_BUTTONS)
return "mouse-extra-buttons";
if (msg->sub_id == HIDPP_SUBID_BATTERY_CHARGING_LEVEL)
return "battery-charging-level";
if (msg->sub_id == HIDPP_SUBID_USER_INTERFACE_EVENT)
return "user-interface-event";
if (msg->sub_id == HIDPP_SUBID_F_LOCK_STATUS)
return "f-lock-status";
if (msg->sub_id == HIDPP_SUBID_CALCULATOR_RESULT)
return "calculator-result";
if (msg->sub_id == HIDPP_SUBID_MENU_NAVIGATE)
return "menu-navigate";
if (msg->sub_id == HIDPP_SUBID_FN_KEY)
return "fn-key";
if (msg->sub_id == HIDPP_SUBID_BATTERY_MILEAGE)
return "battery-mileage";
if (msg->sub_id == HIDPP_SUBID_UART_RX)
return "uart-rx";
if (msg->sub_id == HIDPP_SUBID_BACKLIGHT_DURATION_UPDATE)
return "backlight-duration-update";
if (msg->sub_id == HIDPP_SUBID_DEVICE_DISCONNECTION)
return "device-disconnection";
if (msg->sub_id == HIDPP_SUBID_DEVICE_CONNECTION)
return "device-connection";
if (msg->sub_id == HIDPP_SUBID_DEVICE_DISCOVERY)
return "device-discovery";
if (msg->sub_id == HIDPP_SUBID_PIN_CODE_REQUEST)
return "pin-code-request";
if (msg->sub_id == HIDPP_SUBID_RECEIVER_WORKING_MODE)
return "receiver-working-mode";
if (msg->sub_id == HIDPP_SUBID_ERROR_MESSAGE)
return "error-message";
if (msg->sub_id == HIDPP_SUBID_RF_LINK_CHANGE)
return "rf-link-change";
if (msg->sub_id == HIDPP_SUBID_HCI)
return "hci";
if (msg->sub_id == HIDPP_SUBID_LINK_QUALITY)
return "link-quality";
if (msg->sub_id == HIDPP_SUBID_DEVICE_LOCKING_CHANGED)
return "device-locking-changed";
if (msg->sub_id == HIDPP_SUBID_WIRELESS_DEVICE_CHANGE)
return "wireless-device-change";
if (msg->sub_id == HIDPP_SUBID_ACL)
return "acl";
if (msg->sub_id == HIDPP_SUBID_VOIP_TELEPHONY_EVENT)
return "voip-telephony-event";
if (msg->sub_id == HIDPP_SUBID_LED)
return "led";
if (msg->sub_id == HIDPP_SUBID_GESTURE_AND_AIR)
return "gesture-and-air";
if (msg->sub_id == HIDPP_SUBID_TOUCHPAD_MULTI_TOUCH)
return "touchpad-multi-touch";
if (msg->sub_id == HIDPP_SUBID_TRACEABILITY)
return "traceability";
if (msg->sub_id == HIDPP_SUBID_SET_REGISTER)
return "set-register";
if (msg->sub_id == HIDPP_SUBID_GET_REGISTER)
return "get-register";
if (msg->sub_id == HIDPP_SUBID_SET_LONG_REGISTER)
return "set-long-register";
if (msg->sub_id == HIDPP_SUBID_GET_LONG_REGISTER)
return "get-long-register";
if (msg->sub_id == HIDPP_SUBID_SET_VERY_LONG_REGISTER)
return "set-very-long-register";
if (msg->sub_id == HIDPP_SUBID_GET_VERY_LONG_REGISTER)
return "get-very-long-register";
if (msg->sub_id == HIDPP_SUBID_ERROR_MSG)
return "error-msg";
if (msg->sub_id == HIDPP_SUBID_ERROR_MSG_20)
return "error-msg-v2";
return NULL;
}
gboolean
fu_logitech_hidpp_msg_is_reply (FuLogitechHidPpHidppMsg *msg1, FuLogitechHidPpHidppMsg *msg2)
{
g_return_val_if_fail (msg1 != NULL, FALSE);
g_return_val_if_fail (msg2 != NULL, FALSE);
if (msg1->device_id != msg2->device_id &&
msg1->device_id != HIDPP_DEVICE_ID_UNSET &&
msg2->device_id != HIDPP_DEVICE_ID_UNSET)
return FALSE;
if (msg1->flags & FU_UNIFYING_HIDPP_MSG_FLAG_IGNORE_SUB_ID ||
msg2->flags & FU_UNIFYING_HIDPP_MSG_FLAG_IGNORE_SUB_ID)
return TRUE;
if (msg1->sub_id != msg2->sub_id)
return FALSE;
if (msg1->flags & FU_UNIFYING_HIDPP_MSG_FLAG_IGNORE_FNCT_ID ||
msg2->flags & FU_UNIFYING_HIDPP_MSG_FLAG_IGNORE_FNCT_ID)
return TRUE;
if (msg1->function_id != msg2->function_id)
return FALSE;
return TRUE;
}
/* HID++ error */
gboolean
fu_logitech_hidpp_msg_is_error (FuLogitechHidPpHidppMsg *msg, GError **error)
{
g_return_val_if_fail (msg != NULL, FALSE);
if (msg->sub_id == HIDPP_SUBID_ERROR_MSG) {
switch (msg->data[1]) {
case HIDPP_ERR_INVALID_SUBID:
g_set_error_literal (error,
G_IO_ERROR,
G_IO_ERROR_NOT_SUPPORTED,
"invalid SubID");
break;
case HIDPP_ERR_INVALID_ADDRESS:
g_set_error_literal (error,
G_IO_ERROR,
G_IO_ERROR_INVALID_DATA,
"invalid address");
break;
case HIDPP_ERR_INVALID_VALUE:
g_set_error_literal (error,
G_IO_ERROR,
G_IO_ERROR_INVALID_DATA,
"invalid value");
break;
case HIDPP_ERR_CONNECT_FAIL:
g_set_error_literal (error,
G_IO_ERROR,
G_IO_ERROR_FAILED,
"connection request failed");
break;
case HIDPP_ERR_TOO_MANY_DEVICES:
g_set_error_literal (error,
G_IO_ERROR,
G_IO_ERROR_NO_SPACE,
"too many devices connected");
break;
case HIDPP_ERR_ALREADY_EXISTS:
g_set_error_literal (error,
G_IO_ERROR,
G_IO_ERROR_EXISTS,
"already exists");
break;
case HIDPP_ERR_BUSY:
g_set_error_literal (error,
G_IO_ERROR,
G_IO_ERROR_BUSY,
"busy");
break;
case HIDPP_ERR_UNKNOWN_DEVICE:
g_set_error_literal (error,
G_IO_ERROR,
G_IO_ERROR_NOT_FOUND,
"unknown device");
break;
case HIDPP_ERR_RESOURCE_ERROR:
g_set_error_literal (error,
G_IO_ERROR,
G_IO_ERROR_HOST_UNREACHABLE,
"resource error");
break;
case HIDPP_ERR_REQUEST_UNAVAILABLE:
g_set_error_literal (error,
G_IO_ERROR,
G_IO_ERROR_EXISTS,
"request not valid in current context");
break;
case HIDPP_ERR_INVALID_PARAM_VALUE:
g_set_error_literal (error,
G_IO_ERROR,
G_IO_ERROR_INVALID_DATA,
"request parameter has unsupported value");
break;
case HIDPP_ERR_WRONG_PIN_CODE:
g_set_error_literal (error,
G_IO_ERROR,
G_IO_ERROR_CONNECTION_REFUSED,
"the pin code was wrong");
break;
default:
g_set_error_literal (error,
G_IO_ERROR,
G_IO_ERROR_FAILED,
"generic failure");
}
return FALSE;
}
if (msg->sub_id == HIDPP_SUBID_ERROR_MSG_20) {
switch (msg->data[1]) {
case HIDPP_ERROR_CODE_INVALID_ARGUMENT:
g_set_error (error,
G_IO_ERROR,
G_IO_ERROR_INVALID_ARGUMENT,
"Invalid argument 0x%02x",
msg->data[2]);
break;
case HIDPP_ERROR_CODE_OUT_OF_RANGE:
g_set_error_literal (error,
G_IO_ERROR,
G_IO_ERROR_INVALID_DATA,
"out of range");
break;
case HIDPP_ERROR_CODE_HW_ERROR:
g_set_error_literal (error,
G_IO_ERROR,
G_IO_ERROR_BROKEN_PIPE,
"hardware error");
break;
case HIDPP_ERROR_CODE_INVALID_FEATURE_INDEX:
g_set_error_literal (error,
G_IO_ERROR,
G_IO_ERROR_INVALID_ARGUMENT,
"invalid feature index");
break;
case HIDPP_ERROR_CODE_INVALID_FUNCTION_ID:
g_set_error_literal (error,
G_IO_ERROR,
G_IO_ERROR_INVALID_ARGUMENT,
"invalid function ID");
break;
case HIDPP_ERROR_CODE_BUSY:
g_set_error_literal (error,
G_IO_ERROR,
G_IO_ERROR_BUSY,
"busy");
break;
case HIDPP_ERROR_CODE_UNSUPPORTED:
g_set_error_literal (error,
G_IO_ERROR,
G_IO_ERROR_NOT_SUPPORTED,
"unsupported");
break;
default:
g_set_error_literal (error,
G_IO_ERROR,
G_IO_ERROR_FAILED,
"generic failure");
break;
}
return FALSE;
}
return TRUE;
}
void
fu_logitech_hidpp_msg_copy (FuLogitechHidPpHidppMsg *msg_dst, const FuLogitechHidPpHidppMsg *msg_src)
{
g_return_if_fail (msg_dst != NULL);
g_return_if_fail (msg_src != NULL);
memset (msg_dst->data, 0x00, sizeof(msg_dst->data));
msg_dst->device_id = msg_src->device_id;
msg_dst->sub_id = msg_src->sub_id;
msg_dst->function_id = msg_src->function_id;
memcpy (msg_dst->data, msg_src->data, sizeof(msg_dst->data));
}
/* filter HID++1.0 messages */
gboolean
fu_logitech_hidpp_msg_is_hidpp10_compat (FuLogitechHidPpHidppMsg *msg)
{
g_return_val_if_fail (msg != NULL, FALSE);
if (msg->sub_id == 0x40 ||
msg->sub_id == 0x41 ||
msg->sub_id == 0x49 ||
msg->sub_id == 0x4b ||
msg->sub_id == 0x8f) {
return TRUE;
}
return FALSE;
}
gboolean
fu_logitech_hidpp_msg_verify_swid (FuLogitechHidPpHidppMsg *msg)
{
g_return_val_if_fail (msg != NULL, FALSE);
if ((msg->function_id & 0x0f) != FU_UNIFYING_HIDPP_MSG_SW_ID)
return FALSE;
return TRUE;
}