15d6d1dddSLuis R. Rodriguez /* SPDX-License-Identifier: GPL-2.0 */ 25d6d1dddSLuis R. Rodriguez #ifndef __FIRMWARE_LOADER_H 35d6d1dddSLuis R. Rodriguez #define __FIRMWARE_LOADER_H 45d6d1dddSLuis R. Rodriguez 5eb33eb04SAndres Rodriguez #include <linux/bitops.h> 65d6d1dddSLuis R. Rodriguez #include <linux/firmware.h> 75d6d1dddSLuis R. Rodriguez #include <linux/types.h> 85d6d1dddSLuis R. Rodriguez #include <linux/kref.h> 95d6d1dddSLuis R. Rodriguez #include <linux/list.h> 105d6d1dddSLuis R. Rodriguez #include <linux/completion.h> 115d6d1dddSLuis R. Rodriguez 125d6d1dddSLuis R. Rodriguez #include <generated/utsrelease.h> 135d6d1dddSLuis R. Rodriguez 14eb33eb04SAndres Rodriguez /** 15eb33eb04SAndres Rodriguez * enum fw_opt - options to control firmware loading behaviour 16eb33eb04SAndres Rodriguez * 17eb33eb04SAndres Rodriguez * @FW_OPT_UEVENT: Enables the fallback mechanism to send a kobject uevent 18eb33eb04SAndres Rodriguez * when the firmware is not found. Userspace is in charge to load the 19eb33eb04SAndres Rodriguez * firmware using the sysfs loading facility. 20eb33eb04SAndres Rodriguez * @FW_OPT_NOWAIT: Used to describe the firmware request is asynchronous. 21eb33eb04SAndres Rodriguez * @FW_OPT_USERHELPER: Enable the fallback mechanism, in case the direct 22eb33eb04SAndres Rodriguez * filesystem lookup fails at finding the firmware. For details refer to 23cf1cde7cSAndres Rodriguez * firmware_fallback_sysfs(). 24eb33eb04SAndres Rodriguez * @FW_OPT_NO_WARN: Quiet, avoid printing warning messages. 25eb33eb04SAndres Rodriguez * @FW_OPT_NOCACHE: Disables firmware caching. Firmware caching is used to 26eb33eb04SAndres Rodriguez * cache the firmware upon suspend, so that upon resume races against the 27eb33eb04SAndres Rodriguez * firmware file lookup on storage is avoided. Used for calls where the 28eb33eb04SAndres Rodriguez * file may be too big, or where the driver takes charge of its own 29eb33eb04SAndres Rodriguez * firmware caching mechanism. 3085db1cdeSHans de Goede * @FW_OPT_NOFALLBACK_SYSFS: Disable the sysfs fallback mechanism. Takes 3185db1cdeSHans de Goede * precedence over &FW_OPT_UEVENT and &FW_OPT_USERHELPER. 32e4c2c0ffSHans de Goede * @FW_OPT_FALLBACK_PLATFORM: Enable fallback to device fw copy embedded in 33e4c2c0ffSHans de Goede * the platform's main firmware. If both this fallback and the sysfs 34e4c2c0ffSHans de Goede * fallback are enabled, then this fallback will be tried first. 3559cdb23cSScott Branden * @FW_OPT_PARTIAL: Allow partial read of firmware instead of needing to read 3659cdb23cSScott Branden * entire file. 37eb33eb04SAndres Rodriguez */ 38eb33eb04SAndres Rodriguez enum fw_opt { 39eb33eb04SAndres Rodriguez FW_OPT_UEVENT = BIT(0), 40eb33eb04SAndres Rodriguez FW_OPT_NOWAIT = BIT(1), 41eb33eb04SAndres Rodriguez FW_OPT_USERHELPER = BIT(2), 42eb33eb04SAndres Rodriguez FW_OPT_NO_WARN = BIT(3), 43eb33eb04SAndres Rodriguez FW_OPT_NOCACHE = BIT(4), 4485db1cdeSHans de Goede FW_OPT_NOFALLBACK_SYSFS = BIT(5), 45e4c2c0ffSHans de Goede FW_OPT_FALLBACK_PLATFORM = BIT(6), 4659cdb23cSScott Branden FW_OPT_PARTIAL = BIT(7), 47eb33eb04SAndres Rodriguez }; 485d6d1dddSLuis R. Rodriguez 495d6d1dddSLuis R. Rodriguez enum fw_status { 505d6d1dddSLuis R. Rodriguez FW_STATUS_UNKNOWN, 515d6d1dddSLuis R. Rodriguez FW_STATUS_LOADING, 525d6d1dddSLuis R. Rodriguez FW_STATUS_DONE, 535d6d1dddSLuis R. Rodriguez FW_STATUS_ABORTED, 545d6d1dddSLuis R. Rodriguez }; 555d6d1dddSLuis R. Rodriguez 565d6d1dddSLuis R. Rodriguez /* 575d6d1dddSLuis R. Rodriguez * Concurrent request_firmware() for the same firmware need to be 585d6d1dddSLuis R. Rodriguez * serialized. struct fw_state is simple state machine which hold the 595d6d1dddSLuis R. Rodriguez * state of the firmware loading. 605d6d1dddSLuis R. Rodriguez */ 615d6d1dddSLuis R. Rodriguez struct fw_state { 625d6d1dddSLuis R. Rodriguez struct completion completion; 635d6d1dddSLuis R. Rodriguez enum fw_status status; 645d6d1dddSLuis R. Rodriguez }; 655d6d1dddSLuis R. Rodriguez 665d6d1dddSLuis R. Rodriguez struct fw_priv { 675d6d1dddSLuis R. Rodriguez struct kref ref; 685d6d1dddSLuis R. Rodriguez struct list_head list; 695d6d1dddSLuis R. Rodriguez struct firmware_cache *fwc; 705d6d1dddSLuis R. Rodriguez struct fw_state fw_st; 715d6d1dddSLuis R. Rodriguez void *data; 725d6d1dddSLuis R. Rodriguez size_t size; 735d6d1dddSLuis R. Rodriguez size_t allocated_size; 7459cdb23cSScott Branden size_t offset; 7589287c16SKees Cook u32 opt_flags; 7682fd7a81STakashi Iwai #ifdef CONFIG_FW_LOADER_PAGED_BUF 775d6d1dddSLuis R. Rodriguez bool is_paged_buf; 785d6d1dddSLuis R. Rodriguez struct page **pages; 795d6d1dddSLuis R. Rodriguez int nr_pages; 805d6d1dddSLuis R. Rodriguez int page_array_size; 8182fd7a81STakashi Iwai #endif 8282fd7a81STakashi Iwai #ifdef CONFIG_FW_LOADER_USER_HELPER 8382fd7a81STakashi Iwai bool need_uevent; 845d6d1dddSLuis R. Rodriguez struct list_head pending_list; 855d6d1dddSLuis R. Rodriguez #endif 865d6d1dddSLuis R. Rodriguez const char *fw_name; 875d6d1dddSLuis R. Rodriguez }; 885d6d1dddSLuis R. Rodriguez 895d6d1dddSLuis R. Rodriguez extern struct mutex fw_lock; 90*97730bbbSRuss Weight extern struct firmware_cache fw_cache; 915d6d1dddSLuis R. Rodriguez 925d6d1dddSLuis R. Rodriguez static inline bool __fw_state_check(struct fw_priv *fw_priv, 935d6d1dddSLuis R. Rodriguez enum fw_status status) 945d6d1dddSLuis R. Rodriguez { 955d6d1dddSLuis R. Rodriguez struct fw_state *fw_st = &fw_priv->fw_st; 965d6d1dddSLuis R. Rodriguez 975d6d1dddSLuis R. Rodriguez return fw_st->status == status; 985d6d1dddSLuis R. Rodriguez } 995d6d1dddSLuis R. Rodriguez 1005d6d1dddSLuis R. Rodriguez static inline int __fw_state_wait_common(struct fw_priv *fw_priv, long timeout) 1015d6d1dddSLuis R. Rodriguez { 1025d6d1dddSLuis R. Rodriguez struct fw_state *fw_st = &fw_priv->fw_st; 1035d6d1dddSLuis R. Rodriguez long ret; 1045d6d1dddSLuis R. Rodriguez 1055d6d1dddSLuis R. Rodriguez ret = wait_for_completion_killable_timeout(&fw_st->completion, timeout); 1065d6d1dddSLuis R. Rodriguez if (ret != 0 && fw_st->status == FW_STATUS_ABORTED) 1075d6d1dddSLuis R. Rodriguez return -ENOENT; 1085d6d1dddSLuis R. Rodriguez if (!ret) 1095d6d1dddSLuis R. Rodriguez return -ETIMEDOUT; 1105d6d1dddSLuis R. Rodriguez 1115d6d1dddSLuis R. Rodriguez return ret < 0 ? ret : 0; 1125d6d1dddSLuis R. Rodriguez } 1135d6d1dddSLuis R. Rodriguez 1145d6d1dddSLuis R. Rodriguez static inline void __fw_state_set(struct fw_priv *fw_priv, 1155d6d1dddSLuis R. Rodriguez enum fw_status status) 1165d6d1dddSLuis R. Rodriguez { 1175d6d1dddSLuis R. Rodriguez struct fw_state *fw_st = &fw_priv->fw_st; 1185d6d1dddSLuis R. Rodriguez 1195d6d1dddSLuis R. Rodriguez WRITE_ONCE(fw_st->status, status); 1205d6d1dddSLuis R. Rodriguez 12175d95e2eSAnirudh Rayabharam if (status == FW_STATUS_DONE || status == FW_STATUS_ABORTED) { 12275d95e2eSAnirudh Rayabharam #ifdef CONFIG_FW_LOADER_USER_HELPER 12375d95e2eSAnirudh Rayabharam /* 12475d95e2eSAnirudh Rayabharam * Doing this here ensures that the fw_priv is deleted from 12575d95e2eSAnirudh Rayabharam * the pending list in all abort/done paths. 12675d95e2eSAnirudh Rayabharam */ 12775d95e2eSAnirudh Rayabharam list_del_init(&fw_priv->pending_list); 12875d95e2eSAnirudh Rayabharam #endif 1295d6d1dddSLuis R. Rodriguez complete_all(&fw_st->completion); 1305d6d1dddSLuis R. Rodriguez } 13175d95e2eSAnirudh Rayabharam } 1325d6d1dddSLuis R. Rodriguez 1335d6d1dddSLuis R. Rodriguez static inline void fw_state_aborted(struct fw_priv *fw_priv) 1345d6d1dddSLuis R. Rodriguez { 1355d6d1dddSLuis R. Rodriguez __fw_state_set(fw_priv, FW_STATUS_ABORTED); 1365d6d1dddSLuis R. Rodriguez } 1375d6d1dddSLuis R. Rodriguez 1385d6d1dddSLuis R. Rodriguez static inline bool fw_state_is_aborted(struct fw_priv *fw_priv) 1395d6d1dddSLuis R. Rodriguez { 1405d6d1dddSLuis R. Rodriguez return __fw_state_check(fw_priv, FW_STATUS_ABORTED); 1415d6d1dddSLuis R. Rodriguez } 1425d6d1dddSLuis R. Rodriguez 1435d6d1dddSLuis R. Rodriguez static inline void fw_state_start(struct fw_priv *fw_priv) 1445d6d1dddSLuis R. Rodriguez { 1455d6d1dddSLuis R. Rodriguez __fw_state_set(fw_priv, FW_STATUS_LOADING); 1465d6d1dddSLuis R. Rodriguez } 1475d6d1dddSLuis R. Rodriguez 1485d6d1dddSLuis R. Rodriguez static inline void fw_state_done(struct fw_priv *fw_priv) 1495d6d1dddSLuis R. Rodriguez { 1505d6d1dddSLuis R. Rodriguez __fw_state_set(fw_priv, FW_STATUS_DONE); 1515d6d1dddSLuis R. Rodriguez } 1525d6d1dddSLuis R. Rodriguez 153736da0b6SRuss Weight static inline bool fw_state_is_done(struct fw_priv *fw_priv) 154736da0b6SRuss Weight { 155736da0b6SRuss Weight return __fw_state_check(fw_priv, FW_STATUS_DONE); 156736da0b6SRuss Weight } 157736da0b6SRuss Weight 158736da0b6SRuss Weight static inline bool fw_state_is_loading(struct fw_priv *fw_priv) 159736da0b6SRuss Weight { 160736da0b6SRuss Weight return __fw_state_check(fw_priv, FW_STATUS_LOADING); 161736da0b6SRuss Weight } 162736da0b6SRuss Weight 163*97730bbbSRuss Weight int alloc_lookup_fw_priv(const char *fw_name, struct firmware_cache *fwc, 164*97730bbbSRuss Weight struct fw_priv **fw_priv, void *dbuf, size_t size, 165*97730bbbSRuss Weight size_t offset, u32 opt_flags); 16689287c16SKees Cook int assign_fw(struct firmware *fw, struct device *device); 167*97730bbbSRuss Weight void free_fw_priv(struct fw_priv *fw_priv); 168*97730bbbSRuss Weight void fw_state_init(struct fw_priv *fw_priv); 1695d6d1dddSLuis R. Rodriguez 17048d09e97SLuis Chamberlain #ifdef CONFIG_FW_LOADER 17148d09e97SLuis Chamberlain bool firmware_is_builtin(const struct firmware *fw); 17248d09e97SLuis Chamberlain bool firmware_request_builtin_buf(struct firmware *fw, const char *name, 17348d09e97SLuis Chamberlain void *buf, size_t size); 17448d09e97SLuis Chamberlain #else /* module case */ 17548d09e97SLuis Chamberlain static inline bool firmware_is_builtin(const struct firmware *fw) 17648d09e97SLuis Chamberlain { 17748d09e97SLuis Chamberlain return false; 17848d09e97SLuis Chamberlain } 17948d09e97SLuis Chamberlain static inline bool firmware_request_builtin_buf(struct firmware *fw, 18048d09e97SLuis Chamberlain const char *name, 18148d09e97SLuis Chamberlain void *buf, size_t size) 18248d09e97SLuis Chamberlain { 18348d09e97SLuis Chamberlain return false; 18448d09e97SLuis Chamberlain } 18548d09e97SLuis Chamberlain #endif 18648d09e97SLuis Chamberlain 18782fd7a81STakashi Iwai #ifdef CONFIG_FW_LOADER_PAGED_BUF 1888f58570bSTakashi Iwai void fw_free_paged_buf(struct fw_priv *fw_priv); 1895342e709STakashi Iwai int fw_grow_paged_buf(struct fw_priv *fw_priv, int pages_needed); 1905342e709STakashi Iwai int fw_map_paged_buf(struct fw_priv *fw_priv); 1914965b8cdSPrateek Sood bool fw_is_paged_buf(struct fw_priv *fw_priv); 1928f58570bSTakashi Iwai #else 1938f58570bSTakashi Iwai static inline void fw_free_paged_buf(struct fw_priv *fw_priv) {} 194333a2101STakashi Iwai static inline int fw_grow_paged_buf(struct fw_priv *fw_priv, int pages_needed) { return -ENXIO; } 195333a2101STakashi Iwai static inline int fw_map_paged_buf(struct fw_priv *fw_priv) { return -ENXIO; } 1964965b8cdSPrateek Sood static inline bool fw_is_paged_buf(struct fw_priv *fw_priv) { return false; } 1978f58570bSTakashi Iwai #endif 1988f58570bSTakashi Iwai 1995d6d1dddSLuis R. Rodriguez #endif /* __FIRMWARE_LOADER_H */ 200