diff --git a/configure.ac b/configure.ac index a27e75288..b61555c92 100644 --- a/configure.ac +++ b/configure.ac @@ -162,6 +162,13 @@ AC_ARG_WITH([systemdunitdir], [with_systemdunitdir=$($PKG_CONFIG --variable=systemdsystemunitdir systemd)]) AC_SUBST([systemdunitdir], [$with_systemdunitdir]) +# udev rules +AC_ARG_WITH([udevrulesdir], + AS_HELP_STRING([--with-udevrulesdir=DIR], [Directory for udev rules files]), + [], + [with_udevrulesdir=$($PKG_CONFIG --variable=udevdir udev)/rules.d]) +AC_SUBST([udevrulesdir], [$with_udevrulesdir]) + dnl --------------------------------------------------------------------------- dnl - Makefiles, etc. dnl --------------------------------------------------------------------------- diff --git a/contrib/fwupd.spec.in b/contrib/fwupd.spec.in index 17422af13..decd36708 100644 --- a/contrib/fwupd.spec.in +++ b/contrib/fwupd.spec.in @@ -94,6 +94,7 @@ find %{buildroot} -name '*.la' -exec rm -f {} ';' %dir %{_localstatedir}/cache/app-info %dir %{_localstatedir}/cache/app-info/icons %dir %{_localstatedir}/cache/app-info/xmls +/usr/lib/udev/rules.d/*.rules %files devel %{_includedir}/fwupd-1 diff --git a/data/90-fwupd-devices.rules b/data/90-fwupd-devices.rules new file mode 100644 index 000000000..64e1aa6b8 --- /dev/null +++ b/data/90-fwupd-devices.rules @@ -0,0 +1,24 @@ +######################################################################## +# Copyright (C) 2015 Richard Hughes +# +# Licensed under the GNU General Public License Version 2 +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. + +# VIA USB 3.0 VL811 Hub +SUBSYSTEM=="usb", DRIVER=="hub", ATTRS{idVendor}=="2109", ATTRS{idProduct}=="0810", ENV{FWUPD_GUID}="adbb9034-b577-42c2-a661-1ee4f49ef64c", ENV{FWUPD_VENDOR}="VIA", ENV{FWUPD_MODEL}="USB 3.0 VL811 Hub" + +# VIA USB 3.0 VL811+ Hub +SUBSYSTEM=="usb", DRIVER=="hub", ATTRS{idVendor}=="2109", ATTRS{idProduct}=="0811", ENV{FWUPD_GUID}="54f84d05-c917-4c50-8b35-44feabaaa323", ENV{FWUPD_VENDOR}="VIA", ENV{FWUPD_MODEL}="USB 3.0 VL811+ Hub" + +# VIA USB 3.0 VL812 Hub +SUBSYSTEM=="usb", DRIVER=="hub", ATTRS{idVendor}=="2109", ATTRS{idProduct}=="0812", ENV{FWUPD_GUID}="cd0314ec-b80f-4d1a-a24f-c409183a8b2d", ENV{FWUPD_VENDOR}="VIA", ENV{FWUPD_MODEL}="USB 3.0 VL812 Hub" + +# VIA USB 3.0 VL812 B2 Hub +SUBSYSTEM=="usb", DRIVER=="hub", ATTRS{idVendor}=="2109", ATTRS{idProduct}=="2812", ENV{FWUPD_GUID}="26470009-97a8-4028-867a-bbbac6ee7bf0", ENV{FWUPD_VENDOR}="VIA", ENV{FWUPD_MODEL}="USB 3.0 VL812 B2 Hub" + +ENV{FWUPD_GUID}=="*?", ENV{ID_MODEL}=="", IMPORT{builtin}="usb_id" +ENV{FWUPD_GUID}=="*?", ENV{ID_MODEL_FROM_DATABASE}=="", IMPORT{builtin}="hwdb --subsystem=usb" diff --git a/data/Makefile.am b/data/Makefile.am index 6e094ba8a..3a1c04b12 100644 --- a/data/Makefile.am +++ b/data/Makefile.am @@ -25,7 +25,11 @@ install-data-hook: mkdir -p $(DESTDIR)$(systemdunitdir)/system-update.target.wants ln -sf ../fwupd-offline-update.service $(DESTDIR)$(systemdunitdir)/system-update.target.wants/fwupd-offline-update.service +udevrules_DATA = \ + 90-fwupd-devices.rules + EXTRA_DIST = \ + $(udevrules_DATA) \ $(dbusservicemain_in_files) \ $(systemdservice_in_files) diff --git a/data/via-firmware.xml b/data/via-firmware.xml new file mode 100644 index 000000000..ec8b4d4b5 --- /dev/null +++ b/data/via-firmware.xml @@ -0,0 +1,101 @@ + + + + + + adbb9034-b577-42c2-a661-1ee4f49ef64c + VL811 Firmware + Firmware for VIA USB 3.0 hub + VIA + http://www.via.com.tw/ + + + +

