mirror of
https://git.proxmox.com/git/fwupd
synced 2025-06-05 10:40:20 +00:00

Now incorporate is fixed to copy across the properties we need in the superclass, we don't need to do the subclass ->probe(). Note, we still need to do the subclassed ->probe() when using FU_UDEV_DEVICE_FLAG_VENDOR_FROM_PARENT or when looking at properties on the parent device. This also removes the spurious 'already set GType to FuVliUsbhubDevice, ignoring FuVliUsbhubDevice' messages when running the daemon.
589 lines
17 KiB
C
589 lines
17 KiB
C
/*
|
|
* Copyright (C) 2020 Richard Hughes <richard@hughsie.com>
|
|
* Copyright (c) 2020 Synaptics Incorporated.
|
|
* Copyright (C) 2012 Andrew Duggan
|
|
*
|
|
* SPDX-License-Identifier: LGPL-2.1+
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include <fwupdplugin.h>
|
|
|
|
#include <linux/hidraw.h>
|
|
#include <sys/ioctl.h>
|
|
|
|
#include "fu-synaptics-rmi-hid-device.h"
|
|
#include "fu-synaptics-rmi-v5-device.h"
|
|
#include "fu-synaptics-rmi-v7-device.h"
|
|
|
|
struct _FuSynapticsRmiHidDevice {
|
|
FuSynapticsRmiDevice parent_instance;
|
|
FuIOChannel *io_channel;
|
|
};
|
|
|
|
G_DEFINE_TYPE(FuSynapticsRmiHidDevice, fu_synaptics_rmi_hid_device, FU_TYPE_SYNAPTICS_RMI_DEVICE)
|
|
|
|
#define RMI_WRITE_REPORT_ID 0x9 /* output report */
|
|
#define RMI_READ_ADDR_REPORT_ID 0xa /* output report */
|
|
#define RMI_READ_DATA_REPORT_ID 0xb /* input report */
|
|
#define RMI_ATTN_REPORT_ID 0xc /* input report */
|
|
#define RMI_SET_RMI_MODE_REPORT_ID 0xf /* feature report */
|
|
|
|
#define RMI_DEVICE_DEFAULT_TIMEOUT 2000
|
|
|
|
#define HID_RMI4_REPORT_ID 0
|
|
#define HID_RMI4_READ_INPUT_COUNT 1
|
|
#define HID_RMI4_READ_INPUT_DATA 2
|
|
#define HID_RMI4_READ_OUTPUT_ADDR 2
|
|
#define HID_RMI4_READ_OUTPUT_COUNT 4
|
|
#define HID_RMI4_WRITE_OUTPUT_COUNT 1
|
|
#define HID_RMI4_WRITE_OUTPUT_ADDR 2
|
|
#define HID_RMI4_WRITE_OUTPUT_DATA 4
|
|
#define HID_RMI4_FEATURE_MODE 1
|
|
#define HID_RMI4_ATTN_INTERUPT_SOURCES 1
|
|
#define HID_RMI4_ATTN_DATA 2
|
|
|
|
/*
|
|
* This bit disables whatever sleep mode may be selected by the sleep_mode
|
|
* field and forces the device to run at full power without sleeping.
|
|
*/
|
|
#define RMI_F01_CRTL0_NOSLEEP_BIT (1 << 2)
|
|
|
|
/*
|
|
* msleep mode controls power management on the device and affects all
|
|
* functions of the device.
|
|
*/
|
|
#define RMI_F01_CTRL0_SLEEP_MODE_MASK 0x03
|
|
|
|
#define RMI_SLEEP_MODE_NORMAL 0x00
|
|
#define RMI_SLEEP_MODE_SENSOR_SLEEP 0x01
|
|
|
|
#define FU_SYNAPTICS_RMI_HID_DEVICE_IOCTL_TIMEOUT 5000 /* ms */
|
|
|
|
static GByteArray *
|
|
fu_synaptics_rmi_hid_device_read(FuSynapticsRmiDevice *rmi_device,
|
|
guint16 addr,
|
|
gsize req_sz,
|
|
GError **error)
|
|
{
|
|
FuSynapticsRmiHidDevice *self = FU_SYNAPTICS_RMI_HID_DEVICE(rmi_device);
|
|
g_autoptr(GByteArray) buf = g_byte_array_new();
|
|
g_autoptr(GByteArray) req = g_byte_array_new();
|
|
|
|
/* maximum size */
|
|
if (req_sz > 0xffff) {
|
|
g_set_error_literal(error,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_INTERNAL,
|
|
"data to read was too long");
|
|
return NULL;
|
|
}
|
|
|
|
/* report then old 1 byte read count */
|
|
fu_byte_array_append_uint8(req, RMI_READ_ADDR_REPORT_ID);
|
|
fu_byte_array_append_uint8(req, 0x0);
|
|
|
|
/* address */
|
|
fu_byte_array_append_uint16(req, addr, G_LITTLE_ENDIAN);
|
|
|
|
/* read output count */
|
|
fu_byte_array_append_uint16(req, req_sz, G_LITTLE_ENDIAN);
|
|
|
|
/* request */
|
|
for (guint j = req->len; j < 21; j++)
|
|
fu_byte_array_append_uint8(req, 0x0);
|
|
if (g_getenv("FWUPD_SYNAPTICS_RMI_VERBOSE") != NULL) {
|
|
fu_dump_full(G_LOG_DOMAIN,
|
|
"ReportWrite",
|
|
req->data,
|
|
req->len,
|
|
80,
|
|
FU_DUMP_FLAGS_NONE);
|
|
}
|
|
if (!fu_io_channel_write_byte_array(self->io_channel,
|
|
req,
|
|
RMI_DEVICE_DEFAULT_TIMEOUT,
|
|
FU_IO_CHANNEL_FLAG_SINGLE_SHOT |
|
|
FU_IO_CHANNEL_FLAG_USE_BLOCKING_IO,
|
|
error))
|
|
return NULL;
|
|
|
|
/* keep reading responses until we get enough data */
|
|
while (buf->len < req_sz) {
|
|
guint8 input_count_sz = 0;
|
|
g_autoptr(GByteArray) res = NULL;
|
|
res = fu_io_channel_read_byte_array(self->io_channel,
|
|
req_sz,
|
|
RMI_DEVICE_DEFAULT_TIMEOUT,
|
|
FU_IO_CHANNEL_FLAG_SINGLE_SHOT,
|
|
error);
|
|
if (res == NULL)
|
|
return NULL;
|
|
if (res->len == 0) {
|
|
g_set_error_literal(error,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_INTERNAL,
|
|
"response zero sized");
|
|
return NULL;
|
|
}
|
|
if (g_getenv("FWUPD_SYNAPTICS_RMI_VERBOSE") != NULL) {
|
|
fu_dump_full(G_LOG_DOMAIN,
|
|
"ReportRead",
|
|
res->data,
|
|
res->len,
|
|
80,
|
|
FU_DUMP_FLAGS_NONE);
|
|
}
|
|
|
|
/* ignore non data report events */
|
|
if (res->data[HID_RMI4_REPORT_ID] != RMI_READ_DATA_REPORT_ID) {
|
|
g_debug("ignoring report with ID 0x%02x", res->data[HID_RMI4_REPORT_ID]);
|
|
continue;
|
|
}
|
|
if (res->len < HID_RMI4_READ_INPUT_DATA) {
|
|
g_set_error(error,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_INTERNAL,
|
|
"response too small: 0x%02x",
|
|
res->len);
|
|
return NULL;
|
|
}
|
|
input_count_sz = res->data[HID_RMI4_READ_INPUT_COUNT];
|
|
if (input_count_sz == 0) {
|
|
g_set_error_literal(error,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_INTERNAL,
|
|
"input count zero");
|
|
return NULL;
|
|
}
|
|
if (input_count_sz + (guint)HID_RMI4_READ_INPUT_DATA > res->len) {
|
|
g_set_error(error,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_INTERNAL,
|
|
"underflow 0x%02x from expected 0x%02x",
|
|
res->len,
|
|
(guint)input_count_sz + HID_RMI4_READ_INPUT_DATA);
|
|
return NULL;
|
|
}
|
|
g_byte_array_append(buf, res->data + HID_RMI4_READ_INPUT_DATA, input_count_sz);
|
|
}
|
|
if (g_getenv("FWUPD_SYNAPTICS_RMI_VERBOSE") != NULL) {
|
|
fu_dump_full(G_LOG_DOMAIN,
|
|
"DeviceRead",
|
|
buf->data,
|
|
buf->len,
|
|
80,
|
|
FU_DUMP_FLAGS_NONE);
|
|
}
|
|
|
|
return g_steal_pointer(&buf);
|
|
}
|
|
|
|
static GByteArray *
|
|
fu_synaptics_rmi_hid_device_read_packet_register(FuSynapticsRmiDevice *rmi_device,
|
|
guint16 addr,
|
|
gsize req_sz,
|
|
GError **error)
|
|
{
|
|
return fu_synaptics_rmi_hid_device_read(rmi_device, addr, req_sz, error);
|
|
}
|
|
|
|
static gboolean
|
|
fu_synaptics_rmi_hid_device_write(FuSynapticsRmiDevice *rmi_device,
|
|
guint16 addr,
|
|
GByteArray *req,
|
|
FuSynapticsRmiDeviceFlags flags,
|
|
GError **error)
|
|
{
|
|
FuSynapticsRmiHidDevice *self = FU_SYNAPTICS_RMI_HID_DEVICE(rmi_device);
|
|
guint8 len = 0x0;
|
|
g_autoptr(GByteArray) buf = g_byte_array_new();
|
|
|
|
/* check size */
|
|
if (req != NULL) {
|
|
if (req->len > 0xff) {
|
|
g_set_error_literal(error,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_INTERNAL,
|
|
"data to write was too long");
|
|
return FALSE;
|
|
}
|
|
len = req->len;
|
|
}
|
|
|
|
/* report */
|
|
fu_byte_array_append_uint8(buf, RMI_WRITE_REPORT_ID);
|
|
|
|
/* length */
|
|
fu_byte_array_append_uint8(buf, len);
|
|
|
|
/* address */
|
|
fu_byte_array_append_uint16(buf, addr, G_LITTLE_ENDIAN);
|
|
|
|
/* optional data */
|
|
if (req != NULL)
|
|
g_byte_array_append(buf, req->data, req->len);
|
|
|
|
/* pad out to 21 bytes for some reason */
|
|
for (guint i = buf->len; i < 21; i++)
|
|
fu_byte_array_append_uint8(buf, 0x0);
|
|
if (g_getenv("FWUPD_SYNAPTICS_RMI_VERBOSE") != NULL) {
|
|
fu_dump_full(G_LOG_DOMAIN,
|
|
"DeviceWrite",
|
|
buf->data,
|
|
buf->len,
|
|
80,
|
|
FU_DUMP_FLAGS_NONE);
|
|
}
|
|
|
|
return fu_io_channel_write_byte_array(self->io_channel,
|
|
buf,
|
|
RMI_DEVICE_DEFAULT_TIMEOUT,
|
|
FU_IO_CHANNEL_FLAG_SINGLE_SHOT |
|
|
FU_IO_CHANNEL_FLAG_USE_BLOCKING_IO,
|
|
error);
|
|
}
|
|
|
|
static gboolean
|
|
fu_synaptics_rmi_hid_device_wait_for_attr(FuSynapticsRmiDevice *rmi_device,
|
|
guint8 source_mask,
|
|
guint timeout_ms,
|
|
GError **error)
|
|
{
|
|
FuSynapticsRmiHidDevice *self = FU_SYNAPTICS_RMI_HID_DEVICE(rmi_device);
|
|
g_autoptr(GTimer) timer = g_timer_new();
|
|
|
|
/* wait for event from hardware */
|
|
while (g_timer_elapsed(timer, NULL) * 1000.f < timeout_ms) {
|
|
g_autoptr(GByteArray) res = NULL;
|
|
g_autoptr(GError) error_local = NULL;
|
|
|
|
/* read from fd */
|
|
res = fu_io_channel_read_byte_array(self->io_channel,
|
|
HID_RMI4_ATTN_INTERUPT_SOURCES + 1,
|
|
timeout_ms,
|
|
FU_IO_CHANNEL_FLAG_NONE,
|
|
&error_local);
|
|
if (res == NULL) {
|
|
if (g_error_matches(error_local, G_IO_ERROR, G_IO_ERROR_TIMED_OUT))
|
|
break;
|
|
g_propagate_error(error, g_steal_pointer(&error_local));
|
|
return FALSE;
|
|
}
|
|
if (g_getenv("FWUPD_SYNAPTICS_RMI_VERBOSE") != NULL) {
|
|
fu_dump_full(G_LOG_DOMAIN,
|
|
"ReportRead",
|
|
res->data,
|
|
res->len,
|
|
80,
|
|
FU_DUMP_FLAGS_NONE);
|
|
}
|
|
if (res->len < HID_RMI4_ATTN_INTERUPT_SOURCES + 1) {
|
|
g_debug("attr: ignoring small read of %u", res->len);
|
|
continue;
|
|
}
|
|
if (res->data[HID_RMI4_REPORT_ID] != RMI_ATTN_REPORT_ID) {
|
|
g_debug("attr: ignoring invalid report ID 0x%x",
|
|
res->data[HID_RMI4_REPORT_ID]);
|
|
continue;
|
|
}
|
|
|
|
/* success */
|
|
if (source_mask & res->data[HID_RMI4_ATTN_INTERUPT_SOURCES])
|
|
return TRUE;
|
|
|
|
/* wrong mask */
|
|
g_debug("source mask did not match: 0x%x",
|
|
res->data[HID_RMI4_ATTN_INTERUPT_SOURCES]);
|
|
}
|
|
|
|
/* urgh */
|
|
g_set_error_literal(error,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_NOT_SUPPORTED,
|
|
"no attr report, timed out");
|
|
return FALSE;
|
|
}
|
|
|
|
typedef enum {
|
|
HID_RMI4_MODE_MOUSE = 0,
|
|
HID_RMI4_MODE_ATTN_REPORTS = 1,
|
|
HID_RMI4_MODE_NO_PACKED_ATTN_REPORTS = 2,
|
|
} FuSynapticsRmiHidMode;
|
|
|
|
static gboolean
|
|
fu_synaptics_rmi_hid_device_set_mode(FuSynapticsRmiHidDevice *self,
|
|
FuSynapticsRmiHidMode mode,
|
|
GError **error)
|
|
{
|
|
const guint8 data[] = {0x0f, mode};
|
|
if (g_getenv("FWUPD_SYNAPTICS_RMI_VERBOSE") != NULL)
|
|
fu_dump_raw(G_LOG_DOMAIN, "SetMode", data, sizeof(data));
|
|
return fu_udev_device_ioctl(FU_UDEV_DEVICE(self),
|
|
HIDIOCSFEATURE(sizeof(data)),
|
|
(guint8 *)data,
|
|
NULL,
|
|
FU_SYNAPTICS_RMI_HID_DEVICE_IOCTL_TIMEOUT,
|
|
error);
|
|
}
|
|
|
|
static gboolean
|
|
fu_synaptics_rmi_hid_device_open(FuDevice *device, GError **error)
|
|
{
|
|
FuSynapticsRmiHidDevice *self = FU_SYNAPTICS_RMI_HID_DEVICE(device);
|
|
|
|
/* FuUdevDevice->open */
|
|
if (!FU_DEVICE_CLASS(fu_synaptics_rmi_hid_device_parent_class)->open(device, error))
|
|
return FALSE;
|
|
|
|
/* set up touchpad so we can query it */
|
|
self->io_channel = fu_io_channel_unix_new(fu_udev_device_get_fd(FU_UDEV_DEVICE(device)));
|
|
if (!fu_synaptics_rmi_hid_device_set_mode(self, HID_RMI4_MODE_ATTN_REPORTS, error))
|
|
return FALSE;
|
|
|
|
/* success */
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
fu_synaptics_rmi_hid_device_close(FuDevice *device, GError **error)
|
|
{
|
|
FuSynapticsRmiHidDevice *self = FU_SYNAPTICS_RMI_HID_DEVICE(device);
|
|
g_autoptr(GError) error_local = NULL;
|
|
|
|
/* turn it back to mouse mode */
|
|
if (!fu_synaptics_rmi_hid_device_set_mode(self, HID_RMI4_MODE_MOUSE, &error_local)) {
|
|
/* if just detached for replug, swallow error */
|
|
if (!g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_PERMISSION_DENIED)) {
|
|
g_propagate_error(error, g_steal_pointer(&error_local));
|
|
return FALSE;
|
|
}
|
|
g_debug("ignoring: %s", error_local->message);
|
|
}
|
|
|
|
fu_udev_device_set_fd(FU_UDEV_DEVICE(device), -1);
|
|
g_clear_object(&self->io_channel);
|
|
|
|
/* FuUdevDevice->close */
|
|
return FU_DEVICE_CLASS(fu_synaptics_rmi_hid_device_parent_class)->close(device, error);
|
|
}
|
|
|
|
static gboolean
|
|
fu_synaptics_rmi_hid_device_rebind_driver(FuSynapticsRmiDevice *self, GError **error)
|
|
{
|
|
GUdevDevice *udev_device = fu_udev_device_get_dev(FU_UDEV_DEVICE(self));
|
|
const gchar *hid_id;
|
|
const gchar *driver;
|
|
const gchar *subsystem;
|
|
g_autofree gchar *fn_rebind = NULL;
|
|
g_autofree gchar *fn_unbind = NULL;
|
|
g_autoptr(GUdevDevice) parent_hid = NULL;
|
|
g_autoptr(GUdevDevice) parent_i2c = NULL;
|
|
|
|
/* get actual HID node */
|
|
parent_hid = g_udev_device_get_parent_with_subsystem(udev_device, "hid", NULL);
|
|
if (parent_hid == NULL) {
|
|
g_set_error(error,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_INVALID_FILE,
|
|
"no HID parent device for %s",
|
|
g_udev_device_get_sysfs_path(udev_device));
|
|
return FALSE;
|
|
}
|
|
|
|
/* find the physical ID to use for the rebind */
|
|
hid_id = g_udev_device_get_property(parent_hid, "HID_PHYS");
|
|
if (hid_id == NULL) {
|
|
g_set_error(error,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_INVALID_FILE,
|
|
"no HID_PHYS in %s",
|
|
g_udev_device_get_sysfs_path(parent_hid));
|
|
return FALSE;
|
|
}
|
|
g_debug("HID_PHYS: %s", hid_id);
|
|
|
|
/* build paths */
|
|
parent_i2c = g_udev_device_get_parent_with_subsystem(udev_device, "i2c", NULL);
|
|
if (parent_i2c == NULL) {
|
|
g_set_error(error,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_INVALID_FILE,
|
|
"no I2C parent device for %s",
|
|
g_udev_device_get_sysfs_path(udev_device));
|
|
return FALSE;
|
|
}
|
|
driver = g_udev_device_get_driver(parent_i2c);
|
|
subsystem = g_udev_device_get_subsystem(parent_i2c);
|
|
fn_rebind = g_build_filename("/sys/bus/", subsystem, "drivers", driver, "bind", NULL);
|
|
fn_unbind = g_build_filename("/sys/bus/", subsystem, "drivers", driver, "unbind", NULL);
|
|
|
|
/* unbind hidraw, then bind it again to get a replug */
|
|
fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG);
|
|
if (!fu_synaptics_rmi_device_writeln(fn_unbind, hid_id, error))
|
|
return FALSE;
|
|
if (!fu_synaptics_rmi_device_writeln(fn_rebind, hid_id, error))
|
|
return FALSE;
|
|
|
|
/* success */
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
fu_synaptics_rmi_hid_device_detach(FuDevice *device, FuProgress *progress, GError **error)
|
|
{
|
|
FuSynapticsRmiDevice *self = FU_SYNAPTICS_RMI_DEVICE(device);
|
|
FuSynapticsRmiFunction *f34;
|
|
|
|
f34 = fu_synaptics_rmi_device_get_function(self, 0x34, error);
|
|
if (f34 == NULL)
|
|
return FALSE;
|
|
if (f34->function_version == 0x0 || f34->function_version == 0x1) {
|
|
if (!fu_synaptics_rmi_v5_device_detach(device, progress, error))
|
|
return FALSE;
|
|
} else if (f34->function_version == 0x2) {
|
|
if (!fu_synaptics_rmi_v7_device_detach(device, progress, error))
|
|
return FALSE;
|
|
} else {
|
|
g_set_error(error,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_NOT_SUPPORTED,
|
|
"f34 function version 0x%02x unsupported",
|
|
f34->function_version);
|
|
return FALSE;
|
|
}
|
|
return fu_synaptics_rmi_hid_device_rebind_driver(self, error);
|
|
}
|
|
|
|
static gboolean
|
|
fu_synaptics_rmi_hid_device_attach(FuDevice *device, FuProgress *progress, GError **error)
|
|
{
|
|
FuSynapticsRmiDevice *self = FU_SYNAPTICS_RMI_DEVICE(device);
|
|
|
|
/* sanity check */
|
|
if (!fu_device_has_flag(device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER)) {
|
|
g_debug("already in runtime mode, skipping");
|
|
return TRUE;
|
|
}
|
|
|
|
/* reset device */
|
|
if (!fu_synaptics_rmi_device_reset(self, error))
|
|
return FALSE;
|
|
|
|
/* rebind to rescan PDT with new firmware running */
|
|
return fu_synaptics_rmi_hid_device_rebind_driver(self, error);
|
|
}
|
|
|
|
static gboolean
|
|
fu_synaptics_rmi_hid_device_set_page(FuSynapticsRmiDevice *self, guint8 page, GError **error)
|
|
{
|
|
g_autoptr(GByteArray) req = g_byte_array_new();
|
|
fu_byte_array_append_uint8(req, page);
|
|
if (!fu_synaptics_rmi_device_write(self,
|
|
RMI_DEVICE_PAGE_SELECT_REGISTER,
|
|
req,
|
|
FU_SYNAPTICS_RMI_DEVICE_FLAG_NONE,
|
|
error)) {
|
|
g_prefix_error(error, "failed to set RMA page 0x%x: ", page);
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
fu_synaptics_rmi_hid_device_probe(FuDevice *device, GError **error)
|
|
{
|
|
return fu_udev_device_set_physical_id(FU_UDEV_DEVICE(device), "hid", error);
|
|
}
|
|
|
|
static gboolean
|
|
fu_synaptics_rmi_hid_device_disable_sleep(FuSynapticsRmiDevice *rmi_device, GError **error)
|
|
{
|
|
FuSynapticsRmiFunction *f01;
|
|
g_autoptr(GByteArray) f01_control0 = NULL;
|
|
|
|
f01 = fu_synaptics_rmi_device_get_function(rmi_device, 0x34, error);
|
|
if (f01 == NULL)
|
|
return FALSE;
|
|
f01_control0 = fu_synaptics_rmi_device_read(rmi_device, f01->control_base, 0x1, error);
|
|
if (f01_control0 == NULL) {
|
|
g_prefix_error(error, "failed to write get f01_control0: ");
|
|
return FALSE;
|
|
}
|
|
f01_control0->data[0] |= RMI_F01_CRTL0_NOSLEEP_BIT;
|
|
f01_control0->data[0] =
|
|
(f01_control0->data[0] & ~RMI_F01_CTRL0_SLEEP_MODE_MASK) | RMI_SLEEP_MODE_NORMAL;
|
|
if (!fu_synaptics_rmi_device_write(rmi_device,
|
|
f01->control_base,
|
|
f01_control0,
|
|
FU_SYNAPTICS_RMI_DEVICE_FLAG_NONE,
|
|
error)) {
|
|
g_prefix_error(error, "failed to write f01_control0: ");
|
|
return FALSE;
|
|
}
|
|
|
|
/* success */
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
fu_synaptics_rmi_hid_device_query_status(FuSynapticsRmiDevice *rmi_device, GError **error)
|
|
{
|
|
FuSynapticsRmiFunction *f34;
|
|
f34 = fu_synaptics_rmi_device_get_function(rmi_device, 0x34, error);
|
|
if (f34 == NULL)
|
|
return FALSE;
|
|
if (f34->function_version == 0x0 || f34->function_version == 0x1) {
|
|
return fu_synaptics_rmi_v5_device_query_status(rmi_device, error);
|
|
}
|
|
if (f34->function_version == 0x2) {
|
|
return fu_synaptics_rmi_v7_device_query_status(rmi_device, error);
|
|
}
|
|
g_set_error(error,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_NOT_SUPPORTED,
|
|
"f34 function version 0x%02x unsupported",
|
|
f34->function_version);
|
|
return FALSE;
|
|
}
|
|
|
|
static void
|
|
fu_synaptics_rmi_hid_device_set_progress(FuDevice *self, FuProgress *progress)
|
|
{
|
|
fu_progress_set_id(progress, G_STRLOC);
|
|
fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED);
|
|
fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 2, "detach");
|
|
fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 94, "write");
|
|
fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 2, "attach");
|
|
fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 2, "reload");
|
|
}
|
|
|
|
static void
|
|
fu_synaptics_rmi_hid_device_init(FuSynapticsRmiHidDevice *self)
|
|
{
|
|
fu_device_set_name(FU_DEVICE(self), "Touchpad");
|
|
fu_device_set_remove_delay(FU_DEVICE(self), FU_DEVICE_REMOVE_DELAY_RE_ENUMERATE);
|
|
fu_synaptics_rmi_device_set_max_page(FU_SYNAPTICS_RMI_DEVICE(self), 0xff);
|
|
}
|
|
|
|
static void
|
|
fu_synaptics_rmi_hid_device_class_init(FuSynapticsRmiHidDeviceClass *klass)
|
|
{
|
|
FuDeviceClass *klass_device = FU_DEVICE_CLASS(klass);
|
|
FuSynapticsRmiDeviceClass *klass_rmi = FU_SYNAPTICS_RMI_DEVICE_CLASS(klass);
|
|
klass_device->attach = fu_synaptics_rmi_hid_device_attach;
|
|
klass_device->detach = fu_synaptics_rmi_hid_device_detach;
|
|
klass_device->probe = fu_synaptics_rmi_hid_device_probe;
|
|
klass_device->open = fu_synaptics_rmi_hid_device_open;
|
|
klass_device->close = fu_synaptics_rmi_hid_device_close;
|
|
klass_device->set_progress = fu_synaptics_rmi_hid_device_set_progress;
|
|
klass_rmi->write = fu_synaptics_rmi_hid_device_write;
|
|
klass_rmi->read = fu_synaptics_rmi_hid_device_read;
|
|
klass_rmi->wait_for_attr = fu_synaptics_rmi_hid_device_wait_for_attr;
|
|
klass_rmi->set_page = fu_synaptics_rmi_hid_device_set_page;
|
|
klass_rmi->query_status = fu_synaptics_rmi_hid_device_query_status;
|
|
klass_rmi->read_packet_register = fu_synaptics_rmi_hid_device_read_packet_register;
|
|
klass_rmi->disable_sleep = fu_synaptics_rmi_hid_device_disable_sleep;
|
|
}
|