The wifi component consists of a port of the Linux mac802.11 stack as well
as libnl and wpa_supplicant to Genode. Depending on the used platform it
features a selection of drivers for wireless devices. For example on the
PC platform it contains the ath9k, iwlwifi and rtlwifi drivers for PCI(e)
devices.

In contrast to other DDE Linux based drivers the actual driver portion is
confined to its own library to better isolate the various parts of the driver.
The 'wifi' binary is the generic management part that includes the Wifi
configuration interface and the 'wpa_supplicant'. A suitable driver library
is loaded at run-time (see section [Debugging]).


Configuration
~~~~~~~~~~~~~

This configuration snippet shows how to start the component on the PC
platform.

!start wifi | caps: 250 | ram: 32M
!+ provides | + service Nic
!+ config
!  + vfs
!    + dir dev
!    | + jitterentropy random
!    | + jitterentropy urandom
!    | + log
!    | + null
!    | + rtc
!    + dir firmware | + tar wifi_firmware.tar
!  + libc | stdout: /dev/log | stderr: /dev/null | rtc: /dev/rtc
!+ route
!  + service ROM | label: wifi.lib.so       | + parent | label: pc_wifi.lib.so
!  + service ROM | label: wifi_firmware.tar | + parent | label: pc_wifi_firmware.tar
!  + service Rtc                            | + any-child
!  + any-service
!    + parent
!    + any-child
!-

On other platforms the wifi library will be different. So, the
following snippet illustrates the use of the driver on the PinePhone.

!start wifi | caps: 250 | ram: 32M
!+ provides | + service Nic
!+ config
!  + vfs
!    + dir dev
!    | + jitterentropy random
!    | + jitterentropy urandom
!    | + log
!    | + null
!    | + rtc
!    + dir firmware | + tar wifi_firmware.tar
!  + libc | stdout: /dev/log | stderr: /dev/null | rtc: /dev/rtc
!+ route
!  + service ROM | label: wifi.lib.so       | + parent | label: a64_wifi.lib.so
!  + service ROM | label: wifi_firmware.tar | + parent | label: a64_wifi_firmware.tar
!  + service ROM | label: dtb               | + parent | label: wifi-pinephone.dtb
!  + service Rtc                            | + any-child
!  + any-service
!    + parent
!    + any-child
!-

Note the ROM route for the device-tree binary that is essential on
ARM-based platforms. The name of the request DTB can by changed
by setting the 'dtb' attribute in the config node.

Depending on the used device additional firmware images might be
required. The driver will request them by accessing the '/firmware'
directory in the driver's local VFS. It is up to the configuration
how those files are made available. In these examples they are
contained in an '.tar' archive that is request as a ROM module.

The driver will request access to the 'wifi_config' ROM module that
contains its actual configuration in the 'wifi_config' node. This
node features the following attributes.

* :scan_interval: sets the time interval in seconds in which scan
  operations are requested and is used when not already connected
  to a network. The default is 5 seconds.

* :update_quality_interval: sets the time interval in which the current
  signal quality of the connected access point is updated (RSSI polling).
  The default value is 30 seconds.

* :rfkill: allows to temporarily prevent any radio activity. The
  default is 'false'.

* :bgscan: is an expert option that configures the way the
  supplicant performs background scanning to steer or rather optimize
  roaming decisions within the same network (SSID). The syntax of the
  option string corresponds to the original WPA-supplicant 'bgscan' option.
  The default value is set to 'simple:30:-70:600'. This functionality can
  be disabled by specifying an empty value, e.g. 'bgscan=""'. If bgscan is
  disabled the 'accesspoints' report will not be updated while the
  supplicant is connected to a network.

* :log_level: allows for steering the verbosity of the supplicant
  and may assist while diagnosing problems with the driver.
  Valid values correspond to levels used by the supplicant
  and are as follows 'excessive', 'msgdump', 'debug', 'info',
  'warning' and 'error'. The default value is 'error' and configures
  the least amount of verbosity.

* :verbose: allows for logging of diagnostic messages generated
  by the managing portion of the driver. The default is 'false'.

