diff --git a/docs/fwupd-docs.xml b/docs/fwupd-docs.xml
index 129aa40f2..c2cdd5d9a 100644
--- a/docs/fwupd-docs.xml
+++ b/docs/fwupd-docs.xml
@@ -67,405 +67,7 @@
-
- Plugin Tutorial
-
-
- Introduction
-
- At the heart of fwupd is a plugin loader that gets run at startup,
- when devices get hotplugged and when updates are done.
- The idea is we have lots of small plugins that each do one thing, and
- are ordered by dependencies against each other at runtime.
- Using plugins we can add support for new hardware or new policies
- without making big changes all over the source tree.
-
-
- There are broadly 3 types of plugin methods:
-
-
-
-
- Mechanism: Upload binary data
- into a specific hardware device.
-
-
-
-
- Policy: Control the system when
- updates are happening, e.g. preventing the user from powering-off.
-
-
-
-
- Helpers: Providing more
- metadata about devices, for instance handling device quirks.
-
-
-
-
- In general, building things out-of-tree isn't something that we think is
- a very good idea; the API and ABI internal to fwupd is still
- changing and there's a huge benefit to getting plugins upstream where
- they can undergo review and be ported as the API adapts.
- For this reason we don't install the plugin headers onto the system,
- although you can of course just install the .so
binary file
- manually.
-
-
-
- A plugin only needs to define the vfuncs that are required, and the
- plugin name is taken automatically from the suffix of the
- .so file.
-
-
- A sample plugin
-
-/*
- * Copyright (C) 2017 Richard Hughes
- */
-
-#include <fu-plugin.h>
-#include <fu-plugin-vfuncs.h>
-
-struct FuPluginData {
- gpointer proxy;
-};
-
-void
-fu_plugin_initialize (FuPlugin *plugin)
-{
- fu_plugin_add_rule (plugin, FU_PLUGIN_RULE_RUN_BEFORE, "dfu");
- fu_plugin_alloc_data (plugin, sizeof (FuPluginData));
-}
-
-void
-fu_plugin_destroy (FuPlugin *plugin)
-{
- FuPluginData *data = fu_plugin_get_data (plugin);
- destroy_proxy (data->proxy);
-}
-
-gboolean
-fu_plugin_startup (FuPlugin *plugin, GError **error)
-{
- FuPluginData *data = fu_plugin_get_data (plugin);
- data->proxy = create_proxy ();
- if (data->proxy == NULL) {
- g_set_error (error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED,
- "failed to create proxy");
- return FALSE;
- }
- return TRUE;
-}
-
-
-
-
- We have to define when our plugin is run in reference to other plugins,
- in this case, making sure we run before the dfu
plugin.
- For most plugins it does not matter in what order they are run and
- this information is not required.
-
-
-
-
- Creating an abstract device
-
- This section shows how you would create a device which is exported
- to the daemon and thus can be queried and updated by the client software.
- The example here is all hardcoded, and a true plugin would have to
- derive the details about the FuDevice
from the hardware,
- for example reading data from sysfs
or /dev
.
-
-
- Example adding a custom device
-
-#include <fu-plugin.h>
-
-gboolean
-fu_plugin_coldplug (FuPlugin *plugin, GError **error)
-{
- g_autoptr(FuDevice) dev = NULL;
- fu_device_set_id (dev, "dummy-1:2:3");
- fu_device_add_guid (dev, "2d47f29b-83a2-4f31-a2e8-63474f4d4c2e");
- fu_device_set_version (dev, "1.2.3");
- fu_device_get_version_lowest (dev, "1.2.2");
- fu_device_get_version_bootloader (dev, "0.1.2");
- fu_device_add_icon (dev, "computer");
- fu_device_add_flag (dev, FWUPD_DEVICE_FLAG_UPDATABLE);
- fu_plugin_device_add (plugin, dev);
- return TRUE;
-}
-
-
-
- This shows a lot of the plugin architecture in action. Some notable points:
-
-
-
-
- The device ID (dummy-1:2:3
) has to be unique on the
- system between all plugins, so including the plugin name as a
- prefix is probably a good idea.
-
-
-
-
- The GUID value can be generated automatically using
- fu_device_add_guid(dev,"some-identifier")
but is quoted
- here explicitly.
- The GUID value has to match the provides
value in the
- .metainfo.xml
file for the firmware update to succeed.
-
-
-
-
- Setting a display name and an icon is a good idea in case the
- GUI software needs to display the device to the user.
- Icons can be specified using a full path, although icon theme names
- should be preferred for most devices.
-
-
-
-
- The FWUPD_DEVICE_FLAG_UPDATABLE
flag tells the client
- code that the device is in a state where it can be updated.
- If the device needs to be in a special mode (e.g. a bootloader) then
- the FWUPD_DEVICE_FLAG_NEEDS_BOOTLOADER
flag can also be
- used.
- If the update should only be allowed when there is AC power available
- to the computer (i.e. not on battery) then
- FWUPD_DEVICE_FLAG_REQUIRE_AC
should be used as well.
- There are other flags and the API documentation should be used when
- choosing what flags to use for each kind of device.
-
-
-
-
- Setting the lowest allows client software to refuse downgrading
- the device to specific versions.
- This is required in case the upgrade migrates some kind of data-store
- so as to be incompatible with previous versions.
- Similarly, setting the version of the bootloader (if known) allows
- the firmware to depend on a specific bootloader version, for instance
- allowing signed firmware to only be installable on hardware with
- a bootloader new enough to deploy it
-
-
-
-
-
-
- Mechanism Plugins
-
- Although it would be a wonderful world if we could update all hardware
- using a standard shared protocol this is not the universe we live in.
- Using a mechanism like DFU or UpdateCapsule means that fwupd will just
- work without requiring any special code, but for the real world we need
- to support vendor-specific update protocols with layers of backwards
- compatibility.
-
-
- When a plugin has created a device that is FWUPD_DEVICE_FLAG_UPDATABLE
- we can ask the daemon to update the device with a suitable
- .cab
file.
- When this is done the daemon checks the update for compatibility with
- the device, and then calls the vfuncs to update the device.
-
-
-
- Updating a device
-
-gboolean
-fu_plugin_update (FuPlugin *plugin,
- FuDevice *dev,
- GBytes *blob_fw,
- FwupdInstallFlags flags,
- GError **error)
-{
- gsize sz = 0;
- guint8 *buf = g_bytes_get_data (blob_fw, &sz);
- /* write 'buf' of size 'sz' to the hardware */
- return TRUE;
-}
-
-
-
- It's important to note that the blob_fw
is the binary
- firmware file (e.g. .dfu
) and not
- the .cab
binary data.
-
-
- If FWUPD_INSTALL_FLAG_FORCE
is used then the usual checks
- done by the flashing process can be relaxed (e.g. checking for quirks),
- but please don't brick the users hardware even if they ask you to.
-
-
-
-
- Policy Helpers
-
- For some hardware, we might want to do an action before or after
- the actual firmware is squirted into the device.
- This could be something as simple as checking the system battery
- level is over a certain threshold, or it could be as complicated as
- ensuring a vendor-specific GPIO is asserted when specific types
- of hardware are updated.
-
-
-
- Running before a device update
-
-gboolean
-fu_plugin_update_prepare (FuPlugin *plugin, FuDevice *device, GError **error)
-{
- if (fu_device_has_flag (device, FWUPD_DEVICE_FLAG_REQUIRE_AC && !on_ac_power ()) {
- g_set_error_literal (error,
- FWUPD_ERROR,
- FWUPD_ERROR_AC_POWER_REQUIRED,
- "Cannot install update "
- "when not on AC power");
- return FALSE;
- }
- return TRUE;
-}
-
-
-
- Running after a device update
-
-gboolean
-fu_plugin_update_cleanup (FuPlugin *plugin, FuDevice *device, GError **error)
-{
- return g_file_set_contents ("/var/lib/fwupd/something",
- fu_device_get_id (device), -1, error);
-}
-
-
-
-
-
- Detaching to bootloader mode
-
- Some hardware can only be updated in a special bootloader mode, which
- for most devices can be switched to automatically.
- In some cases the user to do something manually, for instance
- re-inserting the hardware with a secret button pressed.
-
-
- Before the device update is performed the fwupd daemon runs an optional
- update_detach()
vfunc which switches the device to
- bootloader mode.
- After the update (or if the update fails) an the daemon runs an
- optional update_attach()
vfunc which should switch the
- hardware back to runtime mode.
- Finally an optional update_reload()
vfunc is run to
- get the new firmware version from the hardware.
-
-
- The optional vfuncs are only run
- on the plugin currently registered to handle the device ID, although
- the registered plugin can change during the attach and detach phases.
-
-
-
- Running before a device update
-
-gboolean
-fu_plugin_update_detach (FuPlugin *plugin, FuDevice *device, GError **error)
-{
- if (hardware_in_bootloader)
- return TRUE;
- return _device_detach(device, error);
-}
-
-
-
- Running after a device update
-
-gboolean
-fu_plugin_update_attach (FuPlugin *plugin, FuDevice *device, GError **error)
-{
- if (!hardware_in_bootloader)
- return TRUE;
- return _device_attach(device, error);
-}
-
-
-
- Running after a device update on success
-
-gboolean
-fu_plugin_update_reload (FuPlugin *plugin, FuDevice *device, GError **error)
-{
- g_autofree gchar *version = _get_version(plugin, device, error);
- if (version == NULL)
- return FALSE;
- fu_device_set_version(device, version);
- return TRUE;
-}
-
-
-
-
-
- The Plugin Object Cache
-
- The fwupd daemon provides a per-plugin cache which allows objects
- to be added, removed and queried using a specified key.
- Objects added to the cache must be GObject
s to enable the
- cache objects to be properly refcounted.
-
-
-
-
- Debugging a Plugin
-
- If the fwupd daemon is started with --plugin-verbose=$plugin
- then the environment variable FWUPD_$PLUGIN_VERBOSE
is
- set process-wide.
- This allows plugins to detect when they should output detailed debugging
- information that would normally be too verbose to keep in the journal.
- For example, using --plugin-verbose=logitech_hidpp
would set
- FWUPD_LOGITECH_HID_VERBOSE=1
.
-
-
-
-
- Using existing code to develop a plugin
-
- It is not usually possible to share a plugin codebase with
- firmware update programs designed for other operating systems.
- Matching the same rationale as the Linux kernel, trying to use one
- code base between projects with a compatibility shim layer in-between
- is real headache to maintain.
-
-
- The general consensus is that trying to use a abstraction layer for
- hardware is a very bad idea as you're not able to take advantage of the
- platform specific helpers -- for instance quirk files and the custom
- GType device creation.
- The time the vendor saves by creating a shim layer and
- importing existing source code into fwupd will be overtaken 100x by
- upstream maintenance costs longer term, which isn't fair.
-
-
- In a similar way, using C++ rather than GObject C means expanding the
- test matrix to include clang in C++ mode and GNU g++ too.
- It's also doubled the runtime requirements to now include both the C
- standard library as well as the C++ standard library and increases the
- dependency surface.
-
-
- Most rewritten fwupd plugins at up to x10 smaller than the standalone
- code as they can take advantage of helpers provided by fwupd rather
- than re-implementing error handling, device quirking and data chunking.
-
-
-
-
-
+
API Index
diff --git a/docs/meson.build b/docs/meson.build
index 7169a5480..f24e07422 100644
--- a/docs/meson.build
+++ b/docs/meson.build
@@ -5,6 +5,10 @@ gnome.gtkdoc(
join_paths(meson.source_root(), 'libfwupdplugin'),
join_paths(meson.build_root(), 'libfwupd'),
join_paths(meson.build_root(), 'libfwupdplugin'),
+ join_paths(meson.current_source_dir()),
+ ],
+ content_files : [
+ 'tutorial.xml',
],
main_xml : 'fwupd-docs.xml',
install : true
diff --git a/docs/tutorial.xml b/docs/tutorial.xml
new file mode 100644
index 000000000..e1f306351
--- /dev/null
+++ b/docs/tutorial.xml
@@ -0,0 +1,402 @@
+
+
+
+ Plugin Tutorial
+
+
+ Introduction
+
+ At the heart of fwupd is a plugin loader that gets run at startup,
+ when devices get hotplugged and when updates are done.
+ The idea is we have lots of small plugins that each do one thing, and
+ are ordered by dependencies against each other at runtime.
+ Using plugins we can add support for new hardware or new policies
+ without making big changes all over the source tree.
+
+
+ There are broadly 3 types of plugin methods:
+
+
+
+
+ Mechanism: Upload binary data
+ into a specific hardware device.
+
+
+
+
+ Policy: Control the system when
+ updates are happening, e.g. preventing the user from powering-off.
+
+
+
+
+ Helpers: Providing more
+ metadata about devices, for instance handling device quirks.
+
+
+
+
+ In general, building things out-of-tree isn't something that we think is
+ a very good idea; the API and ABI internal to fwupd is still
+ changing and there's a huge benefit to getting plugins upstream where
+ they can undergo review and be ported as the API adapts.
+ For this reason we don't install the plugin headers onto the system,
+ although you can of course just install the .so
binary file
+ manually.
+
+
+
+ A plugin only needs to define the vfuncs that are required, and the
+ plugin name is taken automatically from the suffix of the
+ .so file.
+
+
+ A sample plugin
+
+/*
+* Copyright (C) 2017 Richard Hughes
+*/
+
+#include <fu-plugin.h>
+#include <fu-plugin-vfuncs.h>
+
+struct FuPluginData {
+gpointer proxy;
+};
+
+void
+fu_plugin_initialize (FuPlugin *plugin)
+{
+fu_plugin_add_rule (plugin, FU_PLUGIN_RULE_RUN_BEFORE, "dfu");
+fu_plugin_alloc_data (plugin, sizeof (FuPluginData));
+}
+
+void
+fu_plugin_destroy (FuPlugin *plugin)
+{
+FuPluginData *data = fu_plugin_get_data (plugin);
+destroy_proxy (data->proxy);
+}
+
+gboolean
+fu_plugin_startup (FuPlugin *plugin, GError **error)
+{
+FuPluginData *data = fu_plugin_get_data (plugin);
+data->proxy = create_proxy ();
+if (data->proxy == NULL) {
+ g_set_error (error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED,
+ "failed to create proxy");
+ return FALSE;
+}
+return TRUE;
+}
+
+
+
+
+ We have to define when our plugin is run in reference to other plugins,
+ in this case, making sure we run before the dfu
plugin.
+ For most plugins it does not matter in what order they are run and
+ this information is not required.
+
+
+
+
+ Creating an abstract device
+
+ This section shows how you would create a device which is exported
+ to the daemon and thus can be queried and updated by the client software.
+ The example here is all hardcoded, and a true plugin would have to
+ derive the details about the FuDevice
from the hardware,
+ for example reading data from sysfs
or /dev
.
+
+
+ Example adding a custom device
+
+#include <fu-plugin.h>
+
+gboolean
+fu_plugin_coldplug (FuPlugin *plugin, GError **error)
+{
+g_autoptr(FuDevice) dev = NULL;
+fu_device_set_id (dev, "dummy-1:2:3");
+fu_device_add_guid (dev, "2d47f29b-83a2-4f31-a2e8-63474f4d4c2e");
+fu_device_set_version (dev, "1.2.3");
+fu_device_get_version_lowest (dev, "1.2.2");
+fu_device_get_version_bootloader (dev, "0.1.2");
+fu_device_add_icon (dev, "computer");
+fu_device_add_flag (dev, FWUPD_DEVICE_FLAG_UPDATABLE);
+fu_plugin_device_add (plugin, dev);
+return TRUE;
+}
+
+
+
+ This shows a lot of the plugin architecture in action. Some notable points:
+
+
+
+
+ The device ID (dummy-1:2:3
) has to be unique on the
+ system between all plugins, so including the plugin name as a
+ prefix is probably a good idea.
+
+
+
+
+ The GUID value can be generated automatically using
+ fu_device_add_guid(dev,"some-identifier")
but is quoted
+ here explicitly.
+ The GUID value has to match the provides
value in the
+ .metainfo.xml
file for the firmware update to succeed.
+
+
+
+
+ Setting a display name and an icon is a good idea in case the
+ GUI software needs to display the device to the user.
+ Icons can be specified using a full path, although icon theme names
+ should be preferred for most devices.
+
+
+
+
+ The FWUPD_DEVICE_FLAG_UPDATABLE
flag tells the client
+ code that the device is in a state where it can be updated.
+ If the device needs to be in a special mode (e.g. a bootloader) then
+ the FWUPD_DEVICE_FLAG_NEEDS_BOOTLOADER
flag can also be
+ used.
+ If the update should only be allowed when there is AC power available
+ to the computer (i.e. not on battery) then
+ FWUPD_DEVICE_FLAG_REQUIRE_AC
should be used as well.
+ There are other flags and the API documentation should be used when
+ choosing what flags to use for each kind of device.
+
+
+
+
+ Setting the lowest allows client software to refuse downgrading
+ the device to specific versions.
+ This is required in case the upgrade migrates some kind of data-store
+ so as to be incompatible with previous versions.
+ Similarly, setting the version of the bootloader (if known) allows
+ the firmware to depend on a specific bootloader version, for instance
+ allowing signed firmware to only be installable on hardware with
+ a bootloader new enough to deploy it
+
+
+
+
+
+
+ Mechanism Plugins
+
+ Although it would be a wonderful world if we could update all hardware
+ using a standard shared protocol this is not the universe we live in.
+ Using a mechanism like DFU or UpdateCapsule means that fwupd will just
+ work without requiring any special code, but for the real world we need
+ to support vendor-specific update protocols with layers of backwards
+ compatibility.
+
+
+ When a plugin has created a device that is FWUPD_DEVICE_FLAG_UPDATABLE
+ we can ask the daemon to update the device with a suitable
+ .cab
file.
+ When this is done the daemon checks the update for compatibility with
+ the device, and then calls the vfuncs to update the device.
+
+
+
+ Updating a device
+
+gboolean
+fu_plugin_update (FuPlugin *plugin,
+ FuDevice *dev,
+ GBytes *blob_fw,
+ FwupdInstallFlags flags,
+ GError **error)
+{
+gsize sz = 0;
+guint8 *buf = g_bytes_get_data (blob_fw, &sz);
+/* write 'buf' of size 'sz' to the hardware */
+return TRUE;
+}
+
+
+
+ It's important to note that the blob_fw
is the binary
+ firmware file (e.g. .dfu
) and not
+ the .cab
binary data.
+
+
+ If FWUPD_INSTALL_FLAG_FORCE
is used then the usual checks
+ done by the flashing process can be relaxed (e.g. checking for quirks),
+ but please don't brick the users hardware even if they ask you to.
+
+
+
+
+ Policy Helpers
+
+ For some hardware, we might want to do an action before or after
+ the actual firmware is squirted into the device.
+ This could be something as simple as checking the system battery
+ level is over a certain threshold, or it could be as complicated as
+ ensuring a vendor-specific GPIO is asserted when specific types
+ of hardware are updated.
+
+
+
+ Running before a device update
+
+gboolean
+fu_plugin_update_prepare (FuPlugin *plugin, FuDevice *device, GError **error)
+{
+if (fu_device_has_flag (device, FWUPD_DEVICE_FLAG_REQUIRE_AC && !on_ac_power ()) {
+ g_set_error_literal (error,
+ FWUPD_ERROR,
+ FWUPD_ERROR_AC_POWER_REQUIRED,
+ "Cannot install update "
+ "when not on AC power");
+ return FALSE;
+}
+return TRUE;
+}
+
+
+
+ Running after a device update
+
+gboolean
+fu_plugin_update_cleanup (FuPlugin *plugin, FuDevice *device, GError **error)
+{
+return g_file_set_contents ("/var/lib/fwupd/something",
+ fu_device_get_id (device), -1, error);
+}
+
+
+
+
+
+ Detaching to bootloader mode
+
+ Some hardware can only be updated in a special bootloader mode, which
+ for most devices can be switched to automatically.
+ In some cases the user to do something manually, for instance
+ re-inserting the hardware with a secret button pressed.
+
+
+ Before the device update is performed the fwupd daemon runs an optional
+ update_detach()
vfunc which switches the device to
+ bootloader mode.
+ After the update (or if the update fails) an the daemon runs an
+ optional update_attach()
vfunc which should switch the
+ hardware back to runtime mode.
+ Finally an optional update_reload()
vfunc is run to
+ get the new firmware version from the hardware.
+
+
+ The optional vfuncs are only run
+ on the plugin currently registered to handle the device ID, although
+ the registered plugin can change during the attach and detach phases.
+
+
+
+ Running before a device update
+
+gboolean
+fu_plugin_update_detach (FuPlugin *plugin, FuDevice *device, GError **error)
+{
+if (hardware_in_bootloader)
+ return TRUE;
+return _device_detach(device, error);
+}
+
+
+
+ Running after a device update
+
+gboolean
+fu_plugin_update_attach (FuPlugin *plugin, FuDevice *device, GError **error)
+{
+if (!hardware_in_bootloader)
+ return TRUE;
+return _device_attach(device, error);
+}
+
+
+
+ Running after a device update on success
+
+gboolean
+fu_plugin_update_reload (FuPlugin *plugin, FuDevice *device, GError **error)
+{
+g_autofree gchar *version = _get_version(plugin, device, error);
+if (version == NULL)
+ return FALSE;
+fu_device_set_version(device, version);
+return TRUE;
+}
+
+
+
+
+
+ The Plugin Object Cache
+
+ The fwupd daemon provides a per-plugin cache which allows objects
+ to be added, removed and queried using a specified key.
+ Objects added to the cache must be GObject
s to enable the
+ cache objects to be properly refcounted.
+
+
+
+
+ Debugging a Plugin
+
+ If the fwupd daemon is started with --plugin-verbose=$plugin
+ then the environment variable FWUPD_$PLUGIN_VERBOSE
is
+ set process-wide.
+ This allows plugins to detect when they should output detailed debugging
+ information that would normally be too verbose to keep in the journal.
+ For example, using --plugin-verbose=logitech_hidpp
would set
+ FWUPD_LOGITECH_HID_VERBOSE=1
.
+
+
+
+
+ Using existing code to develop a plugin
+
+ It is not usually possible to share a plugin codebase with
+ firmware update programs designed for other operating systems.
+ Matching the same rationale as the Linux kernel, trying to use one
+ code base between projects with a compatibility shim layer in-between
+ is real headache to maintain.
+
+
+ The general consensus is that trying to use a abstraction layer for
+ hardware is a very bad idea as you're not able to take advantage of the
+ platform specific helpers -- for instance quirk files and the custom
+ GType device creation.
+ The time the vendor saves by creating a shim layer and
+ importing existing source code into fwupd will be overtaken 100x by
+ upstream maintenance costs longer term, which isn't fair.
+
+
+ In a similar way, using C++ rather than GObject C means expanding the
+ test matrix to include clang in C++ mode and GNU g++ too.
+ It's also doubled the runtime requirements to now include both the C
+ standard library as well as the C++ standard library and increases the
+ dependency surface.
+
+
+ Most rewritten fwupd plugins at up to x10 smaller than the standalone
+ code as they can take advantage of helpers provided by fwupd rather
+ than re-implementing error handling, device quirking and data chunking.
+
+
+
+
+