/* * Copyright (C) 2017 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ */ #define G_LOG_DOMAIN "FuDeviceLocker" #include "config.h" #include #ifdef HAVE_GUSB #include #endif #include "fu-device-locker.h" #include "fu-usb-device.h" /** * FuDeviceLocker: * * Easily close a shared resource (such as a device) when an object goes out of * scope. * * See also: [class@FuDevice] */ struct _FuDeviceLocker { GObject parent_instance; GObject *device; gboolean device_open; FuDeviceLockerFunc open_func; FuDeviceLockerFunc close_func; }; G_DEFINE_TYPE(FuDeviceLocker, fu_device_locker, G_TYPE_OBJECT) static void fu_device_locker_finalize(GObject *obj) { FuDeviceLocker *self = FU_DEVICE_LOCKER(obj); /* close device */ if (self->device_open) { g_autoptr(GError) error = NULL; if (!self->close_func(self->device, &error)) g_warning("failed to close device: %s", error->message); } if (self->device != NULL) g_object_unref(self->device); G_OBJECT_CLASS(fu_device_locker_parent_class)->finalize(obj); } static void fu_device_locker_class_init(FuDeviceLockerClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS(klass); object_class->finalize = fu_device_locker_finalize; } static void fu_device_locker_init(FuDeviceLocker *self) { } /** * fu_device_locker_close: * @self: a #FuDeviceLocker * @error: (nullable): optional return location for an error * * Closes the locker before it gets cleaned up. * * This function can be used to manually close a device managed by a locker, * and allows the caller to properly handle the error. * * Returns: %TRUE for success * * Since: 1.4.0 **/ gboolean fu_device_locker_close(FuDeviceLocker *self, GError **error) { g_autoptr(GError) error_local = NULL; g_return_val_if_fail(FU_IS_DEVICE_LOCKER(self), FALSE); g_return_val_if_fail(error == NULL || *error == NULL, FALSE); if (!self->device_open) return TRUE; if (!self->close_func(self->device, &error_local)) { #ifdef HAVE_GUSB if (g_error_matches(error_local, G_USB_DEVICE_ERROR, G_USB_DEVICE_ERROR_NO_DEVICE)) { g_debug("ignoring: %s", error_local->message); return TRUE; } #endif g_propagate_error(error, g_steal_pointer(&error_local)); return FALSE; } self->device_open = FALSE; return TRUE; } /** * fu_device_locker_new: * @device: a #GObject * @error: (nullable): optional return location for an error * * Opens the device for use. When the #FuDeviceLocker is deallocated the device * will be closed and any error will just be directed to the console. * This object is typically called using g_autoptr() but the device can also be * manually closed using g_clear_object(). * * The functions used for opening and closing the device are set automatically. * If the @device is not a type or supertype of #GUsbDevice or #FuDevice then * this function will not work. * * For custom objects please use fu_device_locker_new_full(). * * NOTE: If the @open_func failed then the @close_func will not be called. * * Think of this object as the device ownership. * * Returns: a device locker, or %NULL if the @open_func failed. * * Since: 1.0.0 **/ FuDeviceLocker * fu_device_locker_new(gpointer device, GError **error) { g_return_val_if_fail(device != NULL, NULL); g_return_val_if_fail(error == NULL || *error == NULL, NULL); #ifdef HAVE_GUSB /* GUsbDevice */ if (G_USB_IS_DEVICE(device)) { return fu_device_locker_new_full(device, (FuDeviceLockerFunc)g_usb_device_open, (FuDeviceLockerFunc)g_usb_device_close, error); } #endif /* FuDevice */ if (FU_IS_DEVICE(device)) { return fu_device_locker_new_full(device, (FuDeviceLockerFunc)fu_device_open, (FuDeviceLockerFunc)fu_device_close, error); } g_set_error_literal(error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, "device object type not supported"); return NULL; } /** * fu_device_locker_new_full: * @device: a #GObject * @open_func: (scope async): a function to open the device * @close_func: (scope async): a function to close the device * @error: (nullable): optional return location for an error * * Opens the device for use. When the #FuDeviceLocker is deallocated the device * will be closed and any error will just be directed to the console. * This object is typically called using g_autoptr() but the device can also be * manually closed using g_clear_object(). * * NOTE: If the @open_func failed then the @close_func will not be called. * * Think of this object as the device ownership. * * Returns: a device locker, or %NULL if the @open_func failed. * * Since: 1.0.0 **/ FuDeviceLocker * fu_device_locker_new_full(gpointer device, FuDeviceLockerFunc open_func, FuDeviceLockerFunc close_func, GError **error) { g_autoptr(FuDeviceLocker) self = NULL; g_return_val_if_fail(device != NULL, NULL); g_return_val_if_fail(open_func != NULL, NULL); g_return_val_if_fail(close_func != NULL, NULL); g_return_val_if_fail(error == NULL || *error == NULL, NULL); /* create object */ self = g_object_new(FU_TYPE_DEVICE_LOCKER, NULL); self->device = g_object_ref(device); self->open_func = open_func; self->close_func = close_func; /* open device */ if (!self->open_func(device, error)) { g_autoptr(GError) error_local = NULL; if (!self->close_func(device, &error_local)) { if (!g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_NOTHING_TO_DO)) { g_debug("ignoring close error on aborted open: %s", error_local->message); } } return NULL; } /* success */ self->device_open = TRUE; return g_steal_pointer(&self); }