This stable release fixes the following problems with USB 3.0:

+
    +
  • Do not wake during transition to S4
  • +
  • Do not drop from Apple USB 3.0 Host during S3/S4 and Device PnP
  • +
  • Do not drop during S3/S4 when connected to native Intel and AMD Hosts
  • +
+

This stable release fixes the following problems with USB 2.0:

+
    +
  • Do not drop from Apple USB 3.0 Host during S3/S4 and Device PnP
  • +
+
+
+
+
+ + + + 54f84d05-c917-4c50-8b35-44feabaaa323 + VL811+ Firmware + Firmware for VIA USB 3.0 hub + VIA + http://www.via.com.tw/ + + + +

This stable release fixes the following problems:

+
    +
  • WHAT WAS FIXED?
  • +
+
+
+
+
+ + + + cd0314ec-b80f-4d1a-a24f-c409183a8b2d + VL812 Firmware + Firmware for VIA USB 3.0 hub + VIA + http://www.via.com.tw/ + + + +

This stable release fixes the following problems:

+
    +
  • WHAT WAS FIXED?
  • +
+
+
+
+
+ + + + 26470009-97a8-4028-867a-bbbac6ee7bf0 + VL812 B2 Firmware + Firmware for VIA USB 3.0 hub + VIA + http://www.via.com.tw/ + + + +

This stable release fixes the following problems:

+
    +
  • WHAT WAS FIXED?
  • +
