diff options
author | Luis R. Rodriguez <mcgrof@kernel.org> | 2016-12-16 03:10:36 -0800 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2017-01-11 09:42:59 +0100 |
commit | 113ccc38378b6f0b24c0993040c6044e35163a51 (patch) | |
tree | 2a1fbd9a1ad5144dc316834ccf52505a84191cb3 /Documentation/driver-api/firmware | |
parent | 880444e214cfd293a2e8cc4bd3505f7ffa6ce33a (diff) | |
download | linux-113ccc38378b6f0b24c0993040c6044e35163a51.tar.bz2 |
firmware: revamp firmware documentation
Understanding this code is getting out of control without any
notes. Give the firmware_class driver a much needed documentation love,
and while at it convert it to the new sphinx documentation format.
v2: typos and small fixes
Signed-off-by: Luis R. Rodriguez <mcgrof@kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'Documentation/driver-api/firmware')
-rw-r--r-- | Documentation/driver-api/firmware/built-in-fw.rst | 38 | ||||
-rw-r--r-- | Documentation/driver-api/firmware/core.rst | 16 | ||||
-rw-r--r-- | Documentation/driver-api/firmware/direct-fs-lookup.rst | 30 | ||||
-rw-r--r-- | Documentation/driver-api/firmware/fallback-mechanisms.rst | 195 | ||||
-rw-r--r-- | Documentation/driver-api/firmware/firmware_cache.rst | 51 | ||||
-rw-r--r-- | Documentation/driver-api/firmware/fw_search_path.rst | 26 | ||||
-rw-r--r-- | Documentation/driver-api/firmware/index.rst | 16 | ||||
-rw-r--r-- | Documentation/driver-api/firmware/introduction.rst | 27 | ||||
-rw-r--r-- | Documentation/driver-api/firmware/lookup-order.rst | 18 | ||||
-rw-r--r-- | Documentation/driver-api/firmware/request_firmware.rst | 56 |
10 files changed, 473 insertions, 0 deletions
diff --git a/Documentation/driver-api/firmware/built-in-fw.rst b/Documentation/driver-api/firmware/built-in-fw.rst new file mode 100644 index 000000000000..7300e66857f8 --- /dev/null +++ b/Documentation/driver-api/firmware/built-in-fw.rst @@ -0,0 +1,38 @@ +================= +Built-in firmware +================= + +Firmware can be built-in to the kernel, this means building the firmware +into vmlinux directly, to enable avoiding having to look for firmware from +the filesystem. Instead, firmware can be looked for inside the kernel +directly. You can enable built-in firmware using the kernel configuration +options: + + * CONFIG_EXTRA_FIRMWARE + * CONFIG_EXTRA_FIRMWARE_DIR + +This should not be confused with CONFIG_FIRMWARE_IN_KERNEL, this is for drivers +which enables firmware to be built as part of the kernel build process. This +option, CONFIG_FIRMWARE_IN_KERNEL, will build all firmware for all drivers +enabled which ship its firmware inside the Linux kernel source tree. + +There are a few reasons why you might want to consider building your firmware +into the kernel with CONFIG_EXTRA_FIRMWARE though: + +* Speed +* Firmware is needed for accessing the boot device, and the user doesn't + want to stuff the firmware into the boot initramfs. + +Even if you have these needs there are a few reasons why you may not be +able to make use of built-in firmware: + +* Legalese - firmware is non-GPL compatible +* Some firmware may be optional +* Firmware upgrades are possible, therefore a new firmware would implicate + a complete kernel rebuild. +* Some firmware files may be really large in size. The remote-proc subsystem + is an example subsystem which deals with these sorts of firmware +* The firmware may need to be scraped out from some device specific location + dynamically, an example is calibration data for for some WiFi chipsets. This + calibration data can be unique per sold device. + diff --git a/Documentation/driver-api/firmware/core.rst b/Documentation/driver-api/firmware/core.rst new file mode 100644 index 000000000000..1d1688cbc078 --- /dev/null +++ b/Documentation/driver-api/firmware/core.rst @@ -0,0 +1,16 @@ +========================== +Firmware API core features +========================== + +The firmware API has a rich set of core features available. This section +documents these features. + +.. toctree:: + + fw_search_path + built-in-fw + firmware_cache + direct-fs-lookup + fallback-mechanisms + lookup-order + diff --git a/Documentation/driver-api/firmware/direct-fs-lookup.rst b/Documentation/driver-api/firmware/direct-fs-lookup.rst new file mode 100644 index 000000000000..82b4d585a213 --- /dev/null +++ b/Documentation/driver-api/firmware/direct-fs-lookup.rst @@ -0,0 +1,30 @@ +======================== +Direct filesystem lookup +======================== + +Direct filesystem lookup is the most common form of firmware lookup performed +by the kernel. The kernel looks for the firmware directly on the root +filesystem in the paths documented in the section 'Firmware search paths'. +The filesystem lookup is implemented in fw_get_filesystem_firmware(), it +uses common core kernel file loader facility kernel_read_file_from_path(). +The max path allowed is PATH_MAX -- currently this is 4096 characters. + +It is recommended you keep /lib/firmware paths on your root filesystem, +avoid having a separate partition for them in order to avoid possible +races with lookups and avoid uses of the custom fallback mechanisms +documented below. + +Firmware and initramfs +---------------------- + +Drivers which are built-in to the kernel should have the firmware integrated +also as part of the initramfs used to boot the kernel given that otherwise +a race is possible with loading the driver and the real rootfs not yet being +available. Stuffing the firmware into initramfs resolves this race issue, +however note that using initrd does not suffice to address the same race. + +There are circumstances that justify not wanting to include firmware into +initramfs, such as dealing with large firmware firmware files for the +remote-proc subsystem. For such cases using a userspace fallback mechanism +is currently the only viable solution as only userspace can know for sure +when the real rootfs is ready and mounted. diff --git a/Documentation/driver-api/firmware/fallback-mechanisms.rst b/Documentation/driver-api/firmware/fallback-mechanisms.rst new file mode 100644 index 000000000000..d19354794e67 --- /dev/null +++ b/Documentation/driver-api/firmware/fallback-mechanisms.rst @@ -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. diff --git a/Documentation/driver-api/firmware/firmware_cache.rst b/Documentation/driver-api/firmware/firmware_cache.rst new file mode 100644 index 000000000000..2210e5bfb332 --- /dev/null +++ b/Documentation/driver-api/firmware/firmware_cache.rst @@ -0,0 +1,51 @@ +============== +Firmware cache +============== + +When Linux resumes from suspend some device drivers require firmware lookups to +re-initialize devices. During resume there may be a period of time during which +firmware lookups are not possible, during this short period of time firmware +requests will fail. Time is of essence though, and delaying drivers to wait for +the root filesystem for firmware delays user experience with device +functionality. In order to support these requirements the firmware +infrastructure implements a firmware cache for device drivers for most API +calls, automatically behind the scenes. + +The firmware cache makes using certain firmware API calls safe during a device +driver's suspend and resume callback. Users of these API calls needn't cache +the firmware by themselves for dealing with firmware loss during system resume. + +The firmware cache works by requesting for firmware prior to suspend and +caching it in memory. Upon resume device drivers using the firmware API will +have access to the firmware immediately, without having to wait for the root +filesystem to mount or dealing with possible race issues with lookups as the +root filesystem mounts. + +Some implementation details about the firmware cache setup: + +* The firmware cache is setup by adding a devres entry for each device that + uses all synchronous call except :c:func:`request_firmware_into_buf`. + +* If an asynchronous call is used the firmware cache is only set up for a + device if if the second argument (uevent) to request_firmware_nowait() is + true. When uevent is true it requests that a kobject uevent be sent to + userspace for the firmware request. For details refer to the Fackback + mechanism documented below. + +* If the firmware cache is determined to be needed as per the above two + criteria the firmware cache is setup by adding a devres entry for the + device making the firmware request. + +* The firmware devres entry is maintained throughout the lifetime of the + device. This means that even if you release_firmware() the firmware cache + will still be used on resume from suspend. + +* The timeout for the fallback mechanism is temporarily reduced to 10 seconds + as the firmware cache is set up during suspend, the timeout is set back to + the old value you had configured after the cache is set up. + +* Upon suspend any pending non-uevent firmware requests are killed to avoid + stalling the kernel, this is done with kill_requests_without_uevent(). Kernel + calls requiring the non-uevent therefore need to implement their own firmware + cache mechanism but must not use the firmware API on suspend. + diff --git a/Documentation/driver-api/firmware/fw_search_path.rst b/Documentation/driver-api/firmware/fw_search_path.rst new file mode 100644 index 000000000000..a360f1009fa3 --- /dev/null +++ b/Documentation/driver-api/firmware/fw_search_path.rst @@ -0,0 +1,26 @@ +===================== +Firmware search paths +===================== + +The following search paths are used to look for firmware on your +root filesystem. + +* fw_path_para - module parameter - default is empty so this is ignored +* /lib/firmware/updates/UTS_RELEASE/ +* /lib/firmware/updates/ +* /lib/firmware/UTS_RELEASE/ +* /lib/firmware/ + +The module parameter ''path'' can be passed to the firmware_class module +to activate the first optional custom fw_path_para. The custom path can +only be up to 256 characters long. The kernel parameter passed would be: + +* 'firmware_class.path=$CUSTOMIZED_PATH' + +There is an alternative to customize the path at run time after bootup, you +can use the file: + +* /sys/module/firmware_class/parameters/path + +You would echo into it your custom path and firmware requested will be +searched for there first. diff --git a/Documentation/driver-api/firmware/index.rst b/Documentation/driver-api/firmware/index.rst new file mode 100644 index 000000000000..1abe01793031 --- /dev/null +++ b/Documentation/driver-api/firmware/index.rst @@ -0,0 +1,16 @@ +================== +Linux Firmware API +================== + +.. toctree:: + + introduction + core + request_firmware + +.. only:: subproject and html + + Indices + ======= + + * :ref:`genindex` diff --git a/Documentation/driver-api/firmware/introduction.rst b/Documentation/driver-api/firmware/introduction.rst new file mode 100644 index 000000000000..211cb44eb972 --- /dev/null +++ b/Documentation/driver-api/firmware/introduction.rst @@ -0,0 +1,27 @@ +============ +Introduction +============ + +The firmware API enables kernel code to request files required +for functionality from userspace, the uses vary: + +* Microcode for CPU errata +* Device driver firmware, required to be loaded onto device + microcontrollers +* Device driver information data (calibration data, EEPROM overrides), + some of which can be completely optional. + +Types of firmware requests +========================== + +There are two types of calls: + +* Synchronous +* Asynchronous + +Which one you use vary depending on your requirements, the rule of thumb +however is you should strive to use the asynchronous APIs unless you also +are already using asynchronous initialization mechanisms which will not +stall or delay boot. Even if loading firmware does not take a lot of time +processing firmware might, and this can still delay boot or initialization, +as such mechanisms such as asynchronous probe can help supplement drivers. diff --git a/Documentation/driver-api/firmware/lookup-order.rst b/Documentation/driver-api/firmware/lookup-order.rst new file mode 100644 index 000000000000..88c81739683c --- /dev/null +++ b/Documentation/driver-api/firmware/lookup-order.rst @@ -0,0 +1,18 @@ +===================== +Firmware lookup order +===================== + +Different functionality is available to enable firmware to be found. +Below is chronological order of how firmware will be looked for once +a driver issues a firmware API call. + +* The ''Built-in firmware'' is checked first, if the firmware is present we + return it immediately +* The ''Firmware cache'' is looked at next. If the firmware is found we + return it immediately +* The ''Direct filesystem lookup'' is performed next, if found we + return it immediately +* If no firmware has been found and the fallback mechanism was enabled + the sysfs interface is created. After this either a kobject uevent + is issued or the custom firmware loading is relied upon for firmware + loading up to the timeout value. diff --git a/Documentation/driver-api/firmware/request_firmware.rst b/Documentation/driver-api/firmware/request_firmware.rst new file mode 100644 index 000000000000..cc0aea880824 --- /dev/null +++ b/Documentation/driver-api/firmware/request_firmware.rst @@ -0,0 +1,56 @@ +==================== +request_firmware API +==================== + +You would typically load firmware and then load it into your device somehow. +The typical firmware work flow is reflected below:: + + if(request_firmware(&fw_entry, $FIRMWARE, device) == 0) + copy_fw_to_device(fw_entry->data, fw_entry->size); + release_firmware(fw_entry); + +Synchronous firmware requests +============================= + +Synchronous firmware requests will wait until the firmware is found or until +an error is returned. + +request_firmware +---------------- +.. kernel-doc:: drivers/base/firmware_class.c + :functions: request_firmware + +request_firmware_direct +----------------------- +.. kernel-doc:: drivers/base/firmware_class.c + :functions: request_firmware_direct + +request_firmware_into_buf +------------------------- +.. kernel-doc:: drivers/base/firmware_class.c + :functions: request_firmware_into_buf + +Asynchronous firmware requests +============================== + +Asynchronous firmware requests allow driver code to not have to wait +until the firmware or an error is returned. Function callbacks are +provided so that when the firmware or an error is found the driver is +informed through the callback. request_firmware_nowait() cannot be called +in atomic contexts. + +request_firmware_nowait +----------------------- +.. kernel-doc:: drivers/base/firmware_class.c + :functions: request_firmware_nowait + +request firmware API expected driver use +======================================== + +Once an API call returns you process the firmware and then release the +firmware. For example if you used request_firmware() and it returns, +the driver has the firmware image accessible in fw_entry->{data,size}. +If something went wrong request_firmware() returns non-zero and fw_entry +is set to NULL. Once your driver is done with processing the firmware it +can call call release_firmware(fw_entry) to release the firmware image +and any related resource. |