Merge branch 'upstream' of git://git.linux-mips.org/pub/scm/ralf/upstream-linus
[deliverable/linux.git] / drivers / net / wireless / libertas / firmware.c
CommitLineData
370803c2
DD
1/*
2 * Firmware loading and handling functions.
3 */
4
138a53ef 5#include <linux/sched.h>
534111c7 6#include <linux/firmware.h>
370803c2
DD
7#include <linux/firmware.h>
8#include <linux/module.h>
7608f165 9#include <linux/sched.h>
370803c2 10
534111c7 11#include "dev.h"
370803c2
DD
12#include "decl.h"
13
534111c7
DD
14static void load_next_firmware_from_table(struct lbs_private *private);
15
16static void lbs_fw_loaded(struct lbs_private *priv, int ret,
17 const struct firmware *helper, const struct firmware *mainfw)
18{
19 unsigned long flags;
20
21 lbs_deb_fw("firmware load complete, code %d\n", ret);
22
23 /* User must free helper/mainfw */
24 priv->fw_callback(priv, ret, helper, mainfw);
25
26 spin_lock_irqsave(&priv->driver_lock, flags);
27 priv->fw_callback = NULL;
28 wake_up(&priv->fw_waitq);
29 spin_unlock_irqrestore(&priv->driver_lock, flags);
30}
31
32static void do_load_firmware(struct lbs_private *priv, const char *name,
33 void (*cb)(const struct firmware *fw, void *context))
34{
35 int ret;
36
37 lbs_deb_fw("Requesting %s\n", name);
38 ret = request_firmware_nowait(THIS_MODULE, true, name,
39 priv->fw_device, GFP_KERNEL, priv, cb);
40 if (ret) {
41 lbs_deb_fw("request_firmware_nowait error %d\n", ret);
42 lbs_fw_loaded(priv, ret, NULL, NULL);
43 }
44}
45
46static void main_firmware_cb(const struct firmware *firmware, void *context)
47{
48 struct lbs_private *priv = context;
49
50 if (!firmware) {
51 /* Failed to find firmware: try next table entry */
52 load_next_firmware_from_table(priv);
53 return;
54 }
55
56 /* Firmware found! */
57 lbs_fw_loaded(priv, 0, priv->helper_fw, firmware);
58}
59
60static void helper_firmware_cb(const struct firmware *firmware, void *context)
61{
62 struct lbs_private *priv = context;
63
64 if (!firmware) {
65 /* Failed to find firmware: try next table entry */
66 load_next_firmware_from_table(priv);
67 return;
68 }
69
70 /* Firmware found! */
71 if (priv->fw_iter->fwname) {
72 priv->helper_fw = firmware;
73 do_load_firmware(priv, priv->fw_iter->fwname, main_firmware_cb);
74 } else {
75 /* No main firmware needed for this helper --> success! */
76 lbs_fw_loaded(priv, 0, firmware, NULL);
77 }
78}
79
80static void load_next_firmware_from_table(struct lbs_private *priv)
81{
82 const struct lbs_fw_table *iter;
83
84 if (!priv->fw_iter)
85 iter = priv->fw_table;
86 else
87 iter = ++priv->fw_iter;
88
89 if (priv->helper_fw) {
90 release_firmware(priv->helper_fw);
91 priv->helper_fw = NULL;
92 }
93
94next:
95 if (!iter->helper) {
96 /* End of table hit. */
97 lbs_fw_loaded(priv, -ENOENT, NULL, NULL);
98 return;
99 }
100
101 if (iter->model != priv->fw_model) {
102 iter++;
103 goto next;
104 }
105
106 priv->fw_iter = iter;
107 do_load_firmware(priv, iter->helper, helper_firmware_cb);
108}
109
110void lbs_wait_for_firmware_load(struct lbs_private *priv)
111{
112 wait_event(priv->fw_waitq, priv->fw_callback == NULL);
113}
114
115/**
116 * lbs_get_firmware_async - Retrieves firmware asynchronously. Can load
117 * either a helper firmware and a main firmware (2-stage), or just the helper.
118 *
119 * @priv: Pointer to lbs_private instance
120 * @dev: A pointer to &device structure
121 * @card_model: Bus-specific card model ID used to filter firmware table
122 * elements
123 * @fw_table: Table of firmware file names and device model numbers
124 * terminated by an entry with a NULL helper name
125 * @callback: User callback to invoke when firmware load succeeds or fails.
126 */
127int lbs_get_firmware_async(struct lbs_private *priv, struct device *device,
128 u32 card_model, const struct lbs_fw_table *fw_table,
129 lbs_fw_cb callback)
130{
131 unsigned long flags;
132
133 spin_lock_irqsave(&priv->driver_lock, flags);
134 if (priv->fw_callback) {
135 lbs_deb_fw("firmware load already in progress\n");
136 spin_unlock_irqrestore(&priv->driver_lock, flags);
137 return -EBUSY;
138 }
139
140 priv->fw_device = device;
141 priv->fw_callback = callback;
142 priv->fw_table = fw_table;
143 priv->fw_iter = NULL;
144 priv->fw_model = card_model;
145 spin_unlock_irqrestore(&priv->driver_lock, flags);
146
147 lbs_deb_fw("Starting async firmware load\n");
148 load_next_firmware_from_table(priv);
149 return 0;
150}
151EXPORT_SYMBOL_GPL(lbs_get_firmware_async);
152
370803c2
DD
153/**
154 * lbs_get_firmware - Retrieves two-stage firmware
155 *
156 * @dev: A pointer to &device structure
157 * @card_model: Bus-specific card model ID used to filter firmware table
158 * elements
159 * @fw_table: Table of firmware file names and device model numbers
160 * terminated by an entry with a NULL helper name
161 * @helper: On success, the helper firmware; caller must free
162 * @mainfw: On success, the main firmware; caller must free
163 *
534111c7
DD
164 * Deprecated: use lbs_get_firmware_async() instead.
165 *
370803c2
DD
166 * returns: 0 on success, non-zero on failure
167 */
168int lbs_get_firmware(struct device *dev, u32 card_model,
169 const struct lbs_fw_table *fw_table,
170 const struct firmware **helper,
171 const struct firmware **mainfw)
172{
173 const struct lbs_fw_table *iter;
174 int ret;
175
176 BUG_ON(helper == NULL);
177 BUG_ON(mainfw == NULL);
178
179 /* Search for firmware to use from the table. */
180 iter = fw_table;
181 while (iter && iter->helper) {
182 if (iter->model != card_model)
183 goto next;
184
185 if (*helper == NULL) {
186 ret = request_firmware(helper, iter->helper, dev);
187 if (ret)
188 goto next;
189
190 /* If the device has one-stage firmware (ie cf8305) and
191 * we've got it then we don't need to bother with the
192 * main firmware.
193 */
194 if (iter->fwname == NULL)
195 return 0;
196 }
197
198 if (*mainfw == NULL) {
199 ret = request_firmware(mainfw, iter->fwname, dev);
200 if (ret) {
201 /* Clear the helper to ensure we don't have
202 * mismatched firmware pairs.
203 */
204 release_firmware(*helper);
205 *helper = NULL;
206 }
207 }
208
209 if (*helper && *mainfw)
210 return 0;
211
212 next:
213 iter++;
214 }
215
216 /* Failed */
217 release_firmware(*helper);
218 *helper = NULL;
219 release_firmware(*mainfw);
220 *mainfw = NULL;
221
222 return -ENOENT;
223}
224EXPORT_SYMBOL_GPL(lbs_get_firmware);
This page took 0.049494 seconds and 5 git commands to generate.