fwupd/plugins/gpio/fu-plugin-gpio.c
Richard Hughes d3d7cfa816 Add plugin quirk keys earlier in the startup process
This allows creating the silo when starting the engine with custom
plugin keys such as WacomI2cFlashBaseAddr.

If we move the plugin initialization earlier then we don't get the
HwID matches, so we really do have to split this into a 4-stage startup,
e.g. ->load(), ->init(), ->startup() and ->coldplug().
2022-05-09 11:13:52 +01:00

178 lines
4.5 KiB
C

/*
* Copyright (C) 2022 Richard Hughes <richard@hughsie.com>
*
* SPDX-License-Identifier: LGPL-2.1+
*/
#include "config.h"
#include <fwupdplugin.h>
#include "fu-gpio-device.h"
struct FuPluginData {
GPtrArray *current_logical_ids; /* element-type: utf-8 */
};
static void
fu_plugin_gpio_load(FuContext *ctx)
{
fu_context_add_quirk_key(ctx, "GpioForUpdate");
}
static void
fu_plugin_gpio_init(FuPlugin *plugin)
{
FuPluginData *data = fu_plugin_alloc_data(plugin, sizeof(FuPluginData));
data->current_logical_ids = g_ptr_array_new_with_free_func(g_free);
fu_plugin_add_udev_subsystem(plugin, "gpio");
fu_plugin_add_device_gtype(plugin, FU_TYPE_GPIO_DEVICE);
}
static void
fu_plugin_gpio_destroy(FuPlugin *plugin)
{
FuPluginData *data = fu_plugin_get_data(plugin);
g_ptr_array_unref(data->current_logical_ids);
}
static gboolean
fu_plugin_gpio_parse_level(const gchar *str, gboolean *ret, GError **error)
{
if (g_strcmp0(str, "high") == 0) {
*ret = TRUE;
return TRUE;
}
if (g_strcmp0(str, "low") == 0) {
*ret = FALSE;
return TRUE;
}
g_set_error(error,
G_IO_ERROR,
G_IO_ERROR_INVALID_DATA,
"cannot parse level, got %s and expected high|low",
str);
return FALSE;
}
static gboolean
fu_plugin_gpio_process_quirk(FuPlugin *self, const gchar *str, GError **error)
{
FuPluginData *data = fu_plugin_get_data(self);
gboolean value = FALSE;
FuDevice *device_tmp;
g_auto(GStrv) split = g_strsplit(str, ",", -1);
g_autoptr(FuDeviceLocker) locker = NULL;
/* sanity check */
if (g_strv_length(split) != 3) {
g_set_error(error,
G_IO_ERROR,
G_IO_ERROR_INVALID_DATA,
"invalid format, CHIP_NAME,PIN_NAME,LEVEL, got '%s'",
str);
return FALSE;
}
if (!fu_plugin_gpio_parse_level(split[2], &value, error))
return FALSE;
device_tmp = fu_plugin_cache_lookup(self, split[0]);
if (device_tmp == NULL) {
g_set_error(error,
FWUPD_ERROR,
FWUPD_ERROR_NOT_FOUND,
"GPIO device %s not found",
split[0]);
return FALSE;
}
locker = fu_device_locker_new(device_tmp, error);
if (locker == NULL)
return FALSE;
if (!fu_gpio_device_assign(FU_GPIO_DEVICE(device_tmp), split[1], value, error)) {
g_prefix_error(error, "failed to assign %s: ", split[0]);
return FALSE;
}
/* success */
g_ptr_array_add(data->current_logical_ids, g_strdup(fu_device_get_logical_id(device_tmp)));
return TRUE;
}
static gboolean
fu_plugin_gpio_prepare(FuPlugin *self,
FuDevice *device,
FuProgress *progress,
FwupdInstallFlags flags,
GError **error)
{
GPtrArray *guids = fu_device_get_guids(device);
for (guint i = 0; i < guids->len; i++) {
const gchar *guid = g_ptr_array_index(guids, i);
const gchar *str;
str = fu_context_lookup_quirk_by_id(fu_plugin_get_context(self),
guid,
"GpioForUpdate");
if (str == NULL)
continue;
if (!fu_plugin_gpio_process_quirk(self, str, error))
return FALSE;
}
return TRUE;
}
static gboolean
fu_plugin_gpio_cleanup(FuPlugin *self,
FuDevice *device,
FuProgress *progress,
FwupdInstallFlags flags,
GError **error)
{
FuPluginData *data = fu_plugin_get_data(self);
g_autoptr(GPtrArray) current_logical_ids = NULL;
/* deep copy to local to clear transaction array */
current_logical_ids =
g_ptr_array_copy(data->current_logical_ids, (GCopyFunc)g_strdup, NULL);
g_ptr_array_set_size(data->current_logical_ids, 0);
/* close the fds we opened during ->prepare */
for (guint i = 0; i < current_logical_ids->len; i++) {
FuDevice *device_tmp;
const gchar *current_logical_id = g_ptr_array_index(current_logical_ids, i);
device_tmp = fu_plugin_cache_lookup(self, current_logical_id);
if (device_tmp == NULL) {
g_set_error(error,
FWUPD_ERROR,
FWUPD_ERROR_NOT_FOUND,
"GPIO device %s no longer found",
current_logical_id);
return FALSE;
}
if (!fu_gpio_device_unassign(FU_GPIO_DEVICE(device_tmp), error)) {
g_prefix_error(error, "failed to unassign %s: ", current_logical_id);
return FALSE;
}
}
/* success */
return TRUE;
}
static void
fu_plugin_gpio_device_added(FuPlugin *self, FuDevice *device)
{
fu_plugin_cache_add(self, fu_device_get_logical_id(device), device);
}
void
fu_plugin_init_vfuncs(FuPluginVfuncs *vfuncs)
{
vfuncs->build_hash = FU_BUILD_HASH;
vfuncs->load = fu_plugin_gpio_load;
vfuncs->init = fu_plugin_gpio_init;
vfuncs->destroy = fu_plugin_gpio_destroy;
vfuncs->prepare = fu_plugin_gpio_prepare;
vfuncs->cleanup = fu_plugin_gpio_cleanup;
vfuncs->device_added = fu_plugin_gpio_device_added;
}