mirror of
https://git.proxmox.com/git/fwupd
synced 2025-05-16 02:34:37 +00:00
415 lines
12 KiB
C
415 lines
12 KiB
C
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
|
|
*
|
|
* Copyright (C) 2017 Richard Hughes <richard@hughsie.com>
|
|
*
|
|
* Licensed under the GNU Lesser General Public License Version 2.1
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include <gio/gio.h>
|
|
#include <string.h>
|
|
|
|
#include "lu-hidpp.h"
|
|
#include "lu-hidpp-msg.h"
|
|
|
|
LuHidppMsg *
|
|
lu_hidpp_msg_new (void)
|
|
{
|
|
return g_new0 (LuHidppMsg, 1);
|
|
}
|
|
|
|
const gchar *
|
|
lu_hidpp_msg_dev_id_to_string (LuHidppMsg *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 *
|
|
lu_hidpp_msg_rpt_id_to_string (LuHidppMsg *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
|
|
lu_hidpp_msg_get_payload_length (LuHidppMsg *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 *
|
|
lu_hidpp_msg_fcn_id_to_string (LuHidppMsg *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 *
|
|
lu_hidpp_msg_sub_id_to_string (LuHidppMsg *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
|
|
lu_hidpp_msg_is_reply (LuHidppMsg *msg1, LuHidppMsg *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 & LU_HIDPP_MSG_FLAG_IGNORE_SUB_ID ||
|
|
msg2->flags & LU_HIDPP_MSG_FLAG_IGNORE_SUB_ID)
|
|
return TRUE;
|
|
if (msg1->sub_id != msg2->sub_id)
|
|
return FALSE;
|
|
if (msg1->flags & LU_HIDPP_MSG_FLAG_IGNORE_FNCT_ID ||
|
|
msg2->flags & LU_HIDPP_MSG_FLAG_IGNORE_FNCT_ID)
|
|
return TRUE;
|
|
if (msg1->function_id != msg2->function_id)
|
|
return FALSE;
|
|
return TRUE;
|
|
}
|
|
|
|
/* HID++ error */
|
|
gboolean
|
|
lu_hidpp_msg_is_error (LuHidppMsg *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
|
|
lu_hidpp_msg_copy (LuHidppMsg *msg_dst, LuHidppMsg *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
|
|
lu_hidpp_msg_is_hidpp10_compat (LuHidppMsg *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
|
|
lu_hidpp_msg_verify_swid (LuHidppMsg *msg)
|
|
{
|
|
g_return_val_if_fail (msg != NULL, FALSE);
|
|
if ((msg->function_id & 0x0f) != LU_HIDPP_MSG_SW_ID)
|
|
return FALSE;
|
|
return TRUE;
|
|
}
|