Commit | Line | Data |
---|---|---|
d73f821c LR |
1 | /* SPDX-License-Identifier: GPL-2.0 */ |
2 | #ifndef __FIRMWARE_LOADER_H | |
3 | #define __FIRMWARE_LOADER_H | |
4 | ||
eb33eb04 | 5 | #include <linux/bitops.h> |
d73f821c LR |
6 | #include <linux/firmware.h> |
7 | #include <linux/types.h> | |
8 | #include <linux/kref.h> | |
9 | #include <linux/list.h> | |
10 | #include <linux/completion.h> | |
11 | ||
12 | #include <generated/utsrelease.h> | |
13 | ||
eb33eb04 AR |
14 | /** |
15 | * enum fw_opt - options to control firmware loading behaviour | |
16 | * | |
17 | * @FW_OPT_UEVENT: Enables the fallback mechanism to send a kobject uevent | |
18 | * when the firmware is not found. Userspace is in charge to load the | |
19 | * firmware using the sysfs loading facility. | |
20 | * @FW_OPT_NOWAIT: Used to describe the firmware request is asynchronous. | |
21 | * @FW_OPT_USERHELPER: Enable the fallback mechanism, in case the direct | |
22 | * filesystem lookup fails at finding the firmware. For details refer to | |
cf1cde7c | 23 | * firmware_fallback_sysfs(). |
eb33eb04 AR |
24 | * @FW_OPT_NO_WARN: Quiet, avoid printing warning messages. |
25 | * @FW_OPT_NOCACHE: Disables firmware caching. Firmware caching is used to | |
26 | * cache the firmware upon suspend, so that upon resume races against the | |
27 | * firmware file lookup on storage is avoided. Used for calls where the | |
28 | * file may be too big, or where the driver takes charge of its own | |
29 | * firmware caching mechanism. | |
30 | * @FW_OPT_NOFALLBACK: Disable the fallback mechanism. Takes precedence over | |
31 | * &FW_OPT_UEVENT and &FW_OPT_USERHELPER. | |
32 | */ | |
33 | enum fw_opt { | |
34 | FW_OPT_UEVENT = BIT(0), | |
35 | FW_OPT_NOWAIT = BIT(1), | |
36 | FW_OPT_USERHELPER = BIT(2), | |
37 | FW_OPT_NO_WARN = BIT(3), | |
38 | FW_OPT_NOCACHE = BIT(4), | |
39 | FW_OPT_NOFALLBACK = BIT(5), | |
40 | }; | |
d73f821c LR |
41 | |
42 | enum fw_status { | |
43 | FW_STATUS_UNKNOWN, | |
44 | FW_STATUS_LOADING, | |
45 | FW_STATUS_DONE, | |
46 | FW_STATUS_ABORTED, | |
47 | }; | |
48 | ||
49 | /* | |
50 | * Concurrent request_firmware() for the same firmware need to be | |
51 | * serialized. struct fw_state is simple state machine which hold the | |
52 | * state of the firmware loading. | |
53 | */ | |
54 | struct fw_state { | |
55 | struct completion completion; | |
56 | enum fw_status status; | |
57 | }; | |
58 | ||
59 | struct fw_priv { | |
60 | struct kref ref; | |
61 | struct list_head list; | |
62 | struct firmware_cache *fwc; | |
63 | struct fw_state fw_st; | |
64 | void *data; | |
65 | size_t size; | |
66 | size_t allocated_size; | |
67 | #ifdef CONFIG_FW_LOADER_USER_HELPER | |
68 | bool is_paged_buf; | |
69 | bool need_uevent; | |
70 | struct page **pages; | |
71 | int nr_pages; | |
72 | int page_array_size; | |
73 | struct list_head pending_list; | |
74 | #endif | |
75 | const char *fw_name; | |
76 | }; | |
77 | ||
78 | extern struct mutex fw_lock; | |
79 | ||
80 | static inline bool __fw_state_check(struct fw_priv *fw_priv, | |
81 | enum fw_status status) | |
82 | { | |
83 | struct fw_state *fw_st = &fw_priv->fw_st; | |
84 | ||
85 | return fw_st->status == status; | |
86 | } | |
87 | ||
88 | static inline int __fw_state_wait_common(struct fw_priv *fw_priv, long timeout) | |
89 | { | |
90 | struct fw_state *fw_st = &fw_priv->fw_st; | |
91 | long ret; | |
92 | ||
93 | ret = wait_for_completion_killable_timeout(&fw_st->completion, timeout); | |
94 | if (ret != 0 && fw_st->status == FW_STATUS_ABORTED) | |
95 | return -ENOENT; | |
96 | if (!ret) | |
97 | return -ETIMEDOUT; | |
98 | ||
99 | return ret < 0 ? ret : 0; | |
100 | } | |
101 | ||
102 | static inline void __fw_state_set(struct fw_priv *fw_priv, | |
103 | enum fw_status status) | |
104 | { | |
105 | struct fw_state *fw_st = &fw_priv->fw_st; | |
106 | ||
107 | WRITE_ONCE(fw_st->status, status); | |
108 | ||
109 | if (status == FW_STATUS_DONE || status == FW_STATUS_ABORTED) | |
110 | complete_all(&fw_st->completion); | |
111 | } | |
112 | ||
113 | static inline void fw_state_aborted(struct fw_priv *fw_priv) | |
114 | { | |
115 | __fw_state_set(fw_priv, FW_STATUS_ABORTED); | |
116 | } | |
117 | ||
118 | static inline bool fw_state_is_aborted(struct fw_priv *fw_priv) | |
119 | { | |
120 | return __fw_state_check(fw_priv, FW_STATUS_ABORTED); | |
121 | } | |
122 | ||
123 | static inline void fw_state_start(struct fw_priv *fw_priv) | |
124 | { | |
125 | __fw_state_set(fw_priv, FW_STATUS_LOADING); | |
126 | } | |
127 | ||
128 | static inline void fw_state_done(struct fw_priv *fw_priv) | |
129 | { | |
130 | __fw_state_set(fw_priv, FW_STATUS_DONE); | |
131 | } | |
132 | ||
133 | int assign_fw(struct firmware *fw, struct device *device, | |
eb33eb04 | 134 | enum fw_opt opt_flags); |
d73f821c | 135 | |
8f58570b TI |
136 | #ifdef CONFIG_FW_LOADER_USER_HELPER |
137 | void fw_free_paged_buf(struct fw_priv *fw_priv); | |
138 | #else | |
139 | static inline void fw_free_paged_buf(struct fw_priv *fw_priv) {} | |
140 | #endif | |
141 | ||
d73f821c | 142 | #endif /* __FIRMWARE_LOADER_H */ |