mirror of
https://git.proxmox.com/git/fwupd
synced 2025-05-17 00:46:40 +00:00

It wasn't hugely clear what the platform ID was actually meant to represent. In some cases it was being used like a physical ID, in others it was a logical ID, and in others it was both. In some cases it was even used as a sysfs path. Clear up all the confusion by splitting the platform ID into two parts, an optional *physical* ID to represent the electrical connection, and an optional *logical* ID to disambiguate composite devices with the same physical ID. Also create an explicit sysfs_path getter for FuUdevDevice to make this clear. This allows WAIT_FOR_REPLUG to always work, rather than depending on the order that the GUIDs were added, and that the kernel would always return the same sysfs path (which it doesn't have to do, especially for hidraw devices).
259 lines
6.0 KiB
C
259 lines
6.0 KiB
C
/*
|
|
* Copyright (C) 2016 Richard Hughes <richard@hughsie.com>
|
|
*
|
|
* SPDX-License-Identifier: LGPL-2.1+
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include <fwupd.h>
|
|
|
|
#include "fu-plugin-vfuncs.h"
|
|
|
|
#include "lu-context.h"
|
|
#include "lu-device.h"
|
|
#include "lu-device-peripheral.h"
|
|
|
|
struct FuPluginData {
|
|
LuContext *ctx;
|
|
};
|
|
|
|
static gboolean
|
|
fu_plugin_unifying_device_added (FuPlugin *plugin,
|
|
LuDevice *device,
|
|
GError **error)
|
|
{
|
|
g_autoptr(AsProfile) profile = as_profile_new ();
|
|
g_autoptr(AsProfileTask) ptask = NULL;
|
|
|
|
/* profile */
|
|
ptask = as_profile_start (profile, "FuPluginLu:added{%s}",
|
|
fu_device_get_physical_id (FU_DEVICE (device)));
|
|
g_assert (ptask != NULL);
|
|
|
|
/* open the device */
|
|
if (!lu_device_open (device, error))
|
|
return FALSE;
|
|
|
|
/* insert to hash */
|
|
fu_plugin_device_add (plugin, FU_DEVICE (device));
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
fu_plugin_unifying_detach_cb (gpointer user_data)
|
|
{
|
|
FuDevice *device = FU_DEVICE (user_data);
|
|
g_autoptr(GError) error = NULL;
|
|
|
|
/* ditch this device */
|
|
g_debug ("detaching");
|
|
if (!fu_device_detach (device, &error)) {
|
|
g_warning ("failed to detach: %s", error->message);
|
|
return FALSE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static gboolean
|
|
fu_plugin_unifying_attach_cb (gpointer user_data)
|
|
{
|
|
FuDevice *device = FU_DEVICE (user_data);
|
|
g_autoptr(GError) error = NULL;
|
|
|
|
/* ditch this device */
|
|
g_debug ("attaching");
|
|
if (!fu_device_attach (device, &error)) {
|
|
g_warning ("failed to detach: %s", error->message);
|
|
return FALSE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
gboolean
|
|
fu_plugin_update_detach (FuPlugin *plugin, FuDevice *dev, GError **error)
|
|
{
|
|
FuPluginData *data = fu_plugin_get_data (plugin);
|
|
LuDevice *device = LU_DEVICE (dev);
|
|
|
|
/* get device */
|
|
if (!lu_device_open (device, error))
|
|
return FALSE;
|
|
|
|
/* switch to bootloader if required */
|
|
if (!lu_device_has_flag (device, LU_DEVICE_FLAG_REQUIRES_DETACH))
|
|
return TRUE;
|
|
|
|
/* wait for device to come back */
|
|
fu_device_set_status (dev, FWUPD_STATUS_DEVICE_RESTART);
|
|
if (lu_device_has_flag (device, LU_DEVICE_FLAG_DETACH_WILL_REPLUG)) {
|
|
g_debug ("doing detach in idle");
|
|
g_idle_add_full (G_PRIORITY_DEFAULT_IDLE,
|
|
fu_plugin_unifying_detach_cb,
|
|
g_object_ref (dev),
|
|
(GDestroyNotify) g_object_unref);
|
|
if (!lu_context_wait_for_replug (data->ctx,
|
|
device,
|
|
FU_DEVICE_REMOVE_DELAY_RE_ENUMERATE,
|
|
error))
|
|
return FALSE;
|
|
} else {
|
|
g_debug ("doing detach in main thread");
|
|
if (!fu_device_detach (dev, error))
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
gboolean
|
|
fu_plugin_update_attach (FuPlugin *plugin, FuDevice *dev, GError **error)
|
|
{
|
|
FuPluginData *data = fu_plugin_get_data (plugin);
|
|
LuDevice *device = LU_DEVICE (dev);
|
|
|
|
/* get device */
|
|
if (!lu_device_open (device, error))
|
|
return FALSE;
|
|
|
|
/* wait for it to appear back in runtime mode if required */
|
|
if (!lu_device_has_flag (device, LU_DEVICE_FLAG_REQUIRES_ATTACH))
|
|
return TRUE;
|
|
|
|
/* wait for device to come back */
|
|
fu_device_set_status (dev, FWUPD_STATUS_DEVICE_RESTART);
|
|
if (lu_device_has_flag (device, LU_DEVICE_FLAG_ATTACH_WILL_REPLUG)) {
|
|
g_debug ("doing attach in idle");
|
|
g_idle_add_full (G_PRIORITY_DEFAULT_IDLE,
|
|
fu_plugin_unifying_attach_cb,
|
|
g_object_ref (device),
|
|
(GDestroyNotify) g_object_unref);
|
|
if (!lu_context_wait_for_replug (data->ctx,
|
|
device,
|
|
FU_DEVICE_REMOVE_DELAY_RE_ENUMERATE,
|
|
error))
|
|
return FALSE;
|
|
} else {
|
|
g_debug ("doing attach in main thread");
|
|
if (!fu_device_attach (dev, error))
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
gboolean
|
|
fu_plugin_update_reload (FuPlugin *plugin, FuDevice *dev, GError **error)
|
|
{
|
|
LuDevice *device = LU_DEVICE (dev);
|
|
|
|
/* get device */
|
|
if (!lu_device_open (device, error))
|
|
return FALSE;
|
|
return TRUE;
|
|
}
|
|
|
|
gboolean
|
|
fu_plugin_update (FuPlugin *plugin,
|
|
FuDevice *dev,
|
|
GBytes *blob_fw,
|
|
FwupdInstallFlags flags,
|
|
GError **error)
|
|
{
|
|
LuDevice *device = LU_DEVICE (dev);
|
|
|
|
/* get version */
|
|
if (!lu_device_open (device, error))
|
|
return FALSE;
|
|
|
|
/* write the firmware */
|
|
fu_device_set_status (dev, FWUPD_STATUS_DEVICE_WRITE);
|
|
if (!fu_device_write_firmware (dev, blob_fw, error))
|
|
return FALSE;
|
|
|
|
/* success */
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
fu_plugin_unifying_device_added_cb (LuContext *ctx,
|
|
LuDevice *device,
|
|
FuPlugin *plugin)
|
|
{
|
|
g_autoptr(GError) error = NULL;
|
|
|
|
/* add */
|
|
if (!fu_plugin_unifying_device_added (plugin, device, &error)) {
|
|
if (g_error_matches (error,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_NOT_SUPPORTED)) {
|
|
g_debug ("Failed to add Logitech device: %s",
|
|
error->message);
|
|
} else {
|
|
g_warning ("Failed to add Logitech device: %s",
|
|
error->message);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
fu_plugin_unifying_device_removed_cb (LuContext *ctx,
|
|
LuDevice *device,
|
|
FuPlugin *plugin)
|
|
{
|
|
fu_plugin_device_remove (plugin, FU_DEVICE (device));
|
|
}
|
|
|
|
gboolean
|
|
fu_plugin_startup (FuPlugin *plugin, GError **error)
|
|
{
|
|
FuPluginData *data = fu_plugin_get_data (plugin);
|
|
|
|
/* check the kernel has CONFIG_HIDRAW */
|
|
if (!g_file_test ("/sys/class/hidraw", G_FILE_TEST_IS_DIR)) {
|
|
g_set_error_literal (error,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_NOT_SUPPORTED,
|
|
"no kernel support for CONFIG_HIDRAW");
|
|
return FALSE;
|
|
}
|
|
|
|
/* coldplug */
|
|
g_signal_connect (data->ctx, "added",
|
|
G_CALLBACK (fu_plugin_unifying_device_added_cb),
|
|
plugin);
|
|
g_signal_connect (data->ctx, "removed",
|
|
G_CALLBACK (fu_plugin_unifying_device_removed_cb),
|
|
plugin);
|
|
lu_context_set_supported (data->ctx, fu_plugin_get_supported (plugin));
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
gboolean
|
|
fu_plugin_coldplug (FuPlugin *plugin, GError **error)
|
|
{
|
|
FuPluginData *data = fu_plugin_get_data (plugin);
|
|
lu_context_coldplug (data->ctx);
|
|
lu_context_set_poll_interval (data->ctx, 5000);
|
|
return TRUE;
|
|
}
|
|
|
|
void
|
|
fu_plugin_init (FuPlugin *plugin)
|
|
{
|
|
FuPluginData *data = fu_plugin_alloc_data (plugin, sizeof (FuPluginData));
|
|
GUsbContext *usb_ctx = fu_plugin_get_usb_context (plugin);
|
|
data->ctx = lu_context_new_full (usb_ctx);
|
|
g_object_set (data->ctx,
|
|
"system-quirks", fu_plugin_get_quirks (plugin),
|
|
NULL);
|
|
}
|
|
|
|
void
|
|
fu_plugin_destroy (FuPlugin *plugin)
|
|
{
|
|
FuPluginData *data = fu_plugin_get_data (plugin);
|
|
g_object_unref (data->ctx);
|
|
}
|