| /* SPDX-License-Identifier: GPL-2.0 */ | |
| #ifndef __FIRMWARE_LOADER_H | |
| #define __FIRMWARE_LOADER_H | |
| #include <linux/bitops.h> | |
| #include <linux/firmware.h> | |
| #include <linux/types.h> | |
| #include <linux/kref.h> | |
| #include <linux/list.h> | |
| #include <linux/completion.h> | |
| #include <generated/utsrelease.h> | |
| /** | |
| * enum fw_opt - options to control firmware loading behaviour | |
| * | |
| * @FW_OPT_UEVENT: Enables the fallback mechanism to send a kobject uevent | |
| * when the firmware is not found. Userspace is in charge to load the | |
| * firmware using the sysfs loading facility. | |
| * @FW_OPT_NOWAIT: Used to describe the firmware request is asynchronous. | |
| * @FW_OPT_USERHELPER: Enable the fallback mechanism, in case the direct | |
| * filesystem lookup fails at finding the firmware. For details refer to | |
| * firmware_fallback_sysfs(). | |
| * @FW_OPT_NO_WARN: Quiet, avoid printing warning messages. | |
| * @FW_OPT_NOCACHE: Disables firmware caching. Firmware caching is used to | |
| * cache the firmware upon suspend, so that upon resume races against the | |
| * firmware file lookup on storage is avoided. Used for calls where the | |
| * file may be too big, or where the driver takes charge of its own | |
| * firmware caching mechanism. | |
| * @FW_OPT_NOFALLBACK: Disable the fallback mechanism. Takes precedence over | |
| * &FW_OPT_UEVENT and &FW_OPT_USERHELPER. | |
| */ | |
| enum fw_opt { | |
| FW_OPT_UEVENT = BIT(0), | |
| FW_OPT_NOWAIT = BIT(1), | |
| FW_OPT_USERHELPER = BIT(2), | |
| FW_OPT_NO_WARN = BIT(3), | |
| FW_OPT_NOCACHE = BIT(4), | |
| FW_OPT_NOFALLBACK = BIT(5), | |
| }; | |
| enum fw_status { | |
| FW_STATUS_UNKNOWN, | |
| FW_STATUS_LOADING, | |
| FW_STATUS_DONE, | |
| FW_STATUS_ABORTED, | |
| }; | |
| /* | |
| * Concurrent request_firmware() for the same firmware need to be | |
| * serialized. struct fw_state is simple state machine which hold the | |
| * state of the firmware loading. | |
| */ | |
| struct fw_state { | |
| struct completion completion; | |
| enum fw_status status; | |
| }; | |
| struct fw_priv { | |
| struct kref ref; | |
| struct list_head list; | |
| struct firmware_cache *fwc; | |
| struct fw_state fw_st; | |
| void *data; | |
| size_t size; | |
| size_t allocated_size; | |
| #ifdef CONFIG_FW_LOADER_USER_HELPER | |
| bool is_paged_buf; | |
| bool need_uevent; | |
| struct page **pages; | |
| int nr_pages; | |
| int page_array_size; | |
| struct list_head pending_list; | |
| #endif | |
| const char *fw_name; | |
| }; | |
| extern struct mutex fw_lock; | |
| static inline bool __fw_state_check(struct fw_priv *fw_priv, | |
| enum fw_status status) | |
| { | |
| struct fw_state *fw_st = &fw_priv->fw_st; | |
| return fw_st->status == status; | |
| } | |
| static inline int __fw_state_wait_common(struct fw_priv *fw_priv, long timeout) | |
| { | |
| struct fw_state *fw_st = &fw_priv->fw_st; | |
| long ret; | |
| ret = wait_for_completion_killable_timeout(&fw_st->completion, timeout); | |
| if (ret != 0 && fw_st->status == FW_STATUS_ABORTED) | |
| return -ENOENT; | |
| if (!ret) | |
| return -ETIMEDOUT; | |
| return ret < 0 ? ret : 0; | |
| } | |
| static inline void __fw_state_set(struct fw_priv *fw_priv, | |
| enum fw_status status) | |
| { | |
| struct fw_state *fw_st = &fw_priv->fw_st; | |
| WRITE_ONCE(fw_st->status, status); | |
| if (status == FW_STATUS_DONE || status == FW_STATUS_ABORTED) | |
| complete_all(&fw_st->completion); | |
| } | |
| static inline void fw_state_aborted(struct fw_priv *fw_priv) | |
| { | |
| __fw_state_set(fw_priv, FW_STATUS_ABORTED); | |
| } | |
| static inline bool fw_state_is_aborted(struct fw_priv *fw_priv) | |
| { | |
| return __fw_state_check(fw_priv, FW_STATUS_ABORTED); | |
| } | |
| static inline void fw_state_start(struct fw_priv *fw_priv) | |
| { | |
| __fw_state_set(fw_priv, FW_STATUS_LOADING); | |
| } | |
| static inline void fw_state_done(struct fw_priv *fw_priv) | |
| { | |
| __fw_state_set(fw_priv, FW_STATUS_DONE); | |
| } | |
| int assign_fw(struct firmware *fw, struct device *device, | |
| enum fw_opt opt_flags); | |
| #endif /* __FIRMWARE_LOADER_H */ |