diff --git a/docs/dev/hardware.rst b/docs/dev/hardware.rst
index 56f56cbaaeb5b684f2b7c10cc689bac190b01647..fb56d08f1ccf4cb40e8d64ae5db75c15bf1ebf5f 100644
--- a/docs/dev/hardware.rst
+++ b/docs/dev/hardware.rst
@@ -1,5 +1,5 @@
-Adding support for new hardware
-===============================
+Adding hardware support
+=======================
 This page will give a short overview on how to add support
 for new hardware to Gluon.
 
@@ -7,155 +7,232 @@ Hardware requirements
 ---------------------
 Having an ath9k, ath10k or mt76 based WLAN adapter is highly recommended,
 although other chipsets may also work. VAP (multiple SSID) support
-is a requirement.
-
-.. _device-class-definition:
+with simultaneous AP + Mesh Point (802.11s) operation is required.
 
 Device checklist
 ----------------
-Pull requests adding device support must have the device checklist
-included in their description. The checklist assures core functionality
-of Gluon is well supported on the device.
+The description of pull requests adding device support must include the
+`device integration checklist
+<https://github.com/freifunk-gluon/gluon/wiki/Device-Integration-checklist>`_.
+The checklist ensures that core functionality of Gluon is well supported on the
+device.
 
-The checklist can be found in the `wiki <https://github.com/freifunk-gluon/gluon/wiki/Device-Integration-checklist>`_.
+.. _device-class-definition:
 
 Device classes
 --------------
