diff options
author | Daniel Drake <dsd@laptop.org> | 2012-04-16 18:53:26 -0400 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2012-04-17 14:57:14 -0400 |
commit | 534111c78c59a8db89c570fd07489243dc366a05 (patch) | |
tree | 27b68e1c69f6f872b62ecfce026591d57904605a /drivers/net/wireless/libertas | |
parent | 0beecac8abb3af890d470df541142d55343382d6 (diff) |
libertas: add asynchronous firmware loading capability
As described at
http://article.gmane.org/gmane.linux.kernel.wireless.general/86084
libertas is taking a long time to load because it loads firmware
during module loading.
Add a new API for interface drivers to load their firmware
asynchronously. The same semantics of the firmware table are followed
like before.
Interface drivers will be converted in follow-up patches, then we can
remove the old, synchronous firmware loading function.
Signed-off-by: Daniel Drake <dsd@laptop.org>
Acked-by: Dan Williams <dcbw@redhat.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers/net/wireless/libertas')
-rw-r--r-- | drivers/net/wireless/libertas/decl.h | 8 | ||||
-rw-r--r-- | drivers/net/wireless/libertas/dev.h | 10 | ||||
-rw-r--r-- | drivers/net/wireless/libertas/firmware.c | 143 | ||||
-rw-r--r-- | drivers/net/wireless/libertas/main.c | 3 |
4 files changed, 164 insertions, 0 deletions
diff --git a/drivers/net/wireless/libertas/decl.h b/drivers/net/wireless/libertas/decl.h index 2fb2e31733ee..84a3aa7ac570 100644 --- a/drivers/net/wireless/libertas/decl.h +++ b/drivers/net/wireless/libertas/decl.h | |||
@@ -19,6 +19,10 @@ struct lbs_fw_table { | |||
19 | }; | 19 | }; |
20 | 20 | ||
21 | struct lbs_private; | 21 | struct lbs_private; |
22 | typedef void (*lbs_fw_cb)(struct lbs_private *priv, int ret, | ||
23 | const struct firmware *helper, const struct firmware *mainfw); | ||
24 | |||
25 | struct lbs_private; | ||
22 | struct sk_buff; | 26 | struct sk_buff; |
23 | struct net_device; | 27 | struct net_device; |
24 | struct cmd_ds_command; | 28 | struct cmd_ds_command; |
@@ -70,5 +74,9 @@ int lbs_get_firmware(struct device *dev, u32 card_model, | |||
70 | const struct lbs_fw_table *fw_table, | 74 | const struct lbs_fw_table *fw_table, |
71 | const struct firmware **helper, | 75 | const struct firmware **helper, |
72 | const struct firmware **mainfw); | 76 | const struct firmware **mainfw); |
77 | int lbs_get_firmware_async(struct lbs_private *priv, struct device *device, | ||
78 | u32 card_model, const struct lbs_fw_table *fw_table, | ||
79 | lbs_fw_cb callback); | ||
80 | void lbs_wait_for_firmware_load(struct lbs_private *priv); | ||
73 | 81 | ||
74 | #endif | 82 | #endif |
diff --git a/drivers/net/wireless/libertas/dev.h b/drivers/net/wireless/libertas/dev.h index f3fd447131c2..672005430aca 100644 --- a/drivers/net/wireless/libertas/dev.h +++ b/drivers/net/wireless/libertas/dev.h | |||
@@ -7,6 +7,7 @@ | |||
7 | #define _LBS_DEV_H_ | 7 | #define _LBS_DEV_H_ |
8 | 8 | ||
9 | #include "defs.h" | 9 | #include "defs.h" |
10 | #include "decl.h" | ||
10 | #include "host.h" | 11 | #include "host.h" |
11 | 12 | ||
12 | #include <linux/kfifo.h> | 13 | #include <linux/kfifo.h> |
@@ -180,6 +181,15 @@ struct lbs_private { | |||
180 | wait_queue_head_t scan_q; | 181 | wait_queue_head_t scan_q; |
181 | /* Whether the scan was initiated internally and not by cfg80211 */ | 182 | /* Whether the scan was initiated internally and not by cfg80211 */ |
182 | bool internal_scan; | 183 | bool internal_scan; |
184 | |||
185 | /* Firmware load */ | ||
186 | u32 fw_model; | ||
187 | wait_queue_head_t fw_waitq; | ||
188 | struct device *fw_device; | ||
189 | const struct firmware *helper_fw; | ||
190 | const struct lbs_fw_table *fw_table; | ||
191 | const struct lbs_fw_table *fw_iter; | ||
192 | lbs_fw_cb fw_callback; | ||
183 | }; | 193 | }; |
184 | 194 | ||
185 | extern struct cmd_confirm_sleep confirm_sleep; | 195 | extern struct cmd_confirm_sleep confirm_sleep; |
diff --git a/drivers/net/wireless/libertas/firmware.c b/drivers/net/wireless/libertas/firmware.c index 0c8c845b4901..cd23f1a8c98a 100644 --- a/drivers/net/wireless/libertas/firmware.c +++ b/drivers/net/wireless/libertas/firmware.c | |||
@@ -3,10 +3,151 @@ | |||
3 | */ | 3 | */ |
4 | 4 | ||
5 | #include <linux/firmware.h> | 5 | #include <linux/firmware.h> |
6 | #include <linux/firmware.h> | ||
6 | #include <linux/module.h> | 7 | #include <linux/module.h> |
7 | 8 | ||
9 | #include "dev.h" | ||
8 | #include "decl.h" | 10 | #include "decl.h" |
9 | 11 | ||
12 | static void load_next_firmware_from_table(struct lbs_private *private); | ||
13 | |||
14 | static void lbs_fw_loaded(struct lbs_private *priv, int ret, | ||
15 | const struct firmware *helper, const struct firmware *mainfw) | ||
16 | { | ||
17 | unsigned long flags; | ||
18 | |||
19 | lbs_deb_fw("firmware load complete, code %d\n", ret); | ||
20 | |||
21 | /* User must free helper/mainfw */ | ||
22 | priv->fw_callback(priv, ret, helper, mainfw); | ||
23 | |||
24 | spin_lock_irqsave(&priv->driver_lock, flags); | ||
25 | priv->fw_callback = NULL; | ||
26 | wake_up(&priv->fw_waitq); | ||
27 | spin_unlock_irqrestore(&priv->driver_lock, flags); | ||
28 | } | ||
29 | |||
30 | static void do_load_firmware(struct lbs_private *priv, const char *name, | ||
31 | void (*cb)(const struct firmware *fw, void *context)) | ||
32 | { | ||
33 | int ret; | ||
34 | |||
35 | lbs_deb_fw("Requesting %s\n", name); | ||
36 | ret = request_firmware_nowait(THIS_MODULE, true, name, | ||
37 | priv->fw_device, GFP_KERNEL, priv, cb); | ||
38 | if (ret) { | ||
39 | lbs_deb_fw("request_firmware_nowait error %d\n", ret); | ||
40 | lbs_fw_loaded(priv, ret, NULL, NULL); | ||
41 | } | ||
42 | } | ||
43 | |||
44 | static void main_firmware_cb(const struct firmware *firmware, void *context) | ||
45 | { | ||
46 | struct lbs_private *priv = context; | ||
47 | |||
48 | if (!firmware) { | ||
49 | /* Failed to find firmware: try next table entry */ | ||
50 | load_next_firmware_from_table(priv); | ||
51 | return; | ||
52 | } | ||
53 | |||
54 | /* Firmware found! */ | ||
55 | lbs_fw_loaded(priv, 0, priv->helper_fw, firmware); | ||
56 | } | ||
57 | |||
58 | static void helper_firmware_cb(const struct firmware *firmware, void *context) | ||
59 | { | ||
60 | struct lbs_private *priv = context; | ||
61 | |||
62 | if (!firmware) { | ||
63 | /* Failed to find firmware: try next table entry */ | ||
64 | load_next_firmware_from_table(priv); | ||
65 | return; | ||
66 | } | ||
67 | |||
68 | /* Firmware found! */ | ||
69 | if (priv->fw_iter->fwname) { | ||
70 | priv->helper_fw = firmware; | ||
71 | do_load_firmware(priv, priv->fw_iter->fwname, main_firmware_cb); | ||
72 | } else { | ||
73 | /* No main firmware needed for this helper --> success! */ | ||
74 | lbs_fw_loaded(priv, 0, firmware, NULL); | ||
75 | } | ||
76 | } | ||
77 | |||
78 | static void load_next_firmware_from_table(struct lbs_private *priv) | ||
79 | { | ||
80 | const struct lbs_fw_table *iter; | ||
81 | |||
82 | if (!priv->fw_iter) | ||
83 | iter = priv->fw_table; | ||
84 | else | ||
85 | iter = ++priv->fw_iter; | ||
86 | |||
87 | if (priv->helper_fw) { | ||
88 | release_firmware(priv->helper_fw); | ||
89 | priv->helper_fw = NULL; | ||
90 | } | ||
91 | |||
92 | next: | ||
93 | if (!iter->helper) { | ||
94 | /* End of table hit. */ | ||
95 | lbs_fw_loaded(priv, -ENOENT, NULL, NULL); | ||
96 | return; | ||
97 | } | ||
98 | |||
99 | if (iter->model != priv->fw_model) { | ||
100 | iter++; | ||
101 | goto next; | ||
102 | } | ||
103 | |||
104 | priv->fw_iter = iter; | ||
105 | do_load_firmware(priv, iter->helper, helper_firmware_cb); | ||
106 | } | ||
107 | |||
108 | void lbs_wait_for_firmware_load(struct lbs_private *priv) | ||
109 | { | ||
110 | wait_event(priv->fw_waitq, priv->fw_callback == NULL); | ||
111 | } | ||
112 | |||
113 | /** | ||
114 | * lbs_get_firmware_async - Retrieves firmware asynchronously. Can load | ||
115 | * either a helper firmware and a main firmware (2-stage), or just the helper. | ||
116 | * | ||
117 | * @priv: Pointer to lbs_private instance | ||
118 | * @dev: A pointer to &device structure | ||
119 | * @card_model: Bus-specific card model ID used to filter firmware table | ||
120 | * elements | ||
121 | * @fw_table: Table of firmware file names and device model numbers | ||
122 | * terminated by an entry with a NULL helper name | ||
123 | * @callback: User callback to invoke when firmware load succeeds or fails. | ||
124 | */ | ||
125 | int lbs_get_firmware_async(struct lbs_private *priv, struct device *device, | ||
126 | u32 card_model, const struct lbs_fw_table *fw_table, | ||
127 | lbs_fw_cb callback) | ||
128 | { | ||
129 | unsigned long flags; | ||
130 | |||
131 | spin_lock_irqsave(&priv->driver_lock, flags); | ||
132 | if (priv->fw_callback) { | ||
133 | lbs_deb_fw("firmware load already in progress\n"); | ||
134 | spin_unlock_irqrestore(&priv->driver_lock, flags); | ||
135 | return -EBUSY; | ||
136 | } | ||
137 | |||
138 | priv->fw_device = device; | ||
139 | priv->fw_callback = callback; | ||
140 | priv->fw_table = fw_table; | ||
141 | priv->fw_iter = NULL; | ||
142 | priv->fw_model = card_model; | ||
143 | spin_unlock_irqrestore(&priv->driver_lock, flags); | ||
144 | |||
145 | lbs_deb_fw("Starting async firmware load\n"); | ||
146 | load_next_firmware_from_table(priv); | ||
147 | return 0; | ||
148 | } | ||
149 | EXPORT_SYMBOL_GPL(lbs_get_firmware_async); | ||
150 | |||
10 | /** | 151 | /** |
11 | * lbs_get_firmware - Retrieves two-stage firmware | 152 | * lbs_get_firmware - Retrieves two-stage firmware |
12 | * | 153 | * |
@@ -18,6 +159,8 @@ | |||
18 | * @helper: On success, the helper firmware; caller must free | 159 | * @helper: On success, the helper firmware; caller must free |
19 | * @mainfw: On success, the main firmware; caller must free | 160 | * @mainfw: On success, the main firmware; caller must free |
20 | * | 161 | * |
162 | * Deprecated: use lbs_get_firmware_async() instead. | ||
163 | * | ||
21 | * returns: 0 on success, non-zero on failure | 164 | * returns: 0 on success, non-zero on failure |
22 | */ | 165 | */ |
23 | int lbs_get_firmware(struct device *dev, u32 card_model, | 166 | int lbs_get_firmware(struct device *dev, u32 card_model, |
diff --git a/drivers/net/wireless/libertas/main.c b/drivers/net/wireless/libertas/main.c index 7eaf992775ae..e96ee0aa8439 100644 --- a/drivers/net/wireless/libertas/main.c +++ b/drivers/net/wireless/libertas/main.c | |||
@@ -878,6 +878,7 @@ static int lbs_init_adapter(struct lbs_private *priv) | |||
878 | priv->is_host_sleep_configured = 0; | 878 | priv->is_host_sleep_configured = 0; |
879 | priv->is_host_sleep_activated = 0; | 879 | priv->is_host_sleep_activated = 0; |
880 | init_waitqueue_head(&priv->host_sleep_q); | 880 | init_waitqueue_head(&priv->host_sleep_q); |
881 | init_waitqueue_head(&priv->fw_waitq); | ||
881 | mutex_init(&priv->lock); | 882 | mutex_init(&priv->lock); |
882 | 883 | ||
883 | setup_timer(&priv->command_timer, lbs_cmd_timeout_handler, | 884 | setup_timer(&priv->command_timer, lbs_cmd_timeout_handler, |
@@ -1037,6 +1038,8 @@ void lbs_remove_card(struct lbs_private *priv) | |||
1037 | if (priv->wiphy_registered) | 1038 | if (priv->wiphy_registered) |
1038 | lbs_scan_deinit(priv); | 1039 | lbs_scan_deinit(priv); |
1039 | 1040 | ||
1041 | lbs_wait_for_firmware_load(priv); | ||
1042 | |||
1040 | /* worker thread destruction blocks on the in-flight command which | 1043 | /* worker thread destruction blocks on the in-flight command which |
1041 | * should have been cleared already in lbs_stop_card(). | 1044 | * should have been cleared already in lbs_stop_card(). |
1042 | */ | 1045 | */ |