diff --git a/contrib/fwupd.spec.in b/contrib/fwupd.spec.in index ea18b800b..7c0341bc6 100644 --- a/contrib/fwupd.spec.in +++ b/contrib/fwupd.spec.in @@ -336,6 +336,7 @@ rm ${RPM_BUILD_ROOT}%{_sbindir}/flashrom %if 0%{?enable_dummy} %{_libdir}/fwupd-plugins-3/libfu_plugin_test.so %endif +%{_libdir}/fwupd-plugins-3/libfu_plugin_thelio_io.so %{_libdir}/fwupd-plugins-3/libfu_plugin_thunderbolt.so %{_libdir}/fwupd-plugins-3/libfu_plugin_thunderbolt_power.so %{_libdir}/fwupd-plugins-3/libfu_plugin_udev.so diff --git a/plugins/meson.build b/plugins/meson.build index a0bec45a3..151e106bb 100644 --- a/plugins/meson.build +++ b/plugins/meson.build @@ -12,6 +12,7 @@ subdir('solokey') subdir('synaptics-cxaudio') subdir('synaptics-prometheus') subdir('test') +subdir('thelio-io') subdir('udev') subdir('unifying') subdir('upower') diff --git a/plugins/thelio-io/README.md b/plugins/thelio-io/README.md new file mode 100644 index 000000000..9dbf202a1 --- /dev/null +++ b/plugins/thelio-io/README.md @@ -0,0 +1,17 @@ +Thelio IO Support +================= + +Introduction +------------ + +This plugin is used to detach the Thelio IO device to DFU mode. + +To switch to this mode `1` has to be written to the `bootloader` file +in sysfs. + +GUID Generation +--------------- + +These devices use the standard USB DeviceInstanceId values, e.g. + + * `USB\VID_1209&PID_1776&REV_0001` diff --git a/plugins/thelio-io/fu-plugin-thelio-io.c b/plugins/thelio-io/fu-plugin-thelio-io.c new file mode 100644 index 000000000..1aa7c190e --- /dev/null +++ b/plugins/thelio-io/fu-plugin-thelio-io.c @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2019 Richard Hughes + * Copyright (C) 2019 Jeremy Soller + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-plugin-vfuncs.h" + +#include "fu-thelio-io-device.h" + +void +fu_plugin_init (FuPlugin *plugin) +{ + fu_plugin_set_build_hash (plugin, FU_BUILD_HASH); + fu_plugin_add_rule (plugin, FU_PLUGIN_RULE_REQUIRES_QUIRK, FU_QUIRKS_PLUGIN); +} + +gboolean +fu_plugin_update_detach (FuPlugin *plugin, FuDevice *device, GError **error) +{ + g_autoptr(FuDeviceLocker) locker = fu_device_locker_new (device, error); + if (locker == NULL) + return FALSE; + if (!fu_device_detach (device, error)) + return FALSE; + fu_device_add_flag (device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); + return TRUE; +} + +gboolean +fu_plugin_usb_device_added (FuPlugin *plugin, FuUsbDevice *device, GError **error) +{ + g_autoptr(FuThelioIoDevice) dev = NULL; + g_autoptr(FuDeviceLocker) locker = NULL; + dev = fu_thelio_io_device_new (device); + locker = fu_device_locker_new (dev, error); + if (locker == NULL) + return FALSE; + fu_plugin_device_add (plugin, FU_DEVICE (dev)); + return TRUE; +} diff --git a/plugins/thelio-io/fu-thelio-io-device.c b/plugins/thelio-io/fu-thelio-io-device.c new file mode 100644 index 000000000..61ac9a12b --- /dev/null +++ b/plugins/thelio-io/fu-thelio-io-device.c @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2019 Richard Hughes + * Copyright (C) 2019 Jeremy Soller + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-io-channel.h" +#include "fu-thelio-io-device.h" + +struct _FuThelioIoDevice { + FuUsbDevice parent_instance; +}; + +G_DEFINE_TYPE (FuThelioIoDevice, fu_thelio_io_device, FU_TYPE_USB_DEVICE) + +static gboolean +fu_thelio_io_device_probe (FuDevice *device, GError **error) +{ + const gchar *devpath; + g_autofree gchar *fn = NULL; + g_autofree gchar *buf = NULL; + g_autoptr(GUdevDevice) udev_device = NULL; + + fu_device_add_instance_id (device, "USB\\VID_03EB&PID_2FF4"); + + /* convert GUsbDevice to GUdevDevice */ + udev_device = fu_usb_device_find_udev_device (FU_USB_DEVICE (device), error); + if (udev_device == NULL) + return FALSE; + + devpath = g_udev_device_get_sysfs_path (udev_device); + if (G_UNLIKELY (devpath == NULL)) { + g_set_error_literal (error, + FWUPD_ERROR, FWUPD_ERROR_INTERNAL, + "Could not determine sysfs path for device"); + return FALSE; + } + + fn = g_build_filename (devpath, "revision", NULL); + if (!g_file_get_contents(fn, &buf, NULL, error)) + return FALSE; + + fu_device_set_version (device, (const gchar *) buf, FWUPD_VERSION_FORMAT_TRIPLET); + + return TRUE; +} + +static gboolean +fu_thelio_io_device_detach (FuDevice *device, GError **error) +{ + const gchar *devpath; + g_autofree gchar *fn = NULL; + g_autoptr(FuIOChannel) io_channel = NULL; + g_autoptr(GUdevDevice) udev_device = NULL; + const guint8 buf[] = { '1', '\n' }; + + /* convert GUsbDevice to GUdevDevice */ + udev_device = fu_usb_device_find_udev_device (FU_USB_DEVICE (device), error); + if (udev_device == NULL) + return FALSE; + fu_device_set_status (device, FWUPD_STATUS_DEVICE_RESTART); + + devpath = g_udev_device_get_sysfs_path (udev_device); + if (G_UNLIKELY (devpath == NULL)) { + g_set_error_literal (error, + FWUPD_ERROR, FWUPD_ERROR_INTERNAL, + "Could not determine sysfs path for device"); + return FALSE; + } + + fn = g_build_filename (devpath, "bootloader", NULL); + io_channel = fu_io_channel_new_file (fn, error); + if (io_channel == NULL) + return FALSE; + return fu_io_channel_write_raw (io_channel, buf, sizeof(buf), + 500, FU_IO_CHANNEL_FLAG_SINGLE_SHOT, error); +} + +static void +fu_thelio_io_device_init (FuThelioIoDevice *self) +{ + fu_device_add_flag (FU_DEVICE (self), FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_set_remove_delay (FU_DEVICE (self), FU_DEVICE_REMOVE_DELAY_RE_ENUMERATE); +} + +static void +fu_thelio_io_device_class_init (FuThelioIoDeviceClass *klass) +{ + FuDeviceClass *klass_device = FU_DEVICE_CLASS (klass); + klass_device->probe = fu_thelio_io_device_probe; + klass_device->detach = fu_thelio_io_device_detach; +} + +FuThelioIoDevice * +fu_thelio_io_device_new (FuUsbDevice *device) +{ + FuThelioIoDevice *self = g_object_new (FU_TYPE_THELIO_IO_DEVICE, NULL); + fu_device_incorporate (FU_DEVICE (self), FU_DEVICE (device)); + return self; +} diff --git a/plugins/thelio-io/fu-thelio-io-device.h b/plugins/thelio-io/fu-thelio-io-device.h new file mode 100644 index 000000000..2e8bd58e8 --- /dev/null +++ b/plugins/thelio-io/fu-thelio-io-device.h @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2019 Richard Hughes + * Copyright (C) 2019 Jeremy Soller + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include "fu-plugin.h" + +G_BEGIN_DECLS + +#define FU_TYPE_THELIO_IO_DEVICE (fu_thelio_io_device_get_type ()) +G_DECLARE_FINAL_TYPE (FuThelioIoDevice, fu_thelio_io_device, FU, THELIO_IO_DEVICE, FuUsbDevice) + +FuThelioIoDevice *fu_thelio_io_device_new (FuUsbDevice *device); + +G_END_DECLS diff --git a/plugins/thelio-io/meson.build b/plugins/thelio-io/meson.build new file mode 100644 index 000000000..9f4f85cd0 --- /dev/null +++ b/plugins/thelio-io/meson.build @@ -0,0 +1,27 @@ +cargs = ['-DG_LOG_DOMAIN="FuPluginThelioIo"'] + +install_data(['thelio-io.quirk'], + install_dir: join_paths(datadir, 'fwupd', 'quirks.d') +) + +shared_module('fu_plugin_thelio_io', + fu_hash, + sources : [ + 'fu-plugin-thelio-io.c', + 'fu-thelio-io-device.c', + ], + include_directories : [ + include_directories('../..'), + include_directories('../../src'), + include_directories('../../libfwupd'), + ], + install : true, + install_dir: plugin_dir, + link_with : [ + libfwupdprivate, + ], + c_args : cargs, + dependencies : [ + plugin_deps, + ], +) diff --git a/plugins/thelio-io/thelio-io.quirk b/plugins/thelio-io/thelio-io.quirk new file mode 100644 index 000000000..4a0c5ae06 --- /dev/null +++ b/plugins/thelio-io/thelio-io.quirk @@ -0,0 +1,3 @@ +# System76 Thelio IO +[DeviceInstanceId=USB\VID_1209&PID_1776&REV_0001] +Plugin = thelio_io diff --git a/src/fu-usb-device.c b/src/fu-usb-device.c index fb2daabfc..dfd4bbda6 100644 --- a/src/fu-usb-device.c +++ b/src/fu-usb-device.c @@ -388,6 +388,55 @@ fu_usb_device_set_dev (FuUsbDevice *device, GUsbDevice *usb_device) g_usb_device_get_platform_id (usb_device)); } +/** + * fu_usb_device_find_udev_device: + * @device: A #FuUsbDevice + * @usb_device: A #GUsbDevice, or %NULL + * + * Gets the matching #GUdevDevice for the #GUsbDevice. + * + * Returns: a #GUdevDevice, or NULL if unset or invalid + * + * Since: 1.3.2 + **/ +GUdevDevice * +fu_usb_device_find_udev_device (FuUsbDevice *device, GError **error) +{ + FuUsbDevicePrivate *priv = GET_PRIVATE (device); + g_autoptr(GList) devices = NULL; + g_autoptr(GUdevClient) gudev_client = g_udev_client_new (NULL); + + /* find all tty devices */ + devices = g_udev_client_query_by_subsystem (gudev_client, "usb"); + for (GList *l = devices; l != NULL; l = l->next) { + GUdevDevice *dev = G_UDEV_DEVICE (l->data); + + /* check correct device */ + if (g_udev_device_get_sysfs_attr_as_int (dev, "busnum") != + g_usb_device_get_bus (priv->usb_device)) + continue; + if (g_udev_device_get_sysfs_attr_as_int (dev, "devnum") != + g_usb_device_get_address (priv->usb_device)) + continue; + + /* success */ + g_debug ("USB device %u:%u is %s", + g_usb_device_get_bus (priv->usb_device), + g_usb_device_get_address (priv->usb_device), + g_udev_device_get_sysfs_path (dev)); + return g_object_ref (dev); + } + + /* failure */ + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "could not find sysfs device for %u:%u", + g_usb_device_get_bus (priv->usb_device), + g_usb_device_get_address (priv->usb_device)); + return NULL; +} + /** * fu_usb_device_get_dev: * @device: A #FuUsbDevice diff --git a/src/fu-usb-device.h b/src/fu-usb-device.h index e92f55118..d4a9d9715 100644 --- a/src/fu-usb-device.h +++ b/src/fu-usb-device.h @@ -8,6 +8,7 @@ #include #include +#include #include "fu-plugin.h" @@ -45,5 +46,7 @@ GUsbDevice *fu_usb_device_get_dev (FuUsbDevice *device); void fu_usb_device_set_dev (FuUsbDevice *device, GUsbDevice *usb_device); gboolean fu_usb_device_is_open (FuUsbDevice *device); +GUdevDevice *fu_usb_device_find_udev_device (FuUsbDevice *device, + GError **error); G_END_DECLS