-Gluon currently is aware of two device classes. Depending on the device class, different
-features can be installed onto the device.
-
-The ``tiny`` device-class contains devices with the following limitations:
-
-* All devices with less than 64 MB of system memory
-* All devices with less than 7 MB of usable firmware space
-* Devices using a single ath10k radio and less than 128MB of system memory
-
-.. _hardware-adding-profiles:
-
-Adding profiles
----------------
-The vast majority of devices with ath9k WLAN are based on the ath79 target of OpenWrt.
-If the hardware you want to add support for is ath79, adding a new profile
-is sufficient.
-
-Profiles are defined in ``targets/*`` in a shell-based DSL (so common shell
-command syntax like ``if`` can be used).
+All supported hardware is categorized into "device classes". This allows to
+adjust the feature set of Gluon to the different hardware's capabilities via
+``site.mk`` without having to list individual devices.
 
-The ``device`` command is used to define an image build for a device. It takes
-two or three parameters.
-
-The first parameter defines the Gluon profile name, which is used to refer to the
-device and is part of the generated image name. The profile name must be same as
-the output of the following command (on the target device), so the autoupdater
-can work::
-
-    lua -e 'print(require("platform_info").get_image_name())'
-
-While porting Gluon to a new device, it might happen that the profile name is
-unknown. Best practise is to generate an image first by using an arbitrary value
-and then executing the lua command on the device and use its output from then on.
-
-The second parameter defines the name of the image files generated by OpenWrt. Usually,
-it is also the OpenWrt profile name; for devices that still use the old image build
-code, a third parameter with the OpenWrt profile name can be passed. The profile names
-can be found in the image Makefiles in ``openwrt/target/linux/<target>/image/Makefile``.
-
-Examples::
-
-    device tp-link-tl-wr1043n-nd-v1 tl-wr1043nd-v1
-    device alfa-network-hornet-ub hornet-ub HORNETUB
-
-Suffixes and extensions
-'''''''''''''''''''''''
+There are currently two devices classes defined: "standard" and "tiny". The
+"tiny" class contains all devices that do not meet the following requirements:
 
-By default, image files are expected to have the extension ``.bin``. In addition,
-the images generated by OpenWrt have a suffix before the extension that defaults to
-``-squashfs-factory`` and ``-squashfs-sysupgrade``.
+- At least 7 MiB of usable firmware space
+- At least 64 MiB of RAM (128MiB for devices with ath10k radio)
 
-This can be changed using the ``factory`` and ``sysupgrade`` commands, either at
-the top of the file to set the defaults for all images, or for a single image. There
-are three forms with 0 to 2 arguments (all work with ``sysupgrade`` as well)::
+Target configuration
+--------------------
+Gluon's hardware support is based on OpenWrt's. For each supported target,
+a configuration file exists at ``targets/<target>-<subtarget>`` (or just
+``target/<target>`` for targets without subtargets) that contains all
+Gluon-specific settings for the target. The generic configuration
+``targets/generic`` contains settings that affect all targets.
 
-    factory SUFFIX .EXT
-    factory .EXT
-    factory
+All targets must be listed in ``target/targets.mk``.
 
-When only an extension is given, the default suffix is retained. When no arguments
-are given, this signals that no factory (or sysupgrade) image exists.
+The target configuration language is based on Lua, so Lua's syntax for variables
+and control structures can be used.
 
-Aliases
-'''''''
+Device definitions
+~~~~~~~~~~~~~~~~~~
+To configure a device to be built for Gluon, the ``device`` function is used.
+In the simplest case, only two arguments are passed, for example:
 
-Sometimes multiple models use the same OpenWrt images. In this case, the ``alias``
-command can be used to create symlinks and additional entries in the autoupdater
-manifest for the alternative models.
+.. code-block:: lua
 
-Standalone images
-'''''''''''''''''
+  device('tp-link-tl-wdr3600-v1', 'tplink_tl-wdr3600-v1')
 
-On targets without *per-device rootfs* support in OpenWrt, the commands described above
-can't be used. Instead, ``factory_image`` and ``sysupgrade_image`` are used::
+The first argument is the device name in Gluon, which is part of the output
+image filename, and must correspond to the model string looked up by the
+autoupdater. The second argument is the corresponding device profile name in
+OpenWrt, as found in ``openwrt/target/linux/<target>/image/*``.
 
-    factory_image PROFILE IMAGE .EXT
-    sysupgrade_image PROFILE IMAGE .EXT
+A table of additional settings can be passed as a third argument:
 
-Again, the profile name must match the value printed by the aforementioned Lua
-command. The image name must match the part between the target name and the extension
-as generated by OpenWrt and is to be omitted when no such part exists.
+.. code-block:: lua
 
-Packages
-''''''''
+  device('ubiquiti-edgerouter-x', 'ubnt_edgerouter-x', {
+    factory = false,
+    packages = {'-hostapd-mini'},
+    manifest_aliases = {
+      'ubnt-erx',
+    },
+  })
 
-The ``packages`` command takes an arbitrary number of arguments. Each argument
-defines an additional package to include in the images in addition to the default
-package sets defined by OpenWrt. When a package name is prefixed by a minus sign, the
-packages are excluded instead.
+The supported additional settings are described in the following sections.
 
-The ``packages`` command may be used at the top of a target definition to modify
-the default package list for all images, or just for a single device (when the
-target supports *per-default rootfs*).
-
-
-Configuration
-'''''''''''''
-
-The ``config`` command allows to add arbitrary target-specific OpenWrt configuration
-to be emitted to ``.config``.
-
-Notes
-'''''
-
-On devices with multiple WLAN adapters, care must also be taken that the primary MAC address is
-configured correctly. ``/lib/gluon/core/sysconfig/primary_mac`` should contain the MAC address which
-can be found on a label on most hardware; if it does not, ``/lib/gluon/upgrade/010-primary-mac``
-in ``gluon-core`` might need a fix. (There have also been cases in which the address was incorrect
-even on devices with only one WLAN adapter, in these cases a OpenWrt bug was the cause).
-
-
-Adding support for new hardware targets
----------------------------------------
-
-Adding a new target is much more complex than adding a new profile. There are two basic steps
-required for adding a new target:
-
-Package adjustments
-'''''''''''''''''''
-
-One package that may need adjustments for new targets is ``libplatforminfo`` (to be found in
-`packages/gluon/libs/libplatforminfo <https://github.com/freifunk-gluon/packages/tree/master/libs/libplatforminfo>`_).
-If the new platform works fine with the definitions found in ``default.c``, nothing needs to be done. Otherwise,
-create a definition for the added target or subtarget, either by symlinking one of the files in the ``templates``
-directory, or adding a new source file.
-
-On many targets, Gluon's network setup scripts (mainly in the package ``gluon-core``)
-won't run correctly without some adjustments, so better double check that everything is fine there (and the files
-``primary_mac``, ``lan_ifname`` and ``wan_ifname`` in ``/lib/gluon/core/sysconfig/`` contain sensible values).
-
-Build system support
-''''''''''''''''''''
-
-A definition for the new target must be created under ``targets``, and it must be added
-to ``targets/targets.mk``. The ``GluonTarget`` macro takes one to two arguments:
-the target name and the OpenWrt subtarget name.
-
-After this, is should be sufficient to call ``make GLUON_TARGET=<target>`` to build the images for the new target.
+Suffixes and extensions
+~~~~~~~~~~~~~~~~~~~~~~~
+For many targets, OpenWrt generates images with the suffixes
+``-squashfs-factory.bin`` and ``-squashfs-sysupgrade.bin``. For devices with
+different image names, is it possible to override the suffixes and extensions
+using the settings ``factory``, ``factory_ext``, ``sysupgrade`` and
+``sysupgrade_ext``, for example:
+
+.. code-block:: lua
+
+  {
+    factory = '-squashfs-combined',
+    factory_ext = '.img.gz',
+    sysupgrade = '-squashfs-combined',
+    sysupgrade_ext = '.img.gz',
+  }
+
+Only settings that differ from the defaults need to be passed. ``factory`` and
+``sysupgrade`` can be set to ``false`` when no such images exist.
+
+For some device types, there are multiple factory images with different
+extensions. ``factory_ext`` can be set to a table of strings to account for this
+case:
+
+.. code-block:: lua
+
+  {
+    factory_ext = {'.img.gz', '.vmdk', '.vdi'},
+  }
+
+TODO: Extra images
+
+Aliases and manifest aliases
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Sometimes multiple devices exist that use the same OpenWrt images. To make it
+easier to find these images, the ``aliases`` setting can be used to define
+additional device names. Gluon will create symlinks for these names in the
+image output directory.
+
+.. code-block:: lua
+
+  device('aruba-ap-303', 'aruba_ap-303', {
+    factory = false,
+    aliases = {'aruba-instant-on-ap11'},
+  })
+
+The aliased name will also be added to the autoupdate manifest, allowing upgrade
+images to be found under the different name on targets that perform model name
+detection at runtime.
+
+It is also possible to add alternative names to the autoupdater manifest without
+creating a symlink by using ``manifest_aliases`` instead of ``aliases``, which
+should be done when the alternative name does not refer to a separate device.
+This is particularly useful to allow the autoupdater to work when the model name
+changed between Gluon versions.
+
+Package lists
+~~~~~~~~~~~~~
+Gluon generates lists of packages that are installed in all images based on a
+default list and the features and packages specified in the site configuration.
+
+In addition, OpenWrt defines additional per-device package lists. These lists
+may be modified in Gluon's device definitions, for example to include additional
+drivers and firmware, or to remove unneeded software. Packages to remove are
+prefixed with a ``-`` character.
+
+For many ath10k-based devices, this is used to replace the "CT" variant of
+ath10k with the mainline-based version:
+
+.. code-block:: lua
+
+  local ATH10K_PACKAGES_QCA9880 = {
+    'kmod-ath10k',
+    '-kmod-ath10k-ct',
+    '-kmod-ath10k-ct-smallbuffers',
+    'ath10k-firmware-qca988x',
+    '-ath10k-firmware-qca988x-ct',
+  }
+  device('openmesh-a40', 'openmesh_a40', {
+    packages = ATH10K_PACKAGES_QCA9880,
+    factory = false,
+  })
+
+This example also shows how to define a local variable, allowing the package
+list to be reused for multiple devices.
+
+Device flags
+~~~~~~~~~~~~
+
+The settings ``class``, ``deprecated`` or ``broken`` should be set according to
+the device support status. The default values are as follows:
+
+.. code-block:: lua
+
+  {
+    class = 'standard',
+    deprecated = false,
+    broken = false,
+  }
+
+- Device classes are described in :ref:`device-class-definition`
+- Broken devices are untested or do not meet our requirements as given by the
+  device checklist
+- Deprecated devices are slated for removal in a future Gluon version due to
+  hardware constraints
+
+Global settings
+~~~~~~~~~~~~~~~
+There is a number of directives that can be used outside of a ``device()``
+definition:
+
+- ``include('filename')``: Include another file with global settings
+- ``config(key, value)``: Set a config symbol in OpenWrt's ``.config``. Value
+  may be a string, number, boolean, or nil. Booleans and nil are used for
+  tristate symbols, where nil sets the symbol to ``m``.
+- ``try_config(key, value)``: Like ``config()``, but do not fail if setting
+  the symbol is not possible (usually because its dependencies are not met)
+- ``packages { 'package1', '-package2', ... }``: Define a list of packages to
+  add or remove for all devices of a target. Package lists passed to multiple
+  calls of ``packages`` will be aggregated.
+- ``defaults { key = value, ... }``: Set default values for any of the
+  additional settings that can be passed to ``device()``.
+
+Helper functions
+~~~~~~~~~~~~~~~~
+The following helpers can be used in the target configuration:
+
+- ``env.KEY`` allows to access environment variables
+- ``istrue(value)`` returns true if the passed string is a positive number
+  (often used with ``env``, for example ``if istrue(env.GLUON_DEBUG) then ...``)
+
+Hardware support in packages
+----------------------------
+In addition to the target configuration files, some device-specific changes may
+be required in packages.
+
+gluon-core
+~~~~~~~~~~
+- ``/lib/gluon/upgrade/010-primary-mac``: Override primary MAC address selection
+
+  Usually, the primary (label) MAC address is defined in OpenWrt's Device Trees.
+  For devices or targets where this is not the case, it is possible to specify
+  what interface to take the primary MAC address from in ``010-primary-mac``.
+
+- ``/lib/gluon/upgrade/020-interfaces``: Override LAN/WAN interface assignment
+
+  On PoE-powered devices, the PoE input port should be "WAN".
+
+- ``/usr/lib/lua/gluon/platform.lua``: Contains a list of outdoor devices
+
+gluon-setup-mode
+~~~~~~~~~~~~~~~~
+- ``/lib/gluon/upgrade/320-setup-ifname``: Contains a list of devices that use
+  the WAN port for the config mode
+
+  On PoE-powered devices, the PoE input port should be used for the config
+  mode. This is handled correctly by default for outdoor devices listed in
+  ``platform.lua``.
+
+libplatforminfo
+~~~~~~~~~~~~~~~
+When adding support for a new target to Gluon, it may be necessary to adjust
+libplatforminfo to define how autoupdater image names are derived from the
+model name.