|
@@ -0,0 +1,195 @@
|
|
|
+===================
|
|
|
+Fallback mechanisms
|
|
|
+===================
|
|
|
+
|
|
|
+A fallback mechanism is supported to allow to overcome failures to do a direct
|
|
|
+filesystem lookup on the root filesystem or when the firmware simply cannot be
|
|
|
+installed for practical reasons on the root filesystem. The kernel
|
|
|
+configuration options related to supporting the firmware fallback mechanism are:
|
|
|
+
|
|
|
+ * CONFIG_FW_LOADER_USER_HELPER: enables building the firmware fallback
|
|
|
+ mechanism. Most distributions enable this option today. If enabled but
|
|
|
+ CONFIG_FW_LOADER_USER_HELPER_FALLBACK is disabled, only the custom fallback
|
|
|
+ mechanism is available and for the request_firmware_nowait() call.
|
|
|
+ * CONFIG_FW_LOADER_USER_HELPER_FALLBACK: force enables each request to
|
|
|
+ enable the kobject uevent fallback mechanism on all firmware API calls
|
|
|
+ except request_firmware_direct(). Most distributions disable this option
|
|
|
+ today. The call request_firmware_nowait() allows for one alternative
|
|
|
+ fallback mechanism: if this kconfig option is enabled and your second
|
|
|
+ argument to request_firmware_nowait(), uevent, is set to false you are
|
|
|
+ informing the kernel that you have a custom fallback mechanism and it will
|
|
|
+ manually load the firmware. Read below for more details.
|
|
|
+
|
|
|
+Note that this means when having this configuration:
|
|
|
+
|
|
|
+CONFIG_FW_LOADER_USER_HELPER=y
|
|
|
+CONFIG_FW_LOADER_USER_HELPER_FALLBACK=n
|
|
|
+
|
|
|
+the kobject uevent fallback mechanism will never take effect even
|
|
|
+for request_firmware_nowait() when uevent is set to true.
|
|
|
+
|
|
|
+Justifying the firmware fallback mechanism
|
|
|
+==========================================
|
|
|
+
|
|
|
+Direct filesystem lookups may fail for a variety of reasons. Known reasons for
|
|
|
+this are worth itemizing and documenting as it justifies the need for the
|
|
|
+fallback mechanism:
|
|
|
+
|
|
|
+* Race against access with the root filesystem upon bootup.
|
|
|
+
|
|
|
+* Races upon resume from suspend. This is resolved by the firmware cache, but
|
|
|
+ the firmware cache is only supported if you use uevents, and its not
|
|
|
+ supported for request_firmware_into_buf().
|
|
|
+
|
|
|
+* Firmware is not accessible through typical means:
|
|
|
+ * It cannot be installed into the root filesystem
|
|
|
+ * The firmware provides very unique device specific data tailored for
|
|
|
+ the unit gathered with local information. An example is calibration
|
|
|
+ data for WiFi chipsets for mobile devices. This calibration data is
|
|
|
+ not common to all units, but tailored per unit. Such information may
|
|
|
+ be installed on a separate flash partition other than where the root
|
|
|
+ filesystem is provided.
|
|
|
+
|
|
|
+Types of fallback mechanisms
|
|
|
+============================
|
|
|
+
|
|
|
+There are really two fallback mechanisms available using one shared sysfs
|
|
|
+interface as a loading facility:
|
|
|
+
|
|
|
+* Kobject uevent fallback mechanism
|
|
|
+* Custom fallback mechanism
|
|
|
+
|
|
|
+First lets document the shared sysfs loading facility.
|
|
|
+
|
|
|
+Firmware sysfs loading facility
|
|
|
+===============================
|
|
|
+
|
|
|
+In order to help device drivers upload firmware using a fallback mechanism
|
|
|
+the firmware infrastructure creates a sysfs interface to enable userspace
|
|
|
+to load and indicate when firmware is ready. The sysfs directory is created
|
|
|
+via fw_create_instance(). This call creates a new struct device named after
|
|
|
+the firmware requested, and establishes it in the device hierarchy by
|
|
|
+associating the device used to make the request as the device's parent.
|
|
|
+The sysfs directory's file attributes are defined and controlled through
|
|
|
+the new device's class (firmare_class) and group (fw_dev_attr_groups).
|
|
|
+This is actually where the original firmware_class.c file name comes from,
|
|
|
+as originally the only firmware loading mechanism available was the
|
|
|
+mechanism we now use as a fallback mechanism.
|
|
|
+
|
|
|
+To load firmware using the sysfs interface we expose a loading indicator,
|
|
|
+and a file upload firmware into:
|
|
|
+
|
|
|
+ * /sys/$DEVPATH/loading
|
|
|
+ * /sys/$DEVPATH/data
|
|
|
+
|
|
|
+To upload firmware you will echo 1 onto the loading file to indicate
|
|
|
+you are loading firmware. You then cat the firmware into the data file,
|
|
|
+and you notify the kernel the firmware is ready by echo'ing 0 onto
|
|
|
+the loading file.
|
|
|
+
|
|
|
+The firmware device used to help load firmware using sysfs is only created if
|
|
|
+direct firmware loading fails and if the fallback mechanism is enabled for your
|
|
|
+firmware request, this is set up with fw_load_from_user_helper(). It is
|
|
|
+important to re-iterate that no device is created if a direct filesystem lookup
|
|
|
+succeeded.
|
|
|
+
|
|
|
+Using::
|
|
|
+
|
|
|
+ echo 1 > /sys/$DEVPATH/loading
|
|
|
+
|
|
|
+Will clean any previous partial load at once and make the firmware API
|
|
|
+return an error. When loading firmware the firmware_class grows a buffer
|
|
|
+for the firmware in PAGE_SIZE increments to hold the image as it comes in.
|
|
|
+
|
|
|
+firmware_data_read() and firmware_loading_show() are just provided for the
|
|
|
+test_firmware driver for testing, they are not called in normal use or
|
|
|
+expected to be used regularly by userspace.
|
|
|
+
|
|
|
+Firmware kobject uevent fallback mechanism
|
|
|
+==========================================
|
|
|
+
|
|
|
+Since a device is created for the sysfs interface to help load firmware as a
|
|
|
+fallback mechanism userspace can be informed of the addition of the device by
|
|
|
+relying on kobject uevents. The addition of the device into the device
|
|
|
+hierarchy means the fallback mechanism for firmware loading has been initiated.
|
|
|
+For details of implementation refer to _request_firmware_load(), in particular
|
|
|
+on the use of dev_set_uevent_suppress() and kobject_uevent().
|
|
|
+
|
|
|
+The kernel's kobject uevent mechanism is implemented in lib/kobject_uevent.c,
|
|
|
+it issues uevents to userspace. As a supplement to kobject uevents Linux
|
|
|
+distributions could also enable CONFIG_UEVENT_HELPER_PATH, which makes use of
|
|
|
+core kernel's usermode helper (UMH) functionality to call out to a userspace
|
|
|
+helper for kobject uevents. In practice though no standard distribution has
|
|
|
+ever used the CONFIG_UEVENT_HELPER_PATH. If CONFIG_UEVENT_HELPER_PATH is
|
|
|
+enabled this binary would be called each time kobject_uevent_env() gets called
|
|
|
+in the kernel for each kobject uevent triggered.
|
|
|
+
|
|
|
+Different implementations have been supported in userspace to take advantage of
|
|
|
+this fallback mechanism. When firmware loading was only possible using the
|
|
|
+sysfs mechanism the userspace component "hotplug" provided the functionality of
|
|
|
+monitoring for kobject events. Historically this was superseded be systemd's
|
|
|
+udev, however firmware loading support was removed from udev as of systemd
|
|
|
+commit be2ea723b1d0 ("udev: remove userspace firmware loading support")
|
|
|
+as of v217 on August, 2014. This means most Linux distributions today are
|
|
|
+not using or taking advantage of the firmware fallback mechanism provided
|
|
|
+by kobject uevents. This is specially exacerbated due to the fact that most
|
|
|
+distributions today disable CONFIG_FW_LOADER_USER_HELPER_FALLBACK.
|
|
|
+
|
|
|
+Refer to do_firmware_uevent() for details of the kobject event variables
|
|
|
+setup. Variables passwdd with a kobject add event:
|
|
|
+
|
|
|
+* FIRMWARE=firmware name
|
|
|
+* TIMEOUT=timeout value
|
|
|
+* ASYNC=whether or not the API request was asynchronous
|
|
|
+
|
|
|
+By default DEVPATH is set by the internal kernel kobject infrastructure.
|
|
|
+Below is an example simple kobject uevent script::
|
|
|
+
|
|
|
+ # Both $DEVPATH and $FIRMWARE are already provided in the environment.
|
|
|
+ MY_FW_DIR=/lib/firmware/
|
|
|
+ echo 1 > /sys/$DEVPATH/loading
|
|
|
+ cat $MY_FW_DIR/$FIRMWARE > /sys/$DEVPATH/data
|
|
|
+ echo 0 > /sys/$DEVPATH/loading
|
|
|
+
|
|
|
+Firmware custom fallback mechanism
|
|
|
+==================================
|
|
|
+
|
|
|
+Users of the request_firmware_nowait() call have yet another option available
|
|
|
+at their disposal: rely on the sysfs fallback mechanism but request that no
|
|
|
+kobject uevents be issued to userspace. The original logic behind this
|
|
|
+was that utilities other than udev might be required to lookup firmware
|
|
|
+in non-traditional paths -- paths outside of the listing documented in the
|
|
|
+section 'Direct filesystem lookup'. This option is not available to any of
|
|
|
+the other API calls as uevents are always forced for them.
|
|
|
+
|
|
|
+Since uevents are only meaningful if the fallback mechanism is enabled
|
|
|
+in your kernel it would seem odd to enable uevents with kernels that do not
|
|
|
+have the fallback mechanism enabled in their kernels. Unfortunately we also
|
|
|
+rely on the uevent flag which can be disabled by request_firmware_nowait() to
|
|
|
+also setup the firmware cache for firmware requests. As documented above,
|
|
|
+the firmware cache is only set up if uevent is enabled for an API call.
|
|
|
+Although this can disable the firmware cache for request_firmware_nowait()
|
|
|
+calls, users of this API should not use it for the purposes of disabling
|
|
|
+the cache as that was not the original purpose of the flag. Not setting
|
|
|
+the uevent flag means you want to opt-in for the firmware fallback mechanism
|
|
|
+but you want to suppress kobject uevents, as you have a custom solution which
|
|
|
+will monitor for your device addition into the device hierarchy somehow and
|
|
|
+load firmware for you through a custom path.
|
|
|
+
|
|
|
+Firmware fallback timeout
|
|
|
+=========================
|
|
|
+
|
|
|
+The firmware fallback mechanism has a timeout. If firmware is not loaded
|
|
|
+onto the sysfs interface by the timeout value an error is sent to the
|
|
|
+driver. By default the timeout is set to 60 seconds if uevents are
|
|
|
+desirable, otherwise MAX_JIFFY_OFFSET is used (max timeout possible).
|
|
|
+The logic behind using MAX_JIFFY_OFFSET for non-uevents is that a custom
|
|
|
+solution will have as much time as it needs to load firmware.
|
|
|
+
|
|
|
+You can customize the firmware timeout by echo'ing your desired timeout into
|
|
|
+the following file:
|
|
|
+
|
|
|
+* /sys/class/firmware/timeout
|
|
|
+
|
|
|
+If you echo 0 into it means MAX_JIFFY_OFFSET will be used. The data type
|
|
|
+for the timeout is an int.
|