diff --git a/contrib/ci/build_windows.sh b/contrib/ci/build_windows.sh index 92fad1431..1ea32dff8 100755 --- a/contrib/ci/build_windows.sh +++ b/contrib/ci/build_windows.sh @@ -7,6 +7,13 @@ export DESTDIR=${root}/dist build=$root/build-win32 rm -rf $DESTDIR $build +# For logitech bulk controller being disabled (-Dplugin_logitech_bulkcontroller=false): +# See https://bugzilla.redhat.com/show_bug.cgi?id=1991749 +# When fixed need to do the following to enable: +# 1. need to add mingw64-protobuf mingw64-protobuf-tools to CI build deps +# 2. add protoc = /path/to/protoc-c.exe in mingw64.cross +# 3. Only enable when not a tagged release (Unsupported by Logitech) + #build mkdir -p $build $DESTDIR && cd $build meson .. \ @@ -22,6 +29,7 @@ meson .. \ -Dplugin_redfish=false \ -Dplugin_altos=false \ -Dplugin_dell=false \ + -Dplugin_logitech_bulkcontroller=false \ -Dplugin_nvme=false \ -Dplugin_parade_lspcon=false \ -Dplugin_realtek_mst=false \ diff --git a/contrib/ci/dependencies.xml b/contrib/ci/dependencies.xml index dd15781d4..deca01370 100644 --- a/contrib/ci/dependencies.xml +++ b/contrib/ci/dependencies.xml @@ -1553,4 +1553,41 @@ ShellCheck + + + protobuf-c + + + + + + + + + + + + protobuf-c-devel + + + protobuf-c-devel + + + + + + + + + + + + + + + + + protobuf + + diff --git a/contrib/freebsd/Makefile b/contrib/freebsd/Makefile index 85980f8c2..369263d56 100644 --- a/contrib/freebsd/Makefile +++ b/contrib/freebsd/Makefile @@ -26,6 +26,7 @@ LIB_DEPENDS= libcurl.so:ftp/curl \ libgusb.so:devel/libgusb \ libjcat.so:textproc/libjcat \ libjson-glib-1.0.so:devel/json-glib \ + libprotobuf-c.so:devel/protobuf-c \ libxmlb.so:textproc/libxmlb \ libefiboot.so:devel/gnu-efi diff --git a/contrib/fwupd.spec.in b/contrib/fwupd.spec.in index ba3fa6e53..d9555ee4c 100644 --- a/contrib/fwupd.spec.in +++ b/contrib/fwupd.spec.in @@ -481,6 +481,7 @@ done %{_libdir}/fwupd-plugins-%{fwupdplugin_version}/libfu_plugin_uefi_recovery.so %endif %{_libdir}/fwupd-plugins-%{fwupdplugin_version}/libfu_plugin_logind.so +%{_libdir}/fwupd-plugins-%{fwupdplugin_version}/libfu_plugin_logitech_bulkcontroller.so %{_libdir}/fwupd-plugins-%{fwupdplugin_version}/libfu_plugin_logitech_hidpp.so %{_libdir}/fwupd-plugins-%{fwupdplugin_version}/libfu_plugin_upower.so %{_libdir}/fwupd-plugins-%{fwupdplugin_version}/libfu_plugin_vli.so diff --git a/meson.build b/meson.build index 84702a981..2ac22cd44 100644 --- a/meson.build +++ b/meson.build @@ -544,6 +544,12 @@ if get_option('libarchive') plugin_deps += libarchive endif +if get_option('plugin_logitech_bulkcontroller') + protobufc = dependency('libprotobuf-c') + protoc = find_program('protoc', 'protoc-c') + plugin_deps += protobufc +endif + root_incdir = include_directories('.') if get_option('docs') == 'gtkdoc' diff --git a/meson_options.txt b/meson_options.txt index 4e7afb18b..639724c22 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -17,6 +17,7 @@ option('plugin_amt', type : 'boolean', value : true, description : 'enable Intel option('plugin_dell', type : 'boolean', value : true, description : 'enable Dell-specific support') option('plugin_dummy', type : 'boolean', value : false, description : 'enable the dummy device') option('plugin_emmc', type : 'boolean', value : true, description : 'enable eMMC support') +option('plugin_logitech_bulkcontroller', type : 'boolean', value : true, description : 'enable Logitech bulk controller support') option('plugin_parade_lspcon', type : 'boolean', value : true, description : 'enable Parade LSPCON support') option('plugin_realtek_mst', type : 'boolean', value : true, description : 'enable Realtek MST hub support') option('plugin_synaptics_mst', type: 'boolean', value: true, description : 'enable Synaptics MST hub support') diff --git a/plugins/logitech-bulkcontroller/README.md b/plugins/logitech-bulkcontroller/README.md new file mode 100644 index 000000000..67df69a59 --- /dev/null +++ b/plugins/logitech-bulkcontroller/README.md @@ -0,0 +1,40 @@ +# Logitech Video Collaboration + +## Introduction + +This plugin can upgrade the firmware on Logitech Video Collaboration products (Rally Bar and Rally Bar Mini), using USB bulk transfer. + +## Firmware Format + +The daemon will decompress the cabinet archive and extract a firmware blob in +a packed binary file format. + +This plugin supports the following protocol ID: + +* com.logitech.vc.proto + +## GUID Generation + +These devices use the standard USB DeviceInstanceId values, e.g. + +* `USB\VID_046D&PID_089B` +* `USB\VID_046D&PID_08D3` + +## Quirk Use + +This plugin uses the following plugin-specific quirks: + +## Update Behavior + +The peripheral firmware is deployed when the device is in normal runtime mode, +and the device will reset when the new firmware has been written. + +## Design Notes + +## Vendor ID Security + +The vendor ID is set from the USB vendor, in this instance set to `USB:0x046D` + +## External Interface Access + +This plugin requires read/write access to `/dev/bus/usb`. diff --git a/plugins/logitech-bulkcontroller/fu-logitech-bulkcontroller-common.c b/plugins/logitech-bulkcontroller/fu-logitech-bulkcontroller-common.c new file mode 100644 index 000000000..b5d48fa15 --- /dev/null +++ b/plugins/logitech-bulkcontroller/fu-logitech-bulkcontroller-common.c @@ -0,0 +1,208 @@ +/* + * Copyright (c) 1999-2021 Logitech, Inc. + * All Rights Reserved + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fu-logitech-bulkcontroller-common.h" +#include "usb_msg.pb-c.h" + +static void +proto_manager_set_header(Logi__Device__Proto__Header *header_msg) +{ + gint64 timestamp_tv; + + g_return_if_fail(header_msg != NULL); + + timestamp_tv = g_get_real_time(); + header_msg->id = g_uuid_string_random(); + header_msg->timestamp = g_strdup_printf("%" G_GINT64_FORMAT, timestamp_tv / 1000); +} + +GByteArray * +proto_manager_generate_get_device_info_request(void) +{ + GByteArray *buf = g_byte_array_new(); + Logi__Device__Proto__Header header_msg = LOGI__DEVICE__PROTO__HEADER__INIT; + Logi__Device__Proto__GetDeviceInfoRequest get_deviceinfo_msg = + LOGI__DEVICE__PROTO__GET_DEVICE_INFO_REQUEST__INIT; + Logi__Device__Proto__Request request_msg = { + PROTOBUF_C_MESSAGE_INIT(&logi__device__proto__request__descriptor), + LOGI__DEVICE__PROTO__REQUEST__PAYLOAD_GET_DEVICE_INFO_REQUEST, + {&get_deviceinfo_msg}}; + Logi__Device__Proto__UsbMsg usb_msg = LOGI__DEVICE__PROTO__USB_MSG__INIT; + + proto_manager_set_header(&header_msg); + usb_msg.header = &header_msg; + usb_msg.message_case = LOGI__DEVICE__PROTO__USB_MSG__MESSAGE_REQUEST; + usb_msg.request = &request_msg; + + fu_byte_array_set_size(buf, logi__device__proto__usb_msg__get_packed_size(&usb_msg)); + logi__device__proto__usb_msg__pack(&usb_msg, (unsigned char *)buf->data); + return buf; +} + +GByteArray * +proto_manager_generate_transition_to_device_mode_request(void) +{ + GByteArray *buf = g_byte_array_new(); + Logi__Device__Proto__Header header_msg = LOGI__DEVICE__PROTO__HEADER__INIT; + Logi__Device__Proto__TransitionToDeviceModeRequest transition_to_device_mode_msg = + LOGI__DEVICE__PROTO__TRANSITION_TO_DEVICE_MODE_REQUEST__INIT; + Logi__Device__Proto__Request request_msg = { + PROTOBUF_C_MESSAGE_INIT(&logi__device__proto__request__descriptor), + LOGI__DEVICE__PROTO__REQUEST__PAYLOAD_TRANSITION_TO_DEVICEMODE_REQUEST, + {(Logi__Device__Proto__GetDeviceInfoRequest *)&transition_to_device_mode_msg}}; + Logi__Device__Proto__UsbMsg usb_msg = LOGI__DEVICE__PROTO__USB_MSG__INIT; + + proto_manager_set_header(&header_msg); + usb_msg.header = &header_msg; + usb_msg.message_case = LOGI__DEVICE__PROTO__USB_MSG__MESSAGE_REQUEST; + usb_msg.request = &request_msg; + + fu_byte_array_set_size(buf, logi__device__proto__usb_msg__get_packed_size(&usb_msg)); + logi__device__proto__usb_msg__pack(&usb_msg, (unsigned char *)buf->data); + return buf; +} + +GByteArray * +proto_manager_decode_message(const guint8 *data, + guint32 len, + FuLogitechBulkcontrollerProtoId *proto_id, + GError **error) +{ + g_autoptr(GByteArray) buf_decoded = g_byte_array_new(); + + Logi__Device__Proto__UsbMsg *usb_msg = + logi__device__proto__usb_msg__unpack(NULL, len, (const unsigned char *)data); + if (usb_msg == NULL) { + g_set_error_literal(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "unable to unpack data"); + return NULL; + } + + switch (usb_msg->message_case) { + case LOGI__DEVICE__PROTO__USB_MSG__MESSAGE_ACK: + *proto_id = kProtoId_Ack; + break; + case LOGI__DEVICE__PROTO__USB_MSG__MESSAGE_RESPONSE: + if (!usb_msg->response) { + g_set_error_literal(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "no USB response"); + return NULL; + } + switch (usb_msg->response->payload_case) { + case LOGI__DEVICE__PROTO__RESPONSE__PAYLOAD_GET_DEVICE_INFO_RESPONSE: + if (usb_msg->response->get_device_info_response) { + const gchar *tmp = + usb_msg->response->get_device_info_response->payload; + *proto_id = kProtoId_GetDeviceInfoResponse; + if (tmp != NULL) + g_byte_array_append(buf_decoded, + (const guint8 *)tmp, + strlen(tmp)); + } + break; + case LOGI__DEVICE__PROTO__RESPONSE__PAYLOAD_TRANSITION_TO_DEVICEMODE_RESPONSE: + if (usb_msg->response->transition_to_devicemode_response) { + *proto_id = kProtoId_TransitionToDeviceModeResponse; + fu_byte_array_append_uint8( + buf_decoded, + usb_msg->response->transition_to_devicemode_response->success); + } + break; + default: + break; + }; + break; + case LOGI__DEVICE__PROTO__USB_MSG__MESSAGE_EVENT: + if (!usb_msg->response) { + g_set_error_literal(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "no USB event"); + return NULL; + } + switch (usb_msg->event->payload_case) { + case LOGI__DEVICE__PROTO__EVENT__PAYLOAD_KONG_EVENT: + if (usb_msg->event->kong_event) { + const gchar *tmp = usb_msg->event->kong_event->mqtt_event; + *proto_id = kProtoId_KongEvent; + if (tmp != NULL) + g_byte_array_append(buf_decoded, + (const guint8 *)tmp, + strlen(tmp)); + } + break; + case LOGI__DEVICE__PROTO__EVENT__PAYLOAD_HANDSHAKE_EVENT: + if (usb_msg->event->handshake_event) { + *proto_id = kProtoId_HandshakeEvent; + } + break; + case LOGI__DEVICE__PROTO__EVENT__PAYLOAD_CRASH_DUMP_AVAILABLE_EVENT: + *proto_id = kProtoId_CrashDumpAvailableEvent; + break; + default: + break; + }; + break; + default: + break; + }; + logi__device__proto__usb_msg__free_unpacked(usb_msg, NULL); + return g_steal_pointer(&buf_decoded); +} + +const gchar * +fu_logitech_bulkcontroller_device_status_to_string(FuLogitechBulkcontrollerDeviceStatus status) +{ + if (status == kDeviceStateUnknown) + return "Unknown"; + if (status == kDeviceStateOffline) + return "Offline"; + if (status == kDeviceStateOnline) + return "Online"; + if (status == kDeviceStateIdle) + return "Idle"; + if (status == kDeviceStateInUse) + return "InUse"; + if (status == kDeviceStateAudioOnly) + return "AudioOnly"; + if (status == kDeviceStateEnumerating) + return "Enumerating"; + return NULL; +} + +const gchar * +fu_logitech_bulkcontroller_device_update_state_to_string( + FuLogitechBulkcontrollerDeviceUpdateState update_state) +{ + if (update_state == kUpdateStateUnknown) + return "Unknown"; + if (update_state == kUpdateStateCurrent) + return "Current"; + if (update_state == kUpdateStateAvailable) + return "Available"; + if (update_state == kUpdateStateStarting) + return "Starting"; + if (update_state == kUpdateStateDownloading) + return "Downloading"; + if (update_state == kUpdateStateReady) + return "Ready"; + if (update_state == kUpdateStateUpdating) + return "Updating"; + if (update_state == kUpdateStateScheduled) + return "Scheduled"; + if (update_state == kUpdateStateError) + return "Error"; + return NULL; +} diff --git a/plugins/logitech-bulkcontroller/fu-logitech-bulkcontroller-common.h b/plugins/logitech-bulkcontroller/fu-logitech-bulkcontroller-common.h new file mode 100644 index 000000000..fa397e77a --- /dev/null +++ b/plugins/logitech-bulkcontroller/fu-logitech-bulkcontroller-common.h @@ -0,0 +1,59 @@ +/* + * Copyright (c) 1999-2021 Logitech, Inc. + * All Rights Reserved + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#include "usb_msg.pb-c.h" + +typedef enum { + kDeviceStateUnknown = -1, + kDeviceStateOffline, + kDeviceStateOnline, + kDeviceStateIdle, + kDeviceStateInUse, + kDeviceStateAudioOnly, + kDeviceStateEnumerating +} FuLogitechBulkcontrollerDeviceStatus; + +typedef enum { + kUpdateStateUnknown = -1, + kUpdateStateCurrent, + kUpdateStateAvailable, + kUpdateStateStarting = 3, + kUpdateStateDownloading, + kUpdateStateReady, + kUpdateStateUpdating, + kUpdateStateScheduled, + kUpdateStateError +} FuLogitechBulkcontrollerDeviceUpdateState; + +typedef enum { + kProtoId_UnknownId, + kProtoId_GetDeviceInfoResponse, + kProtoId_TransitionToDeviceModeResponse, + kProtoId_Ack, + kProtoId_KongEvent, + kProtoId_HandshakeEvent, + kProtoId_CrashDumpAvailableEvent +} FuLogitechBulkcontrollerProtoId; + +const gchar * +fu_logitech_bulkcontroller_device_status_to_string(FuLogitechBulkcontrollerDeviceStatus status); +const gchar * +fu_logitech_bulkcontroller_device_update_state_to_string( + FuLogitechBulkcontrollerDeviceUpdateState update_state); +GByteArray * +proto_manager_generate_get_device_info_request(void); +GByteArray * +proto_manager_generate_transition_to_device_mode_request(void); +GByteArray * +proto_manager_decode_message(const guint8 *data, + guint32 len, + FuLogitechBulkcontrollerProtoId *proto_id, + GError **error); diff --git a/plugins/logitech-bulkcontroller/fu-logitech-bulkcontroller-device.c b/plugins/logitech-bulkcontroller/fu-logitech-bulkcontroller-device.c new file mode 100644 index 000000000..b4fc61c1a --- /dev/null +++ b/plugins/logitech-bulkcontroller/fu-logitech-bulkcontroller-device.c @@ -0,0 +1,704 @@ +/* + * Copyright (c) 1999-2021 Logitech, Inc. + * Copyright (C) 2021 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include +#include + +#include "fu-logitech-bulkcontroller-common.h" +#include "fu-logitech-bulkcontroller-device.h" + +/* SYNC interface follows TLSV (Type, Length, SequenceID, Value) protocol */ +/* UPD interface follows TLV (Type, Length, Value) protocol */ +/* Payload size limited to 8k for both interfaces */ +#define UPD_PACKET_HEADER_SIZE (2 * sizeof(guint32)) +#define SYNC_PACKET_HEADER_SIZE (3 * sizeof(guint32)) +#define WRITE_TIME_OUT 100 +#define HASH_TIMEOUT 30000 +#define MAX_DATA_SIZE 8192 /* 8k */ +#define PAYLOAD_SIZE MAX_DATA_SIZE - UPD_PACKET_HEADER_SIZE +#define UPD_INTERFACE_SUBPROTOCOL_ID 117 +#define SYNC_INTERFACE_SUBPROTOCOL_ID 118 +#define BULK_TRANSFER_TIMEOUT 1000 +#define HASH_VALUE_SIZE 16 +#define LENGTH_OFFSET 0x4 +#define COMMAND_OFFSET 0x0 +#define SYNC_ACK_PAYLOAD_LENGTH 5 + +enum { SHA_256, SHA_512, MD5 }; + +enum { EP_OUT, EP_IN, EP_LAST }; + +enum { BULK_INTERFACE_UPD, BULK_INTERFACE_SYNC }; + +typedef enum { + CMD_CHECK_BUFFERSIZE = 0xCC00, + CMD_INIT = 0xCC01, + CMD_START_TRANSFER = 0xCC02, + CMD_DATA_TRANSFER = 0xCC03, + CMD_END_TRANSFER = 0xCC04, + CMD_UNINIT = 0xCC05, + CMD_BUFFER_READ = 0xCC06, + CMD_BUFFER_WRITE = 0xCC07, + CMD_UNINIT_BUFFER = 0xCC08, + CMD_ACK = 0xFF01, + CMD_TIMEOUT = 0xFF02, + CMD_NACK = 0xFF03 +} UsbCommands; + +struct _FuLogitechBulkcontrollerDevice { + FuUsbDevice parent_instance; + guint sync_ep[EP_LAST]; + guint update_ep[EP_LAST]; + guint sync_iface; + guint update_iface; + FuLogitechBulkcontrollerDeviceStatus status; + FuLogitechBulkcontrollerDeviceUpdateState update_status; +}; + +G_DEFINE_TYPE(FuLogitechBulkcontrollerDevice, fu_logitech_bulkcontroller_device, FU_TYPE_USB_DEVICE) + +static void +fu_logitech_bulkcontroller_device_to_string(FuDevice *device, guint idt, GString *str) +{ + FuLogitechBulkcontrollerDevice *self = FU_LOGITECH_BULKCONTROLLER_DEVICE(device); + fu_common_string_append_kx(str, idt, "SyncIface", self->sync_iface); + fu_common_string_append_kx(str, idt, "UpdateIface", self->update_iface); + fu_common_string_append_kv( + str, + idt, + "Status", + fu_logitech_bulkcontroller_device_status_to_string(self->status)); + fu_common_string_append_kv( + str, + idt, + "UpdateState", + fu_logitech_bulkcontroller_device_update_state_to_string(self->update_status)); +} + +static gboolean +fu_logitech_bulkcontroller_device_probe(FuDevice *device, GError **error) +{ + FuLogitechBulkcontrollerDevice *self = FU_LOGITECH_BULKCONTROLLER_DEVICE(device); + g_autoptr(GPtrArray) intfs = NULL; + + intfs = g_usb_device_get_interfaces(fu_usb_device_get_dev(FU_USB_DEVICE(self)), error); + if (intfs == NULL) + return FALSE; + for (guint i = 0; i < intfs->len; i++) { + GUsbInterface *intf = g_ptr_array_index(intfs, i); + if (g_usb_interface_get_class(intf) == G_USB_DEVICE_CLASS_VENDOR_SPECIFIC && + g_usb_interface_get_protocol(intf) == 0x1) { + if (g_usb_interface_get_subclass(intf) == SYNC_INTERFACE_SUBPROTOCOL_ID) { + g_autoptr(GPtrArray) endpoints = + g_usb_interface_get_endpoints(intf); + self->sync_iface = g_usb_interface_get_number(intf); + if (endpoints == NULL) + continue; + for (guint j = 0; j < endpoints->len; j++) { + GUsbEndpoint *ep = g_ptr_array_index(endpoints, j); + if (j == EP_OUT) + self->sync_ep[EP_OUT] = + g_usb_endpoint_get_address(ep); + else + self->sync_ep[EP_IN] = + g_usb_endpoint_get_address(ep); + } + } else if (g_usb_interface_get_subclass(intf) == + UPD_INTERFACE_SUBPROTOCOL_ID) { + g_autoptr(GPtrArray) endpoints = + g_usb_interface_get_endpoints(intf); + self->sync_iface = g_usb_interface_get_number(intf); + if (endpoints == NULL) + continue; + for (guint j = 0; j < endpoints->len; j++) { + GUsbEndpoint *ep = g_ptr_array_index(endpoints, j); + if (j == EP_OUT) + self->update_ep[EP_OUT] = + g_usb_endpoint_get_address(ep); + else + self->update_ep[EP_IN] = + g_usb_endpoint_get_address(ep); + } + } + } + } + return TRUE; +} + +static gboolean +fu_logitech_bulkcontroller_device_send(FuLogitechBulkcontrollerDevice *self, + GByteArray *buf, + gint interface_id, + GError **error) +{ + gsize transferred = 0; + gint ep; + GCancellable *cancellable = NULL; + g_return_val_if_fail(buf != NULL, FALSE); + + if (interface_id == BULK_INTERFACE_SYNC) { + ep = self->sync_ep[EP_OUT]; + } else if (interface_id == BULK_INTERFACE_UPD) { + ep = self->update_ep[EP_OUT]; + } else { + g_set_error_literal(error, G_IO_ERROR, G_IO_ERROR_FAILED, "interface is invalid"); + return FALSE; + } + if (!g_usb_device_bulk_transfer(fu_usb_device_get_dev(FU_USB_DEVICE(self)), + ep, + (guint8 *)buf->data, + buf->len, + &transferred, + WRITE_TIME_OUT, + cancellable, + error)) { + g_prefix_error(error, "bulk transfer failed: "); + return FALSE; + } + return TRUE; +} + +static gboolean +fu_logitech_bulkcontroller_device_recv(FuLogitechBulkcontrollerDevice *self, + GByteArray *buf, + gint interface_id, + guint timeout, + GError **error) +{ + gsize received_length = 0; + gint ep; + + g_return_val_if_fail(buf != NULL, FALSE); + + if (interface_id == BULK_INTERFACE_SYNC) { + ep = self->sync_ep[EP_IN]; + } else if (interface_id == BULK_INTERFACE_UPD) { + ep = self->update_ep[EP_IN]; + } else { + g_set_error_literal(error, G_IO_ERROR, G_IO_ERROR_FAILED, "interface is invalid"); + return FALSE; + } + if (!g_usb_device_bulk_transfer(fu_usb_device_get_dev(FU_USB_DEVICE(self)), + ep, + buf->data, + buf->len, + &received_length, + timeout, + NULL, + error)) { + g_prefix_error(error, "bulk transfer failed: "); + return FALSE; + } + return TRUE; +} + +static gboolean +fu_logitech_bulkcontroller_device_send_upd_cmd(FuLogitechBulkcontrollerDevice *self, + guint32 cmd, + GByteArray *buf, + GError **error) +{ + guint32 cmd_tmp = 0x0; + guint timeout = BULK_TRANSFER_TIMEOUT; + g_autoptr(GByteArray) buf_pkt = g_byte_array_new(); + g_autoptr(GByteArray) buf_ack = g_byte_array_new(); + + fu_byte_array_append_uint32(buf_pkt, cmd, G_LITTLE_ENDIAN); /* Type(T) : Command type */ + fu_byte_array_append_uint32(buf_pkt, + buf != NULL ? buf->len : 0, + G_LITTLE_ENDIAN); /*Length(L) : Length of payload */ + if (buf != NULL) { + g_byte_array_append(buf_pkt, + buf->data, + buf->len); /* Value(V) : Actual payload data */ + } + if (!fu_logitech_bulkcontroller_device_send(self, buf_pkt, BULK_INTERFACE_UPD, error)) + return FALSE; + + /* receiving INIT ACK */ + fu_byte_array_set_size(buf_ack, MAX_DATA_SIZE); + + /* Extending the bulk transfer timeout value, as android device takes some time to + calculate Hash and respond */ + if (CMD_END_TRANSFER == cmd) + timeout = HASH_TIMEOUT; + + if (!fu_logitech_bulkcontroller_device_recv(self, + buf_ack, + BULK_INTERFACE_UPD, + timeout, + error)) + return FALSE; + + if (!fu_common_read_uint32_safe(buf_ack->data, + buf_ack->len, + COMMAND_OFFSET, + &cmd_tmp, + G_LITTLE_ENDIAN, + error)) + return FALSE; + if (cmd_tmp != CMD_ACK) { + g_set_error(error, G_IO_ERROR, G_IO_ERROR_FAILED, "not CMD_ACK, got %x", cmd); + return FALSE; + } + if (!fu_common_read_uint32_safe(buf_ack->data, + buf_ack->len, + UPD_PACKET_HEADER_SIZE, + &cmd_tmp, + G_LITTLE_ENDIAN, + error)) + return FALSE; + if (cmd_tmp != cmd) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "invalid upd message received, expected %x, got %x", + cmd, + cmd_tmp); + return FALSE; + } + return TRUE; +} + +static gboolean +fu_logitech_bulkcontroller_device_send_sync_cmd(FuLogitechBulkcontrollerDevice *self, + guint32 cmd, + GByteArray *buf, + GError **error) +{ + guint32 cmd_tmp = 0x0; + guint64 cmd_tmp_64 = 0x0; + guint32 verify_cmd = 0x0; + gchar ack_payload[SYNC_ACK_PAYLOAD_LENGTH] = {0x0}; + g_autoptr(GByteArray) buf_pkt = g_byte_array_new(); + g_autoptr(GByteArray) buf_ack = g_byte_array_new(); + + fu_byte_array_append_uint32(buf_pkt, cmd, G_LITTLE_ENDIAN); /* Type(T) : Command type */ + fu_byte_array_append_uint32(buf_pkt, + buf != NULL ? buf->len : 0, + G_LITTLE_ENDIAN); /*Length(L) : Length of payload */ + fu_byte_array_append_uint32(buf_pkt, + g_random_int_range(0, G_MAXUINT16), + G_LITTLE_ENDIAN); /*Sequence(S) : Sequence ID of the data */ + if (buf != NULL) { + g_byte_array_append(buf_pkt, + buf->data, + buf->len); /* Value(V) : Actual payload data */ + } + if (!fu_logitech_bulkcontroller_device_send(self, buf_pkt, BULK_INTERFACE_SYNC, error)) + return FALSE; + + /* receiving ACK */ + fu_byte_array_set_size(buf_ack, MAX_DATA_SIZE); + if (!fu_logitech_bulkcontroller_device_recv(self, + buf_ack, + BULK_INTERFACE_SYNC, + BULK_TRANSFER_TIMEOUT, + error)) + return FALSE; + if (!fu_common_read_uint32_safe(buf_ack->data, + buf_ack->len, + COMMAND_OFFSET, + &cmd_tmp, + G_LITTLE_ENDIAN, + error)) + return FALSE; + if (cmd_tmp != CMD_ACK) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "not sync CMD_ACK, got %x", + cmd_tmp); + return FALSE; + } + if (!fu_common_read_uint64_safe(buf_ack->data, + buf_ack->len, + SYNC_PACKET_HEADER_SIZE, + &cmd_tmp_64, + G_LITTLE_ENDIAN, + error)) + return FALSE; + if (!fu_memcpy_safe((guint8 *)ack_payload, + sizeof(ack_payload), + 0x0, + (guint8 *)&cmd_tmp_64, + sizeof(cmd_tmp_64), + 0x0, + SYNC_ACK_PAYLOAD_LENGTH, + error)) { + g_prefix_error(error, "failed to retrieve payload data: "); + return FALSE; + } + verify_cmd = fu_common_strtoull(ack_payload); + if (verify_cmd != cmd) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "invalid sync message payload received, expected %x, got %x", + cmd, + verify_cmd); + return FALSE; + } + return TRUE; +} + +static gboolean +fu_logitech_bulkcontroller_device_recv_sync_cmd(FuLogitechBulkcontrollerDevice *self, + guint32 cmd, + GByteArray *buf, + GError **error) +{ + guint32 cmd_tmp = 0x0; + guint32 response_length = 0; + g_autoptr(GByteArray) buf_pkt = g_byte_array_new(); + g_autoptr(GByteArray) buf_ack = g_byte_array_new(); + + fu_byte_array_set_size(buf_pkt, MAX_DATA_SIZE); + if (!fu_logitech_bulkcontroller_device_recv(self, + buf_pkt, + BULK_INTERFACE_SYNC, + BULK_TRANSFER_TIMEOUT, + error)) + return FALSE; + if (!fu_common_read_uint32_safe(buf_pkt->data, + buf_pkt->len, + COMMAND_OFFSET, + &cmd_tmp, + G_LITTLE_ENDIAN, + error)) + return FALSE; + if (cmd_tmp != cmd) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "invalid sync message received, expected %x, got %x", + cmd, + cmd_tmp); + return FALSE; + } + if (!fu_common_read_uint32_safe(buf_pkt->data, + buf_pkt->len, + LENGTH_OFFSET, + &response_length, + G_LITTLE_ENDIAN, + error)) + return FALSE; + + fu_byte_array_append_uint32(buf_ack, CMD_ACK, G_LITTLE_ENDIAN); /* ACK message */ + fu_byte_array_append_uint32(buf_ack, sizeof(guint32), G_LITTLE_ENDIAN); /* Length */ + fu_byte_array_append_uint32(buf_ack, 0, G_LITTLE_ENDIAN); /* Sequence ID */ + fu_byte_array_append_uint32(buf_ack, cmd, G_LITTLE_ENDIAN); /* Payload */ + if (!fu_logitech_bulkcontroller_device_send(self, buf_ack, BULK_INTERFACE_SYNC, error)) + return FALSE; + if (buf != NULL) + g_byte_array_append(buf, buf_pkt->data + SYNC_PACKET_HEADER_SIZE, response_length); + + return TRUE; +} + +static gchar * +fu_logitech_bulkcontroller_device_compute_hash(GBytes *data) +{ + guint8 md5buf[HASH_VALUE_SIZE] = {0}; + gsize data_len = sizeof(md5buf); + GChecksum *checksum = g_checksum_new(G_CHECKSUM_MD5); + g_checksum_update(checksum, g_bytes_get_data(data, NULL), g_bytes_get_size(data)); + g_checksum_get_digest(checksum, (guint8 *)&md5buf, &data_len); + return g_base64_encode(md5buf, sizeof(md5buf)); +} + +static gboolean +fu_logitech_bulkcontroller_device_write_firmware(FuDevice *device, + FuFirmware *firmware, + FwupdInstallFlags flags, + GError **error) +{ + FuLogitechBulkcontrollerDevice *self = FU_LOGITECH_BULKCONTROLLER_DEVICE(device); + g_autofree gchar *base64hash = NULL; + g_autoptr(GByteArray) end_pkt = g_byte_array_new(); + g_autoptr(GByteArray) start_pkt = g_byte_array_new(); + g_autoptr(GBytes) fw = NULL; + g_autoptr(GPtrArray) chunks = NULL; + + /* get default image */ + fw = fu_firmware_get_bytes(firmware, error); + if (fw == NULL) + return FALSE; + + /* Sending INIT */ + if (!fu_logitech_bulkcontroller_device_send_upd_cmd(self, CMD_INIT, NULL, error)) { + g_prefix_error(error, "error in writing init transfer packet: "); + return FALSE; + } + + /* transfer sent */ + fu_device_set_status(device, FWUPD_STATUS_DEVICE_WRITE); + fu_byte_array_append_uint64(start_pkt, g_bytes_get_size(fw), G_LITTLE_ENDIAN); + if (!fu_logitech_bulkcontroller_device_send_upd_cmd(self, + CMD_START_TRANSFER, + start_pkt, + error)) { + g_prefix_error(error, "error in writing start transfer packet: "); + return FALSE; + } + + /* each block */ + chunks = fu_chunk_array_new_from_bytes(fw, 0x0, 0x0, PAYLOAD_SIZE); + for (guint i = 0; i < chunks->len; i++) { + FuChunk *chk = g_ptr_array_index(chunks, i); + g_autoptr(GByteArray) data_pkt = g_byte_array_new(); + g_byte_array_append(data_pkt, fu_chunk_get_data(chk), fu_chunk_get_data_sz(chk)); + if (!fu_logitech_bulkcontroller_device_send_upd_cmd(self, + CMD_DATA_TRANSFER, + data_pkt, + error)) { + g_prefix_error(error, "failed to send data packet 0x%x: ", i); + return FALSE; + } + fu_device_set_progress_full(FU_DEVICE(self), i + 1, chunks->len); + } + + /* sending end transfer */ + base64hash = fu_logitech_bulkcontroller_device_compute_hash(fw); + fu_byte_array_append_uint32(end_pkt, 1, G_LITTLE_ENDIAN); /* update */ + fu_byte_array_append_uint32(end_pkt, 0, G_LITTLE_ENDIAN); /* force */ + fu_byte_array_append_uint32(end_pkt, MD5, G_LITTLE_ENDIAN); /* checksum type */ + g_byte_array_append(end_pkt, (const guint8 *)base64hash, strlen(base64hash)); + if (!fu_logitech_bulkcontroller_device_send_upd_cmd(self, + CMD_END_TRANSFER, + end_pkt, + error)) { + g_prefix_error(error, "error in writing end transfer transfer packet: "); + return FALSE; + } + + /* send uninit */ + if (!fu_logitech_bulkcontroller_device_send_upd_cmd(self, CMD_UNINIT, NULL, error)) { + g_prefix_error(error, "error in writing finish transfer packet: "); + return FALSE; + } + + /* success! */ + return TRUE; +} + +static gboolean +fu_logitech_bulkcontroller_device_open(FuDevice *device, GError **error) +{ + FuLogitechBulkcontrollerDevice *self = FU_LOGITECH_BULKCONTROLLER_DEVICE(device); + GUsbDevice *usb_device = fu_usb_device_get_dev(FU_USB_DEVICE(device)); + + /* FuUsbDevice->open */ + if (!FU_DEVICE_CLASS(fu_logitech_bulkcontroller_device_parent_class)->open(device, error)) + return FALSE; + + /* claim both interfaces */ + if (!g_usb_device_claim_interface(usb_device, + self->update_iface, + G_USB_DEVICE_CLAIM_INTERFACE_BIND_KERNEL_DRIVER, + error)) { + g_prefix_error(error, "failed to claim update interface: "); + return FALSE; + } + if (!g_usb_device_claim_interface(usb_device, + self->sync_iface, + G_USB_DEVICE_CLAIM_INTERFACE_BIND_KERNEL_DRIVER, + error)) { + g_prefix_error(error, "failed to claim sync interface: "); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_logitech_bulkcontroller_device_close(FuDevice *device, GError **error) +{ + FuLogitechBulkcontrollerDevice *self = FU_LOGITECH_BULKCONTROLLER_DEVICE(device); + GUsbDevice *usb_device = fu_usb_device_get_dev(FU_USB_DEVICE(device)); + + if (!g_usb_device_release_interface(usb_device, + self->update_iface, + G_USB_DEVICE_CLAIM_INTERFACE_BIND_KERNEL_DRIVER, + error)) { + g_prefix_error(error, "failed to release update interface: "); + return FALSE; + } + if (!g_usb_device_release_interface(usb_device, + self->sync_iface, + G_USB_DEVICE_CLAIM_INTERFACE_BIND_KERNEL_DRIVER, + error)) { + g_prefix_error(error, "failed to release sync interface: "); + return FALSE; + } + + /* FuUsbDevice->close */ + return FU_DEVICE_CLASS(fu_logitech_bulkcontroller_device_parent_class) + ->close(device, error); +} + +static gboolean +fu_logitech_bulkcontroller_device_json_parser(FuDevice *device, + GByteArray *decoded_pkt, + GError **error) +{ + FuLogitechBulkcontrollerDevice *self = FU_LOGITECH_BULKCONTROLLER_DEVICE(device); + JsonArray *json_devices; + JsonNode *json_root; + JsonObject *json_device; + JsonObject *json_object; + JsonObject *json_payload; + g_autoptr(JsonParser) json_parser = json_parser_new(); + + /* parse JSON reply */ + if (!json_parser_load_from_data(json_parser, + (const gchar *)decoded_pkt->data, + decoded_pkt->len, + error)) { + g_prefix_error(error, "error in parsing json data: "); + return FALSE; + } + json_root = json_parser_get_root(json_parser); + if (json_root == NULL) { + g_set_error_literal(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "did not get JSON root"); + return FALSE; + } + json_object = json_node_get_object(json_root); + json_payload = json_object_get_object_member(json_object, "payload"); + if (json_payload == NULL) { + g_set_error_literal(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "did not get JSON payload"); + return FALSE; + } + json_devices = json_object_get_array_member(json_payload, "devices"); + if (json_devices == NULL) { + g_set_error_literal(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "did not get JSON devices"); + return FALSE; + } + json_device = json_array_get_object_element(json_devices, 0); + if (json_device == NULL) { + g_set_error_literal(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "did not get JSON device"); + return FALSE; + } + if (json_object_has_member(json_device, "name")) + fu_device_set_name(device, json_object_get_string_member(json_device, "name")); + if (json_object_has_member(json_device, "sw")) + fu_device_set_version(device, json_object_get_string_member(json_device, "sw")); + if (json_object_has_member(json_device, "type")) + fu_device_add_instance_id(device, + json_object_get_string_member(json_device, "type")); + if (json_object_has_member(json_device, "status")) + self->status = json_object_get_int_member(json_device, "status"); + if (json_object_has_member(json_device, "updateStatus")) + self->update_status = json_object_get_int_member(json_device, "updateStatus"); + + return TRUE; +} + +static gboolean +fu_logitech_bulkcontroller_device_setup(FuDevice *device, GError **error) +{ + FuLogitechBulkcontrollerDevice *self = FU_LOGITECH_BULKCONTROLLER_DEVICE(device); + g_autoptr(GByteArray) device_info_request = g_byte_array_new(); + g_autoptr(GByteArray) decoded_pkt = g_byte_array_new(); + g_autoptr(GByteArray) device_info_response = g_byte_array_new(); + FuLogitechBulkcontrollerProtoId proto_id = kProtoId_UnknownId; + + /* FuUsbDevice->setup */ + if (!FU_DEVICE_CLASS(fu_logitech_bulkcontroller_device_parent_class)->setup(device, error)) + return FALSE; + + /* sending GetDeviceInfoRequest */ + device_info_request = proto_manager_generate_get_device_info_request(); + if (!fu_logitech_bulkcontroller_device_send_sync_cmd(self, + CMD_BUFFER_WRITE, + device_info_request, + error)) { + g_prefix_error(error, "error in sending buffer write packet: "); + return FALSE; + } + + /* wait for the GetDeviceInfoResponse */ + if (!fu_logitech_bulkcontroller_device_recv_sync_cmd(self, + CMD_BUFFER_READ, + device_info_response, + error)) { + g_prefix_error(error, "error in buffer read packet: "); + return FALSE; + } + + if (!fu_logitech_bulkcontroller_device_recv_sync_cmd(self, + CMD_UNINIT_BUFFER, + NULL, + error)) { + g_prefix_error(error, "error in buffer read packet: "); + return FALSE; + } + if (!fu_logitech_bulkcontroller_device_send_sync_cmd(self, + CMD_UNINIT_BUFFER, + NULL, + error)) { + g_prefix_error(error, "error in sending buffer uninitialize packet: "); + return FALSE; + } + decoded_pkt = proto_manager_decode_message(device_info_response->data, + device_info_response->len, + &proto_id, + error); + if (decoded_pkt == NULL) { + g_prefix_error(error, "error in unpacking packet: "); + return FALSE; + } + if (proto_id != kProtoId_GetDeviceInfoResponse) { + g_set_error_literal(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "did not get kProtoId_GetDeviceInfoResponse"); + return FALSE; + } + if (!fu_logitech_bulkcontroller_device_json_parser(device, decoded_pkt, error)) + return FALSE; + + /* success */ + return TRUE; +} + +static void +fu_logitech_bulkcontroller_device_init(FuLogitechBulkcontrollerDevice *self) +{ + fu_device_add_protocol(FU_DEVICE(self), "com.logitech.vc.proto"); + fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_TRIPLET); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UPDATABLE); +} + +static void +fu_logitech_bulkcontroller_device_class_init(FuLogitechBulkcontrollerDeviceClass *klass) +{ + FuDeviceClass *klass_device = FU_DEVICE_CLASS(klass); + klass_device->to_string = fu_logitech_bulkcontroller_device_to_string; + klass_device->write_firmware = fu_logitech_bulkcontroller_device_write_firmware; + klass_device->probe = fu_logitech_bulkcontroller_device_probe; + klass_device->setup = fu_logitech_bulkcontroller_device_setup; + klass_device->open = fu_logitech_bulkcontroller_device_open; + klass_device->close = fu_logitech_bulkcontroller_device_close; +} diff --git a/plugins/logitech-bulkcontroller/fu-logitech-bulkcontroller-device.h b/plugins/logitech-bulkcontroller/fu-logitech-bulkcontroller-device.h new file mode 100644 index 000000000..2bbce5e34 --- /dev/null +++ b/plugins/logitech-bulkcontroller/fu-logitech-bulkcontroller-device.h @@ -0,0 +1,16 @@ +/* + * Copyright (C) 2021 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#define FU_TYPE_LOGITECH_BULKCONTROLLER_DEVICE (fu_logitech_bulkcontroller_device_get_type()) +G_DECLARE_FINAL_TYPE(FuLogitechBulkcontrollerDevice, + fu_logitech_bulkcontroller_device, + FU, + LOGITECH_BULKCONTROLLER_DEVICE, + FuUsbDevice) diff --git a/plugins/logitech-bulkcontroller/fu-plugin-logitech-bulkcontroller.c b/plugins/logitech-bulkcontroller/fu-plugin-logitech-bulkcontroller.c new file mode 100644 index 000000000..01526d135 --- /dev/null +++ b/plugins/logitech-bulkcontroller/fu-plugin-logitech-bulkcontroller.c @@ -0,0 +1,18 @@ +/* + * Copyright (c) 1999-2021 Logitech, Inc. + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fu-logitech-bulkcontroller-device.h" + +void +fu_plugin_init(FuPlugin *plugin) +{ + fu_plugin_set_build_hash(plugin, FU_BUILD_HASH); + fu_plugin_add_device_gtype(plugin, FU_TYPE_LOGITECH_BULKCONTROLLER_DEVICE); +} diff --git a/plugins/logitech-bulkcontroller/logitech-bulkcontroller.quirk b/plugins/logitech-bulkcontroller/logitech-bulkcontroller.quirk new file mode 100644 index 000000000..4eae110ce --- /dev/null +++ b/plugins/logitech-bulkcontroller/logitech-bulkcontroller.quirk @@ -0,0 +1,10 @@ +# TODO: revisit InstallDuration + +[USB\VID_046D&PID_089B] +Plugin = logitech_bulkcontroller +InstallDuration = 1500 + +[USB\VID_046D&PID_08D3] +Plugin = logitech_bulkcontroller +InstallDuration = 1500 +Flags = is-mini diff --git a/plugins/logitech-bulkcontroller/meson.build b/plugins/logitech-bulkcontroller/meson.build new file mode 100644 index 000000000..a7e5c6bc1 --- /dev/null +++ b/plugins/logitech-bulkcontroller/meson.build @@ -0,0 +1,37 @@ +if get_option('plugin_logitech_bulkcontroller') + +if not get_option('gusb') + error('gusb is required for plugin_logitech_bulkcontroller') +endif + +cargs = ['-DG_LOG_DOMAIN="FuPluginLogitechBulkController"'] + +install_data(['logitech-bulkcontroller.quirk'], + install_dir: join_paths(get_option('datadir'), 'fwupd', 'quirks.d') +) +subdir('proto') +shared_module('fu_plugin_logitech_bulkcontroller', + fu_hash, + sources : [ + generated, + 'fu-logitech-bulkcontroller-common.c', + 'fu-logitech-bulkcontroller-device.c', + 'fu-plugin-logitech-bulkcontroller.c', + ], + include_directories : [ + root_incdir, + fwupd_incdir, + fwupdplugin_incdir, + ], + install : true, + install_dir: plugin_dir, + link_with : [ + fwupd, + fwupdplugin, + ], + c_args : cargs, + dependencies : [ + plugin_deps, + ], +) +endif diff --git a/plugins/logitech-bulkcontroller/proto/antiflicker.proto b/plugins/logitech-bulkcontroller/proto/antiflicker.proto new file mode 100644 index 000000000..5dbc2bc29 --- /dev/null +++ b/plugins/logitech-bulkcontroller/proto/antiflicker.proto @@ -0,0 +1,41 @@ +/* + * Copyright (c) 1999-2021 Logitech, Inc. + * All Rights Reserved + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +syntax = "proto3"; + +package logi.device.proto; + +option java_package = "com.logitech.vc.proto"; + +import "device_common.proto"; + +/** + * This message data structure holds information about the + * current AntiFlicker configuration. + * + */ +message AntiFlickerConfiguration +{ + enum Mode { + NTSC_60HZ = 0; + PAL_50HZ = 1; + } + + Mode mode = 1; +} + +message SetAntiFlickerConfigurationRequest +{ + AntiFlickerConfiguration.Mode mode = 1; +} + +message SetAntiFlickerConfigurationResponse +{ + bool success = 1; + + repeated Error errors = 2; +} diff --git a/plugins/logitech-bulkcontroller/proto/ble_cfg.proto b/plugins/logitech-bulkcontroller/proto/ble_cfg.proto new file mode 100644 index 000000000..f1306847d --- /dev/null +++ b/plugins/logitech-bulkcontroller/proto/ble_cfg.proto @@ -0,0 +1,28 @@ +/* + * Copyright (c) 1999-2021 Logitech, Inc. + * All Rights Reserved + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +syntax = "proto3"; + +package logi.device.proto; + +option java_package = "com.logitech.vc.proto"; + +import "device_common.proto"; + +message SetBLECfgRequest +{ + /** + * (REQUIRED) If true, BLE is enabled and active otherwise disabled + */ + bool BLE_ON = 1; +} + +message SetBLECfgResponse +{ + bool success = 1; + repeated Error errors = 2; +} diff --git a/plugins/logitech-bulkcontroller/proto/crash_info.proto b/plugins/logitech-bulkcontroller/proto/crash_info.proto new file mode 100644 index 000000000..159846853 --- /dev/null +++ b/plugins/logitech-bulkcontroller/proto/crash_info.proto @@ -0,0 +1,285 @@ +/* + * Copyright (c) 1999-2021 Logitech, Inc. + * All Rights Reserved + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +syntax = "proto3"; + +package logi.device.proto; + +option java_package = "com.logitech.vc.proto"; + +/** + * Kong as an Android device can accumulate + * crash debug information during its operation. + * When Kong is running in device mode, those + * crash dump files need to be copied over to + * PC and uploaded to S3. + * Note, if Kong is running in host mode, uploaded + * files, and then moved to device mode, will it + * copy the same files over? + * + * This message requests that crash dump files be + * copied over to PC + * + * EXPECTED RESPONSE + * SendCrashDumpResponse + * + */ +message SendCrashDumpRequest +{ + /** + * Unused. Reserved for future use. + */ + bool reserved = 1; +} + +/** + * Crash dump information. Most of these + * are supplied by the crash analytics service, so lets + * pass this information along. + */ +message CrashDumpInfo +{ + /** + * the filename + */ + string file_name = 1; + + /** + * the serial number + */ + string device_id = 2; + + /** + * the software version + */ + string software_version = 3; + + /** + * the file size + */ + uint64 file_size = 4; + + /** + * timestamp + */ + uint64 timestamp = 5; + + /** + * md5 for file + */ + string md5 = 6; + + /** + * the device type . Kong|Diddy + */ + string device_type = 7; + + /** + * the device mode. Hosted|Appliance + */ + string device_mode = 8; + + /** + * the report type. BugReport|EventLog,Diagnostics + */ + string report_type = 9; + + /** + * the content type. application/zip | text/plain | application/json + */ + string content_type = 10; +} + +/** + * Response which contains the crash dump file name + * information and bool value to indicate will send + * file + */ +message SendCrashDumpResponse +{ + /** + * (OPTIONAL) + * If crash dump exists, this variable + * contains the file name of crash dump + * that will be copied over. + */ + string crash_dump_file = 1; + + /** + * (REQUIRED) + * bool value to indicate will send file + * true if sending file over. + * false if no file to send. + * If true, caller will look at CrashDumpInfo + */ + bool will_send_file = 2; + + /** + * (OPTIONAL) + * Crash dump info + */ + CrashDumpInfo crash_dump_info = 3; +} + +message SendCrashDumpRequestv2 +{ + /** + * The attestation challenge. + * (REQUIRED) + */ + string challenge = 1; + + /** + * Time to live + * (REQUIRED) + */ + int32 ttl = 2; +} + +/** + * Response which contains the crash dump file name + * information, bool value to indicate will send + * file, body of the request and signature + */ +message SendCrashDumpResponsev2 +{ + /** + * (OPTIONAL) + * If crash dump exists, this variable + * contains the file name of crash dump + * that will be copied over. + */ + string crash_dump_file = 1; + + /** + * (REQUIRED) + * bool value to indicate will send file + * true if sending file over. + * false if no file to send. + * If true, caller will look at CrashDumpInfo + */ + bool will_send_file = 2; + + /** + * (OPTIONAL) + * The get upload url body. This is a json string + */ + string body = 3; + + /** + * (OPTIONAL) + * The get upload url body signature. + */ + string signature = 4; +} + +/** + * This is event sent from PC or Kong to indicate + * Success + */ +message SendCrashDumpEvent +{ + /** + * (REQUIRED) + * Contains the file name of crash dump + * that is being sent or in process of being + * received + */ + string crash_dump_file = 1; + + /** + * (REQUIRED) + * Transfer state. + * true indicates file was received without errors and bug report file was + * uploaded false means an error occurred + */ + bool success = 2; +} + +/** + * Place holder for Android requesting that a crash dump copy + * get initiated from PC side + */ +message CrashDumpAvailableEvent +{ + /** + * Unused. Reserved for future use. + */ + bool reserved = 1; +} + +/** + * Ask device to generate a bug report. This could be + * for gathering logcat, system logs, etc. + * Similar to SendCrashDumpRequestv2, but bug report generation is on + * demand. + * EXPECTED RESPONSE: + * GenerateCrashDumpResponse + * It should follow the same flow as described here + * https://docs.google.com/document/d/1D5nx1nenDu9ucZbYPXlNNxFEN1tx3W7k044mvi74x28/edit#heading=h.a9wyfbpb2282 + */ +message GenerateCrashDumpRequest +{ + /** + * The attestation challenge. + * (REQUIRED) + */ + string challenge = 1; + + /** + * Time to live + * (REQUIRED) + */ + int32 ttl = 2; + + /** + * The note to include in the bug report. This could be empty. + * (OPTIONAL) + */ + string note = 3; +} + +/** + * Response which contains the + * crash dump file name information, + * bool value to indicate will send file, + * body of the request and signature. + * Similar to SendCrashDumpResponsev2, but bug report generation is on + * demand. + * It should follow the same flow as described here + * https://docs.google.com/document/d/1D5nx1nenDu9ucZbYPXlNNxFEN1tx3W7k044mvi74x28/edit#heading=h.a9wyfbpb2282 + */ +message GenerateCrashDumpResponse +{ + /** + * (OPTIONAL) + * If crash dump exists, this variable + * contains the file name of crash dump + * that will be copied over. + */ + string crash_dump_file = 1; + + /** + * (REQUIRED) + * bool value to indicate will send file + * true if sending file over. + * false if no file to send. + */ + bool will_send_file = 2; + + /** + * (OPTIONAL) + * The get upload url body. This is a json string + */ + string body = 3; + + /** + * (OPTIONAL) + * The get upload url body signature. + */ + string signature = 4; +} diff --git a/plugins/logitech-bulkcontroller/proto/device_attestation.proto b/plugins/logitech-bulkcontroller/proto/device_attestation.proto new file mode 100644 index 000000000..ac3554d80 --- /dev/null +++ b/plugins/logitech-bulkcontroller/proto/device_attestation.proto @@ -0,0 +1,43 @@ +/* + * Copyright (c) 1999-2021 Logitech, Inc. + * All Rights Reserved + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +syntax = "proto3"; + +package logi.device.proto; + +option java_package = "com.logitech.vc.proto"; + +/** + * Request for certificate chain + * This is to be included in UsbMsg + + * EXPECTED RESPONSE + * GetCertificateChainResponse + */ +message GetCertificateChainRequest +{ + /** + * attestation challenge + */ + string attestation = 1; + + /** + * time to live + */ + int32 ttl = 2; +} + +/** + * Get certificate chain response + */ +message GetCertificateChainResponse +{ + /** + * array of certs + */ + repeated string certchain = 1; +} diff --git a/plugins/logitech-bulkcontroller/proto/device_common.proto b/plugins/logitech-bulkcontroller/proto/device_common.proto new file mode 100644 index 000000000..4d46dd0bb --- /dev/null +++ b/plugins/logitech-bulkcontroller/proto/device_common.proto @@ -0,0 +1,48 @@ +/* + * Copyright (c) 1999-2021 Logitech, Inc. + * All Rights Reserved + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +syntax = "proto3"; + +package logi.device.proto; + +option java_package = "com.logitech.vc.proto"; + +/** + * This error messages describe a failure that was encountered + * by the Sync service and primarily consist of an error code + * and a short, human-readable message. Therefore, if a client + * receives a message with a field reserved for Error messages, + * it is prudent that the application first check if there are + * errors before doing any further processing of the message. + */ +message Error +{ + /** + * (REQUIRED) Error code. + */ + uint32 error_code = 1; + + /** + * (OPTIONAL) Short, human-readable error message. If no + * message is available, then this will be an empty string. + */ + string error_message = 2; + + /** + * (OPTIONAL) A URI to a log file or some other document + * that contains more detailed information about the error. + * If such a file is not available, this will be an empty + * string. + */ + string error_log_uri = 3; + + /** + * (OPTIONAL) An optional JSON string with additional + * metadata that may be useful to the client. + */ + string json_metadata = 4; +} diff --git a/plugins/logitech-bulkcontroller/proto/device_info.proto b/plugins/logitech-bulkcontroller/proto/device_info.proto new file mode 100644 index 000000000..2efe51826 --- /dev/null +++ b/plugins/logitech-bulkcontroller/proto/device_info.proto @@ -0,0 +1,38 @@ +/* + * Copyright (c) 1999-2021 Logitech, Inc. + * All Rights Reserved + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +syntax = "proto3"; + +package logi.device.proto; + +option java_package = "com.logitech.vc.proto"; + +/** + * Request Device information + * This is to be included in UsbMsg + + * EXPECTED RESPONSE + * GetDeviceInfoResponse + */ +message GetDeviceInfoRequest +{ + /** + * Unused. Reserved for future use. + */ + bool reserved = 1; +} + +/** + * Get device information response + */ +message GetDeviceInfoResponse +{ + /** + * payload contains actual mqtt message + */ + string payload = 1; +} diff --git a/plugins/logitech-bulkcontroller/proto/device_mode.proto b/plugins/logitech-bulkcontroller/proto/device_mode.proto new file mode 100644 index 000000000..426e85a7b --- /dev/null +++ b/plugins/logitech-bulkcontroller/proto/device_mode.proto @@ -0,0 +1,183 @@ +/* + * Copyright (c) 1999-2021 Logitech, Inc. + * All Rights Reserved + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +syntax = "proto3"; + +package logi.device.proto; + +option java_package = "com.logitech.vc.proto"; + +/** + * Behavior change as of 1/28/2021 EE + * Kong sync-agent should not deprovision when this message is + * received. If would just start forwarding events to PC when message is + * received. + * + * (Legacy) + * Request to transition to device mode + * Kong could be provisioned in Host mode. This message + * will ask Kong to deprovisioned/remove host mode provisioning + * data. + * This is to be included in UsbMsg + + * EXPECTED RESPONSE + * TransitionToDeviceModeResponse + */ +message TransitionToDeviceModeRequest +{ + /** + * Unused. Reserved for future use. + */ + bool reserved = 1; +} + +/** + * Request to transition to device mode response + */ +message TransitionToDeviceModeResponse +{ + /** + * boolean value to indicate Kong was able to transition to + * device mode. If Kong is not provisioned, should just respond + * with true value. + * set to false if error was encountered during transition, and Kong + * wasn't able to transition (is this possible?) + */ + bool success = 1; + + /** + * the error in integer if success was false + */ + int32 error = 2; + + /** + * the error description + */ + string error_description = 3; +} + +/** + * Added 1/28/2021 EE + * Request to deprovision Kong + * This request is sent by PC sync-agent when PC + * is provisioned. + * Kong sync-agent should deprovision (if provisioned) + * + * EXPECTED RESPONSE + * SetDeprovisionResponse + */ +message SetDeprovisionRequest +{ + /** + * Unused. Reserved for future use. + */ + bool reserved = 1; +} + +/** + * Response to deprovision request + */ +message SetDeprovisionResponse +{ + /** + * boolean value to indicate Kong was able to deprovision Kong. + * If Kong is not provisioned, should just respond + * with true value. + * set to false if error was encountered during deprovisioning. + */ + bool success = 1; + + /** + * the error in integer if success was false + */ + int32 error = 2; + + /** + * the error description + */ + string error_description = 3; +} + +/** + * Added 3/22/2021 EE + * For sending a certificate as data. There are currently + * 2 known certificate that will be transferred - Root CA, and 802.1x cert. + * Upon receipt, sync-agent should verify using the supplied hash + * and write the data to the file system. + * + * EXPECTED RESPONSE + * SendCertificateDataResponse + */ +message SendCertificateDataRequest +{ + /** + * The certificate type + */ + enum CertType { + /** + * Reserved. Do not use. + */ + RESERVED = 0; + /** + * Root CA + */ + ROOT_CA = 1; + /** + * 802.1x cert + */ + NET_CONFIG = 2; + } + + /** + * (REQUIRED) + * The certificate type + */ + CertType cert_type = 1; + + /** + * (REQUIRED) + * the certificate file name + */ + string file_name = 2; + + /** + * (REQUIRED) + * the certificate data + */ + bytes cert_data = 3; + + /** + * (REQUIRED) + * the certificate md5 hash + */ + string md5 = 4; +} + +/** + * Response to SendCertificateData Request + */ +message SendCertificateDataResponse +{ + /** + * (REQUIRED) + * boolean value to indicate data was received, hash verified . + * set to false if error was encountered during transfer and verification. + */ + bool success = 1; + + /** + * (OPTIONAL) + * the error in integer if success was false + */ + int32 error = 2; + + /** + * (OPTIONAL) + * the error description if there are errors + */ + string error_description = 3; +} diff --git a/plugins/logitech-bulkcontroller/proto/device_request.proto b/plugins/logitech-bulkcontroller/proto/device_request.proto new file mode 100644 index 000000000..3969c73d3 --- /dev/null +++ b/plugins/logitech-bulkcontroller/proto/device_request.proto @@ -0,0 +1,143 @@ +/* + * Copyright (c) 1999-2021 Logitech, Inc. + * All Rights Reserved + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +syntax = "proto3"; + +package logi.device.proto; + +option java_package = "com.logitech.vc.proto"; + +/** + * Request to reboot device + * This is to be included in UsbMsg + + * EXPECTED RESPONSE + * RebootDeviceResponse + */ +message RebootDeviceRequest +{ + /** + * Unused. Reserved for future use. + */ + bool reserved = 1; + + /** + * A timestamp indicating when the reboot request + * was initiated. + * The device should include this entry as part of the event information + * it sends back to PC during a reboot request. + */ + uint64 iat = 2; +} + +/** + * Reboot device response + */ +message RebootDeviceResponse +{ + /** + * bool value to indicate reboot was requested. If there are errors + * while requesting a device to reboot, should set the value to false + */ + bool success = 1; +} + +/** + * This message requests that the speaker boost audio setting be changed. + * The device should send a device info event after this setting request are + * handled. + * + * EXPECTED RESPONSE + * SetSpeakerBoostResponse + * + */ +message SetSpeakerBoostRequest +{ + /** + * (REQUIRED) The speaker boost setting to be set + * + * If value is 0, the request is to disable. If 1, + * the request is to enable. + */ + int32 speaker_boost = 1; +} + +message SetSpeakerBoostResponse +{ + /** + * (REQUIRED) set to true if the audio setting request was successfully sent, + * false otherwise + */ + bool success = 1; +} + +/** + * This message requests that the noise reduction audio setting be changed. + * The device should send a device info event after this setting request are + * handled. + * + * EXPECTED RESPONSE + * SetNoiseReductionResponse + * + */ +message SetNoiseReductionRequest +{ + /** + * (REQUIRED) The noise reduction setting to be set + * + * If value is 0, the request is to disable. If 1, + * the request is to enable. + */ + int32 noise_reduction = 1; +} + +message SetNoiseReductionResponse +{ + /** + * (REQUIRED) set to true if the audio setting request was successfully sent, + * false otherwise + */ + bool success = 1; +} + +/** + * This message requests that the reverb mode audio setting be changed. + * The device should send a device info event after this setting request are + * handled. + * + * EXPECTED RESPONSE + * SetReverbModeResponse + * + */ +message SetReverbModeRequest +{ + /** + * Reverb mode enumeration + */ + enum ReverbMode { + DISABLED = 0; + MILD = 1; + NORMAL = 2; + AGGRESSIVE = 3; + } + + /** + * (REQUIRED) The reverb mode setting to be set + * + * see Reverb mode enumeration + */ + ReverbMode reverb_mode = 1; +} + +message SetReverbModeResponse +{ + /** + * (REQUIRED) set to true if the setting request was successfully sent, false + * otherwise + */ + bool success = 1; +} diff --git a/plugins/logitech-bulkcontroller/proto/device_time.proto b/plugins/logitech-bulkcontroller/proto/device_time.proto new file mode 100644 index 000000000..7db54028f --- /dev/null +++ b/plugins/logitech-bulkcontroller/proto/device_time.proto @@ -0,0 +1,32 @@ +/* + * Copyright (c) 1999-2021 Logitech, Inc. + * All Rights Reserved + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +syntax = "proto3"; + +package logi.device.proto; + +option java_package = "com.logitech.vc.proto"; + +/** + * Request for setting device time + * This is to be included in UsbMsg + */ +message SetDeviceTimeRequest +{ + /** + * utc timestamp. + */ + uint64 ts = 1; + /** + * the time zone. + */ + string time_zone = 2; +} + +/** + * Send an ack as the response + */ diff --git a/plugins/logitech-bulkcontroller/proto/firmware_update.proto b/plugins/logitech-bulkcontroller/proto/firmware_update.proto new file mode 100644 index 000000000..67e904249 --- /dev/null +++ b/plugins/logitech-bulkcontroller/proto/firmware_update.proto @@ -0,0 +1,38 @@ +/* + * Copyright (c) 1999-2021 Logitech, Inc. + * All Rights Reserved + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +syntax = "proto3"; + +package logi.device.proto; + +option java_package = "com.logitech.vc.proto"; + +/** + * Request to start update + * This is to be included in UsbMsg + + * EXPECTED RESPONSE + * UpdateNowResponse + */ +message UpdateNowRequest +{ + /** + * Unused. Reserved for future use. + */ + bool reserved = 1; +} + +/** + * Update now response + */ +message UpdateNowResponse +{ + /** + * bool value to indicate update was started + */ + bool started = 1; +} diff --git a/plugins/logitech-bulkcontroller/proto/meson.build b/plugins/logitech-bulkcontroller/proto/meson.build new file mode 100644 index 000000000..625737d9b --- /dev/null +++ b/plugins/logitech-bulkcontroller/proto/meson.build @@ -0,0 +1,21 @@ + +gen = generator(protoc, \ + output : ['@BASENAME@.pb-c.c', '@BASENAME@.pb-c.h'], + arguments : ['--proto_path=@CURRENT_SOURCE_DIR@', '--c_out=@BUILD_DIR@', '@INPUT@']) + +src = [ + 'antiflicker.proto', + 'ble_cfg.proto', + 'crash_info.proto', + 'device_attestation.proto', + 'device_common.proto', + 'device_info.proto', + 'device_mode.proto', + 'device_request.proto', + 'device_time.proto', + 'firmware_update.proto', + 'rightsight.proto', + 'ota_manifest.proto', + 'usb_msg.proto', + ] +generated = gen.process(src) diff --git a/plugins/logitech-bulkcontroller/proto/ota_manifest.proto b/plugins/logitech-bulkcontroller/proto/ota_manifest.proto new file mode 100644 index 000000000..3efed3a85 --- /dev/null +++ b/plugins/logitech-bulkcontroller/proto/ota_manifest.proto @@ -0,0 +1,72 @@ +/* + * Copyright (c) 1999-2021 Logitech, Inc. + * All Rights Reserved + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +syntax = "proto3"; + +package logi.device.proto; + +option java_package = "com.logitech.vc.proto"; + +/** + * Request device to create a GetManifestv2 body. See + * https://docs.google.com/document/d/1l31A1TWhtJC0xR8GwuNtiGN4vPLURRsj5ZcC1uEIwVQ/edit#heading=h.ctbthi1iyxw1 + * + * + * This is to be included in UsbMsg + * + * EXPECTED RESPONSE + * GetManifestBodyResponse + */ +message GetManifestBodyRequest +{ + /** + * The attestation challenge. + * (REQUIRED) + */ + string challenge = 1; + + /** + * The manifest version. + * (REQUIRED) + */ + string version = 2; + + /** + * The channel. Dont use if empty or null + * (OPTIONAL) + */ + string channel = 3; + + /** + * The meta info in json format. This + * field usually comes from PC. + * (OPTIONAL) + */ + string meta_info = 4; + + /** + * Time to live + * (REQUIRED) + */ + int32 ttl = 5; +} + +/** + * GetManifestv2 body response + */ +message GetManifestBodyResponse +{ + /** + * The get manifest body. This is a json string + */ + string body = 1; + + /** + * The get manifest body signature. + */ + string signature = 2; +} diff --git a/plugins/logitech-bulkcontroller/proto/rightsight.proto b/plugins/logitech-bulkcontroller/proto/rightsight.proto new file mode 100644 index 000000000..771ed4e1e --- /dev/null +++ b/plugins/logitech-bulkcontroller/proto/rightsight.proto @@ -0,0 +1,123 @@ +/* + * Copyright (c) 1999-2021 Logitech, Inc. + * All Rights Reserved + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +syntax = "proto3"; + +package logi.device.proto; + +option java_package = "com.logitech.vc.proto"; + +import "device_common.proto"; + +/** + * This message data structure holds information about the + * current RightSight configuration. + * + */ +message RightSightConfiguration +{ + /** + * Enumeration of modes that the RightSight service can be in. + */ + enum Mode { + /** + * This does not indicate a default value. + * + */ + DO_NOT_USE = 0; + + /** + * The camera will continually pan, tilt, and zoom + * to properly frame everyone during a meeting. + */ + DYNAMIC = 1; + + /** + * The camera will pan, tilt, and zoom to properly in + * the meeting only when the call starts. + */ + ON_CALL_START = 2; + } + + /** + * (REQUIRED) If true, RightSight is enabled and active. + */ + bool enabled = 1; + + /** + * (REQUIRED) The current mode that RightSight is in. + */ + Mode mode = 2; + + /** + * (REQUIRED) A timestamp indicating when the RightSight + * settings were last modified. This is the number of + * milliseconds since the epoch. + */ + uint64 last_modified = 3; +} + +/** + * RightSight is an auto-framing feature that is available in Kong. + * With RightSight enabled, your device will automatically pan, tilt, and zoom + * the camera lens in order to capture all meeting participants + * within the image frame. This feature can be set to one of two + * modes: dynamic and on call start. When in dynamic mode, the + * device will actively pan, tilt, and zoom the camera lens when + * appropriate in order to keep all participants in frame during + * the entire course of the meeting. When in on call start mode, + * the camera lens will pan, tilt, and zoom to capture everybody + * in frame only when the meeting starts. + * + * When RightSight is enabled, it is set + * to dynamic mode by default. + * + * This message requests that the RightSight configuration + * settings be changed. + * + * EXPECTED RESPONSE + * SetRightSightConfigurationResponse + * + */ +message SetRightSightConfigurationRequest +{ + /** + * (REQUIRED) If true, requests that RightSight be + * turned on. If false, indicates that + * RightSight should be turned off. + */ + bool enabled = 1; + + /** + * (REQUIRED) The mode for RightSight to be in. A value is + * required, but if none is provided, then this will + * default to DYNAMIC mode. + * + * If enabled is set to false, then this will effectively + * do nothing as RightSight is turned off. + */ + RightSightConfiguration.Mode mode = 2; +} + +/** + * Response which contains the RightSight configuration that was + * set as a result of the request. + */ +message SetRightSightConfigurationResponse +{ + /** + * (OPTIONAL) If any errors occurred while processing the + * request, then this field should be set accordingly. + */ + repeated Error errors = 1; + + /** + * (REQUIRED) The RightSight configuration that was set on + * the product. + */ + RightSightConfiguration right_sight_configuration = 2; +} diff --git a/plugins/logitech-bulkcontroller/proto/usb_msg.proto b/plugins/logitech-bulkcontroller/proto/usb_msg.proto new file mode 100644 index 000000000..95f40cfa2 --- /dev/null +++ b/plugins/logitech-bulkcontroller/proto/usb_msg.proto @@ -0,0 +1,204 @@ +/* + * Copyright (c) 1999-2021 Logitech, Inc. + * All Rights Reserved + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +syntax = "proto3"; + +package logi.device.proto; + +option java_package = "com.logitech.vc.proto"; + +import "device_info.proto"; +import "firmware_update.proto"; +import "crash_info.proto"; +import "device_mode.proto"; +import "device_attestation.proto"; +import "rightsight.proto"; +import "ota_manifest.proto"; +import "device_time.proto"; +import "ble_cfg.proto"; +import "antiflicker.proto"; +import "device_request.proto"; + +/** + * + * Header message to be included in UsbMsg. This contains + * message metadata that aids in processing of messages + */ +message Header +{ + /** + * A unique id of the message. If responding after receiving + * data, the value stored in this field should be used in the ack message + * msgId field + */ + string id = 1; + /** + * A timestamp indicating when the message was + * sent. This is the number of milliseconds that have + * elapsed since the epoch, in string format + */ + string timestamp = 2; +} + +/** + * The Ack message. + * This is to be included in UsbMsg + */ +message Acknowledge +{ + /** + * The message Id. This should be the same value + * in UsbMsg.Header.id field + */ + string msgId = 1; + + /** + * The message processing result. true indicates message was + * successfully processed, false otherwise. + */ + bool success = 2; +} + +/** + * The Kong Event message. + * Anything that is not part of + * Request/Response messaging, but is being sent to mqtt distributor + * should be considered as a KongEvent, and forwarded to device host. + * This is to be included in UsbMsg + */ +message KongEvent +{ + /** + * mqtt_event contains actual mqtt message + */ + string mqtt_event = 1; +} + +/** + * Sent by Kong sync-agent. + * If Kong sync-agent starts-up and it is in Device mode, then + * it can send this event. When PC sync-agent receives this event, + * it should send a TransitionToDeviceModeRequest. + * This is to be included in UsbMsg + */ +message HandshakeEvent +{ + /** + * Unused. Reserved for future use. + */ + bool reserved = 1; +} + +/** + * The enclosing message. + * This is the root message of all messagesszx + */ +message UsbMsg +{ + /** + * Header for the message containing additional + * message metadata. + */ + Header header = 1; + + /** + * The actual message being sent. One of these must be + * included + */ + oneof message + { + /** + * Ack message + */ + Acknowledge ack = 2; + /** + * Request message + */ + Request request = 3; + /** + * Response message + */ + Response response = 4; + /** + * Event + */ + Event event = 5; + } +} + +/** + * The Request message. + * This is to be included in UsbMsg + */ +message Request +{ + oneof payload + { + GetDeviceInfoRequest get_device_info_request = 2; + UpdateNowRequest update_now_request = 3; + SendCrashDumpRequest crash_dump_request = 4; + TransitionToDeviceModeRequest transition_to_devicemode_request = 5; + GetCertificateChainRequest get_certificate_chain_request = 6; + SetRightSightConfigurationRequest set_right_sight_configuration_request = 7; + GetManifestBodyRequest get_manifest_body_request = 8; + SendCrashDumpRequestv2 crash_dump_request_v2 = 9; + SetDeviceTimeRequest set_device_time_request = 10; + SetAntiFlickerConfigurationRequest set_anti_flicker_configuration_request = 11; + SetBLECfgRequest set_ble_cfg_request = 12; + SetDeprovisionRequest set_deprovision_request = 13; + RebootDeviceRequest reboot_device_request = 14; + SetSpeakerBoostRequest speaker_boost_request = 15; + SetNoiseReductionRequest noise_reduction_request = 16; + SetReverbModeRequest reverb_mode_request = 17; + GenerateCrashDumpRequest generate_bug_report_request = 18; + SendCertificateDataRequest send_certificate_data_request = 19; + } +} + +/** + * The Response message. + * This is to be included in UsbMsg + */ +message Response +{ + oneof payload + { + GetDeviceInfoResponse get_device_info_response = 2; + UpdateNowResponse update_now_response = 3; + SendCrashDumpResponse crash_dump_response = 4; + TransitionToDeviceModeResponse transition_to_devicemode_response = 5; + GetCertificateChainResponse get_certificate_chain_response = 6; + SetRightSightConfigurationResponse set_right_sight_configuration_response = 7; + GetManifestBodyResponse get_manifest_body_response = 8; + SendCrashDumpResponsev2 crash_dump_response_v2 = 9; + SetAntiFlickerConfigurationResponse set_anti_flicker_configuration_response = 11; + SetBLECfgResponse set_ble_cfg_response = 12; + SetDeprovisionResponse set_deprovision_response = 13; + RebootDeviceResponse reboot_device_response = 14; + + SetSpeakerBoostResponse speaker_boost_response = 15; + SetNoiseReductionResponse noise_reduction_response = 16; + SetReverbModeResponse reverb_mode_response = 17; + GenerateCrashDumpResponse generate_bug_report_response = 18; + SendCertificateDataResponse send_certificate_data_response = 19; + } +} + +/** + * The Event message. + * This is to be included in UsbMsg + */ +message Event +{ + oneof payload + { + KongEvent kong_event = 1; + SendCrashDumpEvent send_crash_dump_event = 2; + CrashDumpAvailableEvent crash_dump_available_event = 3; + HandshakeEvent handshake_event = 4; + } +} diff --git a/plugins/meson.build b/plugins/meson.build index f97ed2524..4df17e769 100644 --- a/plugins/meson.build +++ b/plugins/meson.build @@ -35,6 +35,7 @@ subdir('linux-swap') subdir('linux-tainted') subdir('logind') subdir('logitech-hidpp') +subdir('logitech-bulkcontroller') subdir('modem-manager') subdir('msr') subdir('nitrokey') diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml index 29ccee5f5..f27a1e0c9 100644 --- a/snap/snapcraft.yaml +++ b/snap/snapcraft.yaml @@ -211,11 +211,13 @@ parts: - libpango1.0-dev - libpci-dev - libpolkit-gobject-1-dev + - libprotobuf-c-dev - libsmbios-dev - libsqlite3-dev - libsystemd-dev - locales - pkg-config + - protobuf-c-compiler - systemd - uuid-dev stage-packages: