mirror of
https://git.proxmox.com/git/fwupd
synced 2025-05-18 20:11:47 +00:00

Until now, the unifying plugin was a 'special snowflake' and did things in very different ways to the other plugins. Refactor each object to derive from either FuUsbDevice or FuUdevDevice, and then remove LuDevice and the LuContext layers.
399 lines
12 KiB
C
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-unifying-hidpp.h"
|
|
#include "fu-unifying-hidpp-msg.h"
|
|
|
|
FuUnifyingHidppMsg *
|
|
fu_unifying_hidpp_msg_new (void)
|
|
{
|
|
return g_new0 (FuUnifyingHidppMsg, 1);
|
|
}
|
|
|
|
const gchar *
|
|
fu_unifying_hidpp_msg_dev_id_to_string (FuUnifyingHidppMsg *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_unifying_hidpp_msg_rpt_id_to_string (FuUnifyingHidppMsg *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_unifying_hidpp_msg_get_payload_length (FuUnifyingHidppMsg *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_unifying_hidpp_msg_fcn_id_to_string (FuUnifyingHidppMsg *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_unifying_hidpp_msg_sub_id_to_string (FuUnifyingHidppMsg *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_unifying_hidpp_msg_is_reply (FuUnifyingHidppMsg *msg1, FuUnifyingHidppMsg *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_unifying_hidpp_msg_is_error (FuUnifyingHidppMsg *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_unifying_hidpp_msg_copy (FuUnifyingHidppMsg *msg_dst, const FuUnifyingHidppMsg *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_unifying_hidpp_msg_is_hidpp10_compat (FuUnifyingHidppMsg *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_unifying_hidpp_msg_verify_swid (FuUnifyingHidppMsg *msg)
|
|
{
|
|
g_return_val_if_fail (msg != NULL, FALSE);
|
|
if ((msg->function_id & 0x0f) != FU_UNIFYING_HIDPP_MSG_SW_ID)
|
|
return FALSE;
|
|
return TRUE;
|
|
}
|