Besides those attributes the 'wifi_config' node can host 'network'
nodes. Such a node describes the parameters of a network and its
existence implies the intent to join the network. It has the following
attributes.

* :ssid: sets the name of the network. It also serves as look-up key
  for a network and adding multiple nodes with the same SSID are
  treated as the same node whose attributes are changing.

  Note: the SSID is presented in an encoded notion where non-printeable
        ASCII characters (and in addition '&', '"', '|' and '\') are
        provided as '\x[0-9][a-f][0-9][a-f]' sequence.

* :bssid: can be used to select a specific access point within a
  network.

* :protection: specifies the used protection mechanism of the
  network. Valid values are 'WPA', 'WPA2', 'WPA3' and 'NONE'.
  The last one is used in case the network uses other means of
  protection and access is open.

  Note: currently only personal WPA protection using a pre-shared-key
        (PSK) is supported.

* :passphrase: sets an ASCII passphrase that is used to derive the
  PSK and is required should the network be protected.
  Like the SSID it supports the encoded notion and is required in
  case the passphrase contains one of mentioned characters that
  need encoding.

Of all attributes solely the 'ssid' attribute is mandatory and all
others are optional. They should be used when needed only.

Note:  If configured networks overlap in locality, the driver might
       switch dynamically between these networks.

To scan for a hidden network a 'explicit_scan' node must by added.
It contains the following mandatory attribute.

* :ssid: set the name of the hidden network to scan for

  Note: the SSID is copied verbatim and at the moment, there is no way
        to express or escape non alphanumeric characters.

The number of hidden networks that can be scanned for is restricted to
a practical amount, around 48 networks, and all networks after hitting
the limit are silently omitted.

The following exemplary snippet showcases a config for two networks where
the first one should be automatically considered for joining and uses 'WPA2'
while the second one is hidden but should show up in the scan results.

+ wifi_config | scan_interval: 10 | update_quality_interval: 30
  + network | ssid: Zero | protection: WPA2 | passphrase: allyourbase
  + explicit_scan | ssid: Skynet
-

To join the hidden network a corresponding 'network' is needed.

The wifi driver uses two distinct reports, 'state' and 'accesspoints',
to communicate its state of connectivity and information about the wireless
access points in the vicinity to other components.
This exemplary 'accesspoints' report shows its general structure.

!accesspoints
!+ accesspoint | ssid: skynet | bssid: 00:01:02:03:04:05 | freq: 5180 | quality: 40
!+ accesspoint | ssid: foobar | bssid: 01:02:03:04:05:06 | freq: 2418 | quality: 70 | protection: WPA2
!+ accesspoint | ssid: foobar | bssid: 01:02:03:04:05:07 | freq: 2437 | quality: 10 | protection: WPA2
!-

The 'accesspoints' node can contain a fluctuating number of 'accesspoint'
nodes that describe an access point with the following attributes.

* :ssid: specifies the name of the network the access point advertises.
  Empty SSIDs are not reported.

* :bssid: specifies the physical address of the access point.

* :freq: specifies the frequency used by the access point.

* :quality: specifies the approximated link quality (calculated from the
  RSSI value).

* :protection: specifies which kind of protection is employed by the access
  point.

  Note: when a mixed protection is used by the network, like WPA2-PSK and
        WPA3-PSK mixed-mode, only the strongest protection (WPA3-PSK) is
        advertised.

The 'state' report provides information about the state of the connectivity
and looks as follows.

!state
!+ accesspoint | ssid: foobar | bssid: 01:02:03:04:05:06 | quality: 70 | freq: 2418 | state: connected
!-

The 'state' node encompasses one 'accesspoint' node that has the
following additional attributes beside the ones already discussed.

* :state: specifies the actual state of connectivity. Valid values
  are 'connected', 'connecting' and 'disconnected'.

* :auth_failure: is an optional attribute and set to 'true' in case
  the PSK was wrong

* :rfkilled: is an optional attribute and set to 'true' whenever
  radio activity was temporarily disabled.

* :not_found: is an optional attribute and is only set when a single
  network was configured but could not be found.

By subscribing to both reports and providing the required 'wifi_config'
ROM module, a component is able control the wireless driver.

The driver optionally reports the following information under the
label "devices" if requested in the config as depicted.

!config
!+ report | mac_address: true
!-

!devices
!+ nic | mac_address: 02:00:00:00:00:01
!-


Best practices
~~~~~~~~~~~~~~

The following section describes common use-cases, like being managed
or configured explicitly by a user provided configuration, and how to
interact with the driver in each of these cases. The way Sculpt handles
the driver can serve as an example.

Externally managed (Sculpt mode)
--------------------------------

When the driver is managed by an external component via its configuration
based on its 'state' and 'accesspoints' reports the configuration should
contain at most one 'network' node. This node contains the network the
management component has chosen to join after evaluating the 'accesspoints'
report. This makes it easier for the management component to process the
'state' reports as all reports are immediate and logically belong to one
network and there is no unexpected switch between different networks.
The 'state' report contains at least the mandatory 'state' attribute that
denotes the state of connectivity, the 'ssid' and 'bssid' as well as the
'freq' attribute. The existence of any other attributes is not certain and
depends on the state of connectivity. The signal quality may not always
be available and the external component should account for that.

For every hidden network a 'explicit_scan' node must be added for the
access points to appear in the 'accesspoints' report. Such a node should
not be added for non-hidden networks as it influences the scanning attempt
and could lead to longer join times. As it also proactively asks for the
network it could allow for fingerprinting a device according to its list
of hidden networks.

Roaming within one network is performed according to the 'bgscan' settings
automatically. This implicitly also refreshes the 'accesspoints' report
that then may be considered by the management component to switch to a
different network by replacing the 'network' node in the configuration.
It is up to the management component to make an educated decision. For
roaming to work properly the management component should not specify
the 'bssid' attribute in the 'network' node.

Removing the 'network' node will lead to a disconnect from the network.

Manually managed
----------------

When the driver is manually or rather explicitly managed by a user provided
configuration it generally exhibits the same behavior. However, as a common
pattern, user generated configurations tend to contain multiple networks and
it is expected that the driver joins a known network if it encounters one.
Normally these networks do not overlap in locality, i.e. there is one at home,
another one at work, and still a different one at a friend's dwelling place.
Providing multiple nodes covers this use case in a fair fashion as a network
is made known to the driver by adding the corresponding 'network' node to
the configuration.

Since the driver does not know in advance which network it will end up joining
providing immediate feedback is not possible and switching to a different
network may occur naturally. This behavior renders the configuration of
multiple network nodes impractical for management components and should be
avoided.


Debugging
~~~~~~~~~

As mentioned in the introduction the 'wifi' component is special in the
regard that the actual driver is provided as a shared-object to better isolate
it from the the driver binary that is a Libc::Component managing the
'wpa_supplicant'. Since this code and in return the binary is the same for each
platform it is linked against an artificial 'wifi' library that only exists as
an ABI stub created via 'lib/symbols/wifi'. In case the driver is integrated
via depot archives this is, besides setting the proper ROM routes, of no
concern. However, when the driver is built without the depot, the boot image
assemble-mechanism tries to include the non-existent 'wifi.lib.so' library. To
prevent that from failing one way is adding a dummy file - the
'repos/pc/run/pc_wifi.run' illustrates how to do that.

During development it might be necessary to automatically generate certain
dummy functions as is normally the case with drivers ported over from Linux.
To make the existing tooling (tool/dde_linux) work with the wifi driver it
is necessary to link the driver binary directly against the driver library
rather then stub ABI library. This is achieved by adapting the driver's
'target.mk' file. In

!TARGET  := wifi
!SRC_CC  := main.cc wpa.cc access_firmware.cc
!LIBS    := base wifi
![…]

'LIBS' must be changed as follows in case the PC wifi driver library is
used.

!LIBS    := base pc_wifi

Afterwards the driver library is built by the 'driver/wifi' target and
since it is now a direct dependency 'tool/dde_linux/create_dummies' can
by used to created the 'generated_dummies.c' file.