+
+
+
+
+
diff --git a/src/Makefile.am b/src/Makefile.am index abb6f151a..3e0250c36 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -88,6 +88,8 @@ fwupd_SOURCES = \ fu-pending.h \ fu-provider.c \ fu-provider.h \ + fu-provider-udev.c \ + fu-provider-udev.h \ fu-provider-usb.c \ fu-provider-usb.h \ fu-resources.c \ diff --git a/src/fu-main.c b/src/fu-main.c index ec9f286e0..6deeac861 100644 --- a/src/fu-main.c +++ b/src/fu-main.c @@ -40,6 +40,7 @@ #include "fu-keyring.h" #include "fu-pending.h" #include "fu-provider.h" +#include "fu-provider-udev.h" #include "fu-provider-usb.h" #include "fu-resources.h" @@ -1282,6 +1283,7 @@ main (int argc, char *argv[]) /* add providers */ priv->providers = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref); + fu_main_add_provider (priv, fu_provider_udev_new ()); fu_main_add_provider (priv, fu_provider_usb_new ()); #ifdef HAVE_COLORHUG fu_main_add_provider (priv, fu_provider_chug_new ()); diff --git a/src/fu-provider-udev.c b/src/fu-provider-udev.c new file mode 100644 index 000000000..38eccb921 --- /dev/null +++ b/src/fu-provider-udev.c @@ -0,0 +1,256 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- + * + * Copyright (C) 2015 Richard Hughes + * + * Licensed under the GNU General Public License Version 2 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "config.h" + +#include +#include +#include + +#include "fu-cleanup.h" +#include "fu-device.h" +#include "fu-provider-udev.h" + +static void fu_provider_udev_finalize (GObject *object); + +#define FU_PROVIDER_UDEV_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), FU_TYPE_PROVIDER_UDEV, FuProviderUdevPrivate)) + +/** + * FuProviderUdevPrivate: + **/ +struct _FuProviderUdevPrivate +{ + GHashTable *devices; + GUdevClient *gudev_client; +}; + +G_DEFINE_TYPE (FuProviderUdev, fu_provider_udev, FU_TYPE_PROVIDER) + +/** + * fu_provider_udev_get_name: + **/ +static const gchar * +fu_provider_udev_get_name (FuProvider *provider) +{ + return "Udev"; +} + +/** + * fu_provider_udev_get_id: + **/ +static gchar * +fu_provider_udev_get_id (GUdevDevice *device) +{ + gchar *id; + id = g_strdup_printf ("ro-%s", g_udev_device_get_sysfs_path (device)); + g_strdelimit (id, "/:.-", '_'); + return id; +} + +/** + * fu_provider_udev_client_add: + **/ +static void +fu_provider_udev_client_add (FuProviderUdev *provider_udev, GUdevDevice *device) +{ + FuDevice *dev; + const gchar *display_name; + const gchar *guid; + const gchar *product; + const gchar *vendor; + _cleanup_free_ gchar *id = NULL; + _cleanup_strv_free_ gchar **split = NULL; + + /* interesting device? */ + guid = g_udev_device_get_property (device, "FWUPD_GUID"); + if (guid == NULL) + return; + + /* get data */ + g_debug ("adding udev device: %s", g_udev_device_get_sysfs_path (device)); + if (0) { + const gchar * const *keys; + guint i; + keys = g_udev_device_get_property_keys (device); + for (i = 0; keys[i] != NULL; i++) + g_debug ("KEY: %s=%s", keys[i], + g_udev_device_get_property (device, keys[i])); + + keys = g_udev_device_get_sysfs_attr_keys (device); + for (i = 0; keys[i] != NULL; i++) + g_debug ("SYS: %s=%s", keys[i], + g_udev_device_get_sysfs_attr (device, keys[i])); + } + + /* is already in database */ + id = fu_provider_udev_get_id (device); + dev = g_hash_table_lookup (provider_udev->priv->devices, id); + if (dev != NULL) { + g_debug ("ignoring duplicate %s", id); + return; + } + + /* get the FW version from the BCD device revision */ + product = g_udev_device_get_property (device, "PRODUCT"); + if (product != NULL) { + split = g_strsplit (product, "/", -1); + if (g_strv_length (split) != 3) { + g_warning ("env{PRODUCT} is invalid: %s", product); + return; + } + } + + /* did we get enough data */ + dev = fu_device_new (); + fu_device_set_id (dev, id); + fu_device_set_guid (dev, guid); + display_name = g_udev_device_get_property (device, "FWUPD_MODEL"); + if (display_name != NULL) + fu_device_set_display_name (dev, display_name); + vendor = g_udev_device_get_property (device, "FWUPD_VENDOR"); + if (vendor != NULL) + fu_device_set_metadata (dev, FU_DEVICE_KEY_VENDOR, vendor); + fu_device_set_metadata (dev, FU_DEVICE_KEY_VERSION, split[2]); + + /* insert to hash */ + g_hash_table_insert (provider_udev->priv->devices, g_strdup (id), dev); + fu_provider_device_add (FU_PROVIDER (provider_udev), dev); +} + +/** + * fu_provider_udev_client_remove: + **/ +static void +fu_provider_udev_client_remove (FuProviderUdev *provider_udev, GUdevDevice *device) +{ + FuDevice *dev; + _cleanup_free_ gchar *id = NULL; + + /* interesting device? */ + if (g_udev_device_get_property (device, "FWUPD_GUID") == NULL) + return; + + /* already in database */ + id = fu_provider_udev_get_id (device); + dev = g_hash_table_lookup (provider_udev->priv->devices, id); + if (dev == NULL) + return; + fu_provider_device_remove (FU_PROVIDER (provider_udev), dev); +} + +/** + * fu_provider_udev_client_uevent_cb: + **/ +static void +fu_provider_udev_client_uevent_cb (GUdevClient *gudev_client, + const gchar *action, + GUdevDevice *udev_device, + FuProviderUdev *provider_udev) +{ + if (g_strcmp0 (action, "remove") == 0) { + fu_provider_udev_client_remove (provider_udev, udev_device); + return; + } + if (g_strcmp0 (action, "add") == 0) { + fu_provider_udev_client_add (provider_udev, udev_device); + return; + } +} + +/** + * fu_provider_udev_coldplug: + **/ +static gboolean +fu_provider_udev_coldplug (FuProvider *provider, GError **error) +{ + FuProviderUdev *provider_udev = FU_PROVIDER_UDEV (provider); + GList *devices; + GList *l; + GUdevDevice *udev_device; + + /* get all usb devices */ + devices = g_udev_client_query_by_subsystem (provider_udev->priv->gudev_client, "usb"); + for (l = devices; l != NULL; l = l->next) { + udev_device = l->data; + fu_provider_udev_client_add (provider_udev, udev_device); + } + g_list_foreach (devices, (GFunc) g_object_unref, NULL); + g_list_free (devices); + + return TRUE; +} + +/** + * fu_provider_udev_class_init: + **/ +static void +fu_provider_udev_class_init (FuProviderUdevClass *klass) +{ + FuProviderClass *provider_class = FU_PROVIDER_CLASS (klass); + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + provider_class->get_name = fu_provider_udev_get_name; + provider_class->coldplug = fu_provider_udev_coldplug; + object_class->finalize = fu_provider_udev_finalize; + + g_type_class_add_private (klass, sizeof (FuProviderUdevPrivate)); +} + +/** + * fu_provider_udev_init: + **/ +static void +fu_provider_udev_init (FuProviderUdev *provider_udev) +{ + const gchar *subsystems[] = { NULL }; + provider_udev->priv = FU_PROVIDER_UDEV_GET_PRIVATE (provider_udev); + provider_udev->priv->devices = g_hash_table_new_full (g_str_hash, g_str_equal, + g_free, (GDestroyNotify) g_object_unref); + provider_udev->priv->gudev_client = g_udev_client_new (subsystems); + g_signal_connect (provider_udev->priv->gudev_client, "uevent", + G_CALLBACK (fu_provider_udev_client_uevent_cb), provider_udev); +} + +/** + * fu_provider_udev_finalize: + **/ +static void +fu_provider_udev_finalize (GObject *object) +{ + FuProviderUdev *provider_udev = FU_PROVIDER_UDEV (object); + FuProviderUdevPrivate *priv = provider_udev->priv; + + g_hash_table_unref (priv->devices); + g_object_unref (priv->gudev_client); + + G_OBJECT_CLASS (fu_provider_udev_parent_class)->finalize (object); +} + +/** + * fu_provider_udev_new: + **/ +FuProvider * +fu_provider_udev_new (void) +{ + FuProviderUdev *provider; + provider = g_object_new (FU_TYPE_PROVIDER_UDEV, NULL); + return FU_PROVIDER (provider); +} diff --git a/src/fu-provider-udev.h b/src/fu-provider-udev.h new file mode 100644 index 000000000..dc9a29fa4 --- /dev/null +++ b/src/fu-provider-udev.h @@ -0,0 +1,56 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- + * + * Copyright (C) 2015 Richard Hughes + * + * Licensed under the GNU General Public License Version 2 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef __FU_PROVIDER_UDEV_H +#define __FU_PROVIDER_UDEV_H + +#include + +#include "fu-device.h" +#include "fu-provider.h" + +G_BEGIN_DECLS + +#define FU_TYPE_PROVIDER_UDEV (fu_provider_udev_get_type ()) +#define FU_PROVIDER_UDEV(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), FU_TYPE_PROVIDER_UDEV, FuProviderUdev)) +#define FU_IS_PROVIDER_UDEV(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), FU_TYPE_PROVIDER_UDEV)) + +typedef struct _FuProviderUdevPrivate FuProviderUdevPrivate; +typedef struct _FuProviderUdev FuProviderUdev; +typedef struct _FuProviderUdevClass FuProviderUdevClass; + +struct _FuProviderUdev +{ + FuProvider parent; + FuProviderUdevPrivate *priv; +}; + +struct _FuProviderUdevClass +{ + FuProviderClass parent_class; +}; + +GType fu_provider_udev_get_type (void); +FuProvider *fu_provider_udev_new (void); + +G_END_DECLS + +#endif /* __FU_PROVIDER_UDEV_H */