fwupd/plugins/unifying/fu-unifying-hidpp-msg.c
Richard Hughes 9d3f791727 unifying: Use the new daemon-provided functionality
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.
2018-09-07 16:22:38 +01: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-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;
}