xref: /linux/drivers/base/firmware_loader/firmware.h (revision 5d6d1ddd27301dc85b13b794262c8bcececf88f1)
1*5d6d1dddSLuis R. Rodriguez /* SPDX-License-Identifier: GPL-2.0 */
2*5d6d1dddSLuis R. Rodriguez #ifndef __FIRMWARE_LOADER_H
3*5d6d1dddSLuis R. Rodriguez #define __FIRMWARE_LOADER_H
4*5d6d1dddSLuis R. Rodriguez 
5*5d6d1dddSLuis R. Rodriguez #include <linux/firmware.h>
6*5d6d1dddSLuis R. Rodriguez #include <linux/types.h>
7*5d6d1dddSLuis R. Rodriguez #include <linux/kref.h>
8*5d6d1dddSLuis R. Rodriguez #include <linux/list.h>
9*5d6d1dddSLuis R. Rodriguez #include <linux/completion.h>
10*5d6d1dddSLuis R. Rodriguez 
11*5d6d1dddSLuis R. Rodriguez #include <generated/utsrelease.h>
12*5d6d1dddSLuis R. Rodriguez 
13*5d6d1dddSLuis R. Rodriguez /* firmware behavior options */
14*5d6d1dddSLuis R. Rodriguez #define FW_OPT_UEVENT			(1U << 0)
15*5d6d1dddSLuis R. Rodriguez #define FW_OPT_NOWAIT			(1U << 1)
16*5d6d1dddSLuis R. Rodriguez #define FW_OPT_USERHELPER		(1U << 2)
17*5d6d1dddSLuis R. Rodriguez #define FW_OPT_NO_WARN			(1U << 3)
18*5d6d1dddSLuis R. Rodriguez #define FW_OPT_NOCACHE			(1U << 4)
19*5d6d1dddSLuis R. Rodriguez #define FW_OPT_NOFALLBACK		(1U << 5)
20*5d6d1dddSLuis R. Rodriguez 
21*5d6d1dddSLuis R. Rodriguez enum fw_status {
22*5d6d1dddSLuis R. Rodriguez 	FW_STATUS_UNKNOWN,
23*5d6d1dddSLuis R. Rodriguez 	FW_STATUS_LOADING,
24*5d6d1dddSLuis R. Rodriguez 	FW_STATUS_DONE,
25*5d6d1dddSLuis R. Rodriguez 	FW_STATUS_ABORTED,
26*5d6d1dddSLuis R. Rodriguez };
27*5d6d1dddSLuis R. Rodriguez 
28*5d6d1dddSLuis R. Rodriguez /*
29*5d6d1dddSLuis R. Rodriguez  * Concurrent request_firmware() for the same firmware need to be
30*5d6d1dddSLuis R. Rodriguez  * serialized.  struct fw_state is simple state machine which hold the
31*5d6d1dddSLuis R. Rodriguez  * state of the firmware loading.
32*5d6d1dddSLuis R. Rodriguez  */
33*5d6d1dddSLuis R. Rodriguez struct fw_state {
34*5d6d1dddSLuis R. Rodriguez 	struct completion completion;
35*5d6d1dddSLuis R. Rodriguez 	enum fw_status status;
36*5d6d1dddSLuis R. Rodriguez };
37*5d6d1dddSLuis R. Rodriguez 
38*5d6d1dddSLuis R. Rodriguez struct fw_priv {
39*5d6d1dddSLuis R. Rodriguez 	struct kref ref;
40*5d6d1dddSLuis R. Rodriguez 	struct list_head list;
41*5d6d1dddSLuis R. Rodriguez 	struct firmware_cache *fwc;
42*5d6d1dddSLuis R. Rodriguez 	struct fw_state fw_st;
43*5d6d1dddSLuis R. Rodriguez 	void *data;
44*5d6d1dddSLuis R. Rodriguez 	size_t size;
45*5d6d1dddSLuis R. Rodriguez 	size_t allocated_size;
46*5d6d1dddSLuis R. Rodriguez #ifdef CONFIG_FW_LOADER_USER_HELPER
47*5d6d1dddSLuis R. Rodriguez 	bool is_paged_buf;
48*5d6d1dddSLuis R. Rodriguez 	bool need_uevent;
49*5d6d1dddSLuis R. Rodriguez 	struct page **pages;
50*5d6d1dddSLuis R. Rodriguez 	int nr_pages;
51*5d6d1dddSLuis R. Rodriguez 	int page_array_size;
52*5d6d1dddSLuis R. Rodriguez 	struct list_head pending_list;
53*5d6d1dddSLuis R. Rodriguez #endif
54*5d6d1dddSLuis R. Rodriguez 	const char *fw_name;
55*5d6d1dddSLuis R. Rodriguez };
56*5d6d1dddSLuis R. Rodriguez 
57*5d6d1dddSLuis R. Rodriguez extern struct mutex fw_lock;
58*5d6d1dddSLuis R. Rodriguez 
59*5d6d1dddSLuis R. Rodriguez static inline bool __fw_state_check(struct fw_priv *fw_priv,
60*5d6d1dddSLuis R. Rodriguez 				    enum fw_status status)
61*5d6d1dddSLuis R. Rodriguez {
62*5d6d1dddSLuis R. Rodriguez 	struct fw_state *fw_st = &fw_priv->fw_st;
63*5d6d1dddSLuis R. Rodriguez 
64*5d6d1dddSLuis R. Rodriguez 	return fw_st->status == status;
65*5d6d1dddSLuis R. Rodriguez }
66*5d6d1dddSLuis R. Rodriguez 
67*5d6d1dddSLuis R. Rodriguez static inline int __fw_state_wait_common(struct fw_priv *fw_priv, long timeout)
68*5d6d1dddSLuis R. Rodriguez {
69*5d6d1dddSLuis R. Rodriguez 	struct fw_state *fw_st = &fw_priv->fw_st;
70*5d6d1dddSLuis R. Rodriguez 	long ret;
71*5d6d1dddSLuis R. Rodriguez 
72*5d6d1dddSLuis R. Rodriguez 	ret = wait_for_completion_killable_timeout(&fw_st->completion, timeout);
73*5d6d1dddSLuis R. Rodriguez 	if (ret != 0 && fw_st->status == FW_STATUS_ABORTED)
74*5d6d1dddSLuis R. Rodriguez 		return -ENOENT;
75*5d6d1dddSLuis R. Rodriguez 	if (!ret)
76*5d6d1dddSLuis R. Rodriguez 		return -ETIMEDOUT;
77*5d6d1dddSLuis R. Rodriguez 
78*5d6d1dddSLuis R. Rodriguez 	return ret < 0 ? ret : 0;
79*5d6d1dddSLuis R. Rodriguez }
80*5d6d1dddSLuis R. Rodriguez 
81*5d6d1dddSLuis R. Rodriguez static inline void __fw_state_set(struct fw_priv *fw_priv,
82*5d6d1dddSLuis R. Rodriguez 				  enum fw_status status)
83*5d6d1dddSLuis R. Rodriguez {
84*5d6d1dddSLuis R. Rodriguez 	struct fw_state *fw_st = &fw_priv->fw_st;
85*5d6d1dddSLuis R. Rodriguez 
86*5d6d1dddSLuis R. Rodriguez 	WRITE_ONCE(fw_st->status, status);
87*5d6d1dddSLuis R. Rodriguez 
88*5d6d1dddSLuis R. Rodriguez 	if (status == FW_STATUS_DONE || status == FW_STATUS_ABORTED)
89*5d6d1dddSLuis R. Rodriguez 		complete_all(&fw_st->completion);
90*5d6d1dddSLuis R. Rodriguez }
91*5d6d1dddSLuis R. Rodriguez 
92*5d6d1dddSLuis R. Rodriguez static inline void fw_state_aborted(struct fw_priv *fw_priv)
93*5d6d1dddSLuis R. Rodriguez {
94*5d6d1dddSLuis R. Rodriguez 	__fw_state_set(fw_priv, FW_STATUS_ABORTED);
95*5d6d1dddSLuis R. Rodriguez }
96*5d6d1dddSLuis R. Rodriguez 
97*5d6d1dddSLuis R. Rodriguez static inline bool fw_state_is_aborted(struct fw_priv *fw_priv)
98*5d6d1dddSLuis R. Rodriguez {
99*5d6d1dddSLuis R. Rodriguez 	return __fw_state_check(fw_priv, FW_STATUS_ABORTED);
100*5d6d1dddSLuis R. Rodriguez }
101*5d6d1dddSLuis R. Rodriguez 
102*5d6d1dddSLuis R. Rodriguez static inline void fw_state_start(struct fw_priv *fw_priv)
103*5d6d1dddSLuis R. Rodriguez {
104*5d6d1dddSLuis R. Rodriguez 	__fw_state_set(fw_priv, FW_STATUS_LOADING);
105*5d6d1dddSLuis R. Rodriguez }
106*5d6d1dddSLuis R. Rodriguez 
107*5d6d1dddSLuis R. Rodriguez static inline void fw_state_done(struct fw_priv *fw_priv)
108*5d6d1dddSLuis R. Rodriguez {
109*5d6d1dddSLuis R. Rodriguez 	__fw_state_set(fw_priv, FW_STATUS_DONE);
110*5d6d1dddSLuis R. Rodriguez }
111*5d6d1dddSLuis R. Rodriguez 
112*5d6d1dddSLuis R. Rodriguez int assign_fw(struct firmware *fw, struct device *device,
113*5d6d1dddSLuis R. Rodriguez 	      unsigned int opt_flags);
114*5d6d1dddSLuis R. Rodriguez 
115*5d6d1dddSLuis R. Rodriguez #endif /* __FIRMWARE_LOADER_H */
116