diff options
Diffstat (limited to 'drivers/net/wireless')
37 files changed, 25465 insertions, 0 deletions
diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig index 7aeb113cbb90..f354bd4e121e 100644 --- a/drivers/net/wireless/Kconfig +++ b/drivers/net/wireless/Kconfig | |||
@@ -284,5 +284,6 @@ source "drivers/net/wireless/rtlwifi/Kconfig" | |||
284 | source "drivers/net/wireless/wl1251/Kconfig" | 284 | source "drivers/net/wireless/wl1251/Kconfig" |
285 | source "drivers/net/wireless/wl12xx/Kconfig" | 285 | source "drivers/net/wireless/wl12xx/Kconfig" |
286 | source "drivers/net/wireless/zd1211rw/Kconfig" | 286 | source "drivers/net/wireless/zd1211rw/Kconfig" |
287 | source "drivers/net/wireless/mwifiex/Kconfig" | ||
287 | 288 | ||
288 | endif # WLAN | 289 | endif # WLAN |
diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile index ddd3fb6ba1d3..7bba6a82b875 100644 --- a/drivers/net/wireless/Makefile +++ b/drivers/net/wireless/Makefile | |||
@@ -56,3 +56,5 @@ obj-$(CONFIG_WL12XX) += wl12xx/ | |||
56 | obj-$(CONFIG_WL12XX_PLATFORM_DATA) += wl12xx/ | 56 | obj-$(CONFIG_WL12XX_PLATFORM_DATA) += wl12xx/ |
57 | 57 | ||
58 | obj-$(CONFIG_IWM) += iwmc3200wifi/ | 58 | obj-$(CONFIG_IWM) += iwmc3200wifi/ |
59 | |||
60 | obj-$(CONFIG_MWIFIEX) += mwifiex/ | ||
diff --git a/drivers/net/wireless/mwifiex/11n.c b/drivers/net/wireless/mwifiex/11n.c new file mode 100644 index 000000000000..0e04a21be0a3 --- /dev/null +++ b/drivers/net/wireless/mwifiex/11n.c | |||
@@ -0,0 +1,922 @@ | |||
1 | /* | ||
2 | * Marvell Wireless LAN device driver: 802.11n | ||
3 | * | ||
4 | * Copyright (C) 2011, Marvell International Ltd. | ||
5 | * | ||
6 | * This software file (the "File") is distributed by Marvell International | ||
7 | * Ltd. under the terms of the GNU General Public License Version 2, June 1991 | ||
8 | * (the "License"). You may use, redistribute and/or modify this File in | ||
9 | * accordance with the terms and conditions of the License, a copy of which | ||
10 | * is available by writing to the Free Software Foundation, Inc., | ||
11 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the | ||
12 | * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. | ||
13 | * | ||
14 | * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE | ||
15 | * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE | ||
16 | * ARE EXPRESSLY DISCLAIMED. The License provides additional details about | ||
17 | * this warranty disclaimer. | ||
18 | */ | ||
19 | |||
20 | #include "decl.h" | ||
21 | #include "ioctl.h" | ||
22 | #include "util.h" | ||
23 | #include "fw.h" | ||
24 | #include "main.h" | ||
25 | #include "wmm.h" | ||
26 | #include "11n.h" | ||
27 | |||
28 | /* | ||
29 | * Fills HT capability information field, AMPDU Parameters field, HT extended | ||
30 | * capability field, and supported MCS set fields. | ||
31 | * | ||
32 | * Only the following HT capability information fields are used, all other | ||
33 | * fields are always turned off. | ||
34 | * | ||
35 | * Bit 1 : Supported channel width (0: 20MHz, 1: Both 20 and 40 MHz) | ||
36 | * Bit 4 : Greenfield support (0: Not supported, 1: Supported) | ||
37 | * Bit 5 : Short GI for 20 MHz support (0: Not supported, 1: Supported) | ||
38 | * Bit 6 : Short GI for 40 MHz support (0: Not supported, 1: Supported) | ||
39 | * Bit 7 : Tx STBC (0: Not supported, 1: Supported) | ||
40 | * Bit 8-9 : Rx STBC (0: Not supported, X: Support for up to X spatial streams) | ||
41 | * Bit 10 : Delayed BA support (0: Not supported, 1: Supported) | ||
42 | * Bit 11 : Maximum AMSDU length (0: 3839 octets, 1: 7935 octets) | ||
43 | * Bit 14 : 40-Mhz intolerant support (0: Not supported, 1: Supported) | ||
44 | * | ||
45 | * In addition, the following AMPDU Parameters are set - | ||
46 | * - Maximum AMPDU length exponent (set to 3) | ||
47 | * - Minimum AMPDU start spacing (set to 0 - No restrictions) | ||
48 | * | ||
49 | * MCS is set for 1x1, with MSC32 for infra mode or ad-hoc mode with 40 MHz | ||
50 | * support. | ||
51 | * | ||
52 | * RD responder bit to set to clear in the extended capability header. | ||
53 | */ | ||
54 | void | ||
55 | mwifiex_fill_cap_info(struct mwifiex_private *priv, | ||
56 | struct mwifiex_ie_types_htcap *ht_cap) | ||
57 | { | ||
58 | struct mwifiex_adapter *adapter = priv->adapter; | ||
59 | u8 *mcs; | ||
60 | int rx_mcs_supp; | ||
61 | uint16_t ht_cap_info = le16_to_cpu(ht_cap->ht_cap.cap_info); | ||
62 | uint16_t ht_ext_cap = le16_to_cpu(ht_cap->ht_cap.extended_ht_cap_info); | ||
63 | |||
64 | if (ISSUPP_CHANWIDTH40(adapter->hw_dot_11n_dev_cap) && | ||
65 | ISSUPP_CHANWIDTH40(adapter->usr_dot_11n_dev_cap)) | ||
66 | SETHT_SUPPCHANWIDTH(ht_cap_info); | ||
67 | else | ||
68 | RESETHT_SUPPCHANWIDTH(ht_cap_info); | ||
69 | |||
70 | if (ISSUPP_GREENFIELD(adapter->hw_dot_11n_dev_cap) && | ||
71 | ISSUPP_GREENFIELD(adapter->usr_dot_11n_dev_cap)) | ||
72 | SETHT_GREENFIELD(ht_cap_info); | ||
73 | else | ||
74 | RESETHT_GREENFIELD(ht_cap_info); | ||
75 | |||
76 | if (ISSUPP_SHORTGI20(adapter->hw_dot_11n_dev_cap) && | ||
77 | ISSUPP_SHORTGI20(adapter->usr_dot_11n_dev_cap)) | ||
78 | SETHT_SHORTGI20(ht_cap_info); | ||
79 | else | ||
80 | RESETHT_SHORTGI20(ht_cap_info); | ||
81 | |||
82 | if (ISSUPP_SHORTGI40(adapter->hw_dot_11n_dev_cap) && | ||
83 | ISSUPP_SHORTGI40(adapter->usr_dot_11n_dev_cap)) | ||
84 | SETHT_SHORTGI40(ht_cap_info); | ||
85 | else | ||
86 | RESETHT_SHORTGI40(ht_cap_info); | ||
87 | |||
88 | /* No user config for RX STBC yet */ | ||
89 | if (ISSUPP_RXSTBC(adapter->hw_dot_11n_dev_cap) | ||
90 | && ISSUPP_RXSTBC(adapter->usr_dot_11n_dev_cap)) | ||
91 | SETHT_RXSTBC(ht_cap_info, 1); | ||
92 | else | ||
93 | RESETHT_RXSTBC(ht_cap_info); | ||
94 | |||
95 | /* No user config for TX STBC yet */ | ||
96 | if (ISSUPP_TXSTBC(adapter->hw_dot_11n_dev_cap)) | ||
97 | SETHT_TXSTBC(ht_cap_info); | ||
98 | else | ||
99 | RESETHT_TXSTBC(ht_cap_info); | ||
100 | |||
101 | /* No user config for Delayed BACK yet */ | ||
102 | if (GET_DELAYEDBACK(adapter->hw_dot_11n_dev_cap)) | ||
103 | SETHT_DELAYEDBACK(ht_cap_info); | ||
104 | else | ||
105 | RESETHT_DELAYEDBACK(ht_cap_info); | ||
106 | |||
107 | if (ISENABLED_40MHZ_INTOLARENT(adapter->usr_dot_11n_dev_cap)) | ||
108 | SETHT_40MHZ_INTOLARANT(ht_cap_info); | ||
109 | else | ||
110 | RESETHT_40MHZ_INTOLARANT(ht_cap_info); | ||
111 | |||
112 | SETAMPDU_SIZE(ht_cap->ht_cap.ampdu_params_info, AMPDU_FACTOR_64K); | ||
113 | SETAMPDU_SPACING(ht_cap->ht_cap.ampdu_params_info, 0); | ||
114 | |||
115 | /* Need change to support 8k AMSDU receive */ | ||
116 | RESETHT_MAXAMSDU(ht_cap_info); | ||
117 | |||
118 | rx_mcs_supp = GET_RXMCSSUPP(adapter->hw_dev_mcs_support); | ||
119 | |||
120 | mcs = (u8 *)&ht_cap->ht_cap.mcs; | ||
121 | |||
122 | /* Set MCS for 1x1 */ | ||
123 | memset(mcs, 0xff, rx_mcs_supp); | ||
124 | |||
125 | /* Clear all the other values */ | ||
126 | memset(&mcs[rx_mcs_supp], 0, | ||
127 | sizeof(struct ieee80211_mcs_info) - rx_mcs_supp); | ||
128 | |||
129 | if (priv->bss_mode == MWIFIEX_BSS_MODE_INFRA || | ||
130 | (ISSUPP_CHANWIDTH40(adapter->hw_dot_11n_dev_cap) && | ||
131 | ISSUPP_CHANWIDTH40(adapter->usr_dot_11n_dev_cap))) | ||
132 | /* Set MCS32 for infra mode or ad-hoc mode with 40MHz support */ | ||
133 | SETHT_MCS32(ht_cap->ht_cap.mcs.rx_mask); | ||
134 | |||
135 | /* Clear RD responder bit */ | ||
136 | RESETHT_EXTCAP_RDG(ht_ext_cap); | ||
137 | |||
138 | ht_cap->ht_cap.cap_info = cpu_to_le16(ht_cap_info); | ||
139 | ht_cap->ht_cap.extended_ht_cap_info = cpu_to_le16(ht_ext_cap); | ||
140 | } | ||
141 | |||
142 | /* | ||
143 | * Shows HT capability information fields. | ||
144 | * | ||
145 | * The following HT capability information fields are supported. | ||
146 | * - Maximum AMSDU length (3839 bytes or 7935 bytes) | ||
147 | * - Beam forming support | ||
148 | * - Greenfield preamble support | ||
149 | * - AMPDU support | ||
150 | * - MIMO Power Save support | ||
151 | * - Rx STBC support | ||
152 | * - Tx STBC support | ||
153 | * - Short GI for 20 MHz support | ||
154 | * - Short GI for 40 MHz support | ||
155 | * - LDPC coded packets receive support | ||
156 | * - Number of delayed BA streams | ||
157 | * - Number of immediate BA streams | ||
158 | * - 10 MHz channel width support | ||
159 | * - 20 MHz channel width support | ||
160 | * - 40 MHz channel width support | ||
161 | * - Presence of Tx antenna A/B/C/D | ||
162 | * - Presence of Rx antenna A/B/C/D | ||
163 | */ | ||
164 | void | ||
165 | mwifiex_show_dot_11n_dev_cap(struct mwifiex_adapter *adapter, u32 cap) | ||
166 | { | ||
167 | dev_dbg(adapter->dev, "info: GET_HW_SPEC: Max MSDU len = %s octets\n", | ||
168 | (ISSUPP_MAXAMSDU(cap) ? "7935" : "3839")); | ||
169 | dev_dbg(adapter->dev, "info: GET_HW_SPEC: Beam forming %s\n", | ||
170 | (ISSUPP_BEAMFORMING(cap) ? "supported" : "not supported")); | ||
171 | dev_dbg(adapter->dev, "info: GET_HW_SPEC: Greenfield preamble %s\n", | ||
172 | (ISSUPP_GREENFIELD(cap) ? "supported" : "not supported")); | ||
173 | dev_dbg(adapter->dev, "info: GET_HW_SPEC: AMPDU %s\n", | ||
174 | (ISSUPP_AMPDU(cap) ? "supported" : "not supported")); | ||
175 | dev_dbg(adapter->dev, "info: GET_HW_SPEC: MIMO Power Save %s\n", | ||
176 | (ISSUPP_MIMOPS(cap) ? "supported" : "not supported")); | ||
177 | dev_dbg(adapter->dev, "info: GET_HW_SPEC: Rx STBC %s\n", | ||
178 | (ISSUPP_RXSTBC(cap) ? "supported" : "not supported")); | ||
179 | dev_dbg(adapter->dev, "info: GET_HW_SPEC: Tx STBC %s\n", | ||
180 | (ISSUPP_TXSTBC(cap) ? "supported" : "not supported")); | ||
181 | dev_dbg(adapter->dev, "info: GET_HW_SPEC: Short GI for 40 Mhz %s\n", | ||
182 | (ISSUPP_SHORTGI40(cap) ? "supported" : "not supported")); | ||
183 | dev_dbg(adapter->dev, "info: GET_HW_SPEC: Short GI for 20 Mhz %s\n", | ||
184 | (ISSUPP_SHORTGI20(cap) ? "supported" : "not supported")); | ||
185 | dev_dbg(adapter->dev, "info: GET_HW_SPEC: LDPC coded packet receive %s\n", | ||
186 | (ISSUPP_RXLDPC(cap) ? "supported" : "not supported")); | ||
187 | dev_dbg(adapter->dev, | ||
188 | "info: GET_HW_SPEC: Number of Delayed Block Ack streams = %d\n", | ||
189 | GET_DELAYEDBACK(cap)); | ||
190 | dev_dbg(adapter->dev, | ||
191 | "info: GET_HW_SPEC: Number of Immediate Block Ack streams = %d\n", | ||
192 | GET_IMMEDIATEBACK(cap)); | ||
193 | dev_dbg(adapter->dev, "info: GET_HW_SPEC: 40 Mhz channel width %s\n", | ||
194 | (ISSUPP_CHANWIDTH40(cap) ? "supported" : "not supported")); | ||
195 | dev_dbg(adapter->dev, "info: GET_HW_SPEC: 20 Mhz channel width %s\n", | ||
196 | (ISSUPP_CHANWIDTH20(cap) ? "supported" : "not supported")); | ||
197 | dev_dbg(adapter->dev, "info: GET_HW_SPEC: 10 Mhz channel width %s\n", | ||
198 | (ISSUPP_CHANWIDTH10(cap) ? "supported" : "not supported")); | ||
199 | |||
200 | if (ISSUPP_RXANTENNAA(cap)) | ||
201 | dev_dbg(adapter->dev, "info: GET_HW_SPEC: Prescence of Rx antennea A\n"); | ||
202 | |||
203 | if (ISSUPP_RXANTENNAB(cap)) | ||
204 | dev_dbg(adapter->dev, "info: GET_HW_SPEC: Prescence of Rx antennea B\n"); | ||
205 | |||
206 | if (ISSUPP_RXANTENNAC(cap)) | ||
207 | dev_dbg(adapter->dev, "info: GET_HW_SPEC: Prescence of Rx antennea C\n"); | ||
208 | |||
209 | if (ISSUPP_RXANTENNAD(cap)) | ||
210 | dev_dbg(adapter->dev, "info: GET_HW_SPEC: Prescence of Rx antennea D\n"); | ||
211 | |||
212 | if (ISSUPP_TXANTENNAA(cap)) | ||
213 | dev_dbg(adapter->dev, "info: GET_HW_SPEC: Prescence of Tx antennea A\n"); | ||
214 | |||
215 | if (ISSUPP_TXANTENNAB(cap)) | ||
216 | dev_dbg(adapter->dev, "info: GET_HW_SPEC: Prescence of Tx antennea B\n"); | ||
217 | |||
218 | if (ISSUPP_TXANTENNAC(cap)) | ||
219 | dev_dbg(adapter->dev, "info: GET_HW_SPEC: Prescence of Tx antennea C\n"); | ||
220 | |||
221 | if (ISSUPP_TXANTENNAD(cap)) | ||
222 | dev_dbg(adapter->dev, "info: GET_HW_SPEC: Prescence of Tx antennea D\n"); | ||
223 | |||
224 | return; | ||
225 | } | ||
226 | |||
227 | /* | ||
228 | * Shows HT MCS support field. | ||
229 | */ | ||
230 | void | ||
231 | mwifiex_show_dev_mcs_support(struct mwifiex_adapter *adapter, u8 support) | ||
232 | { | ||
233 | dev_dbg(adapter->dev, "info: GET_HW_SPEC: MCSs for %dx%d MIMO\n", | ||
234 | GET_RXMCSSUPP(support), GET_TXMCSSUPP(support)); | ||
235 | return; | ||
236 | } | ||
237 | |||
238 | /* | ||
239 | * This function returns the pointer to an entry in BA Stream | ||
240 | * table which matches the requested BA status. | ||
241 | */ | ||
242 | static struct mwifiex_tx_ba_stream_tbl * | ||
243 | mwifiex_11n_get_tx_ba_stream_status(struct mwifiex_private *priv, | ||
244 | enum mwifiex_ba_status ba_status) | ||
245 | { | ||
246 | struct mwifiex_tx_ba_stream_tbl *tx_ba_tsr_tbl; | ||
247 | unsigned long flags; | ||
248 | |||
249 | spin_lock_irqsave(&priv->tx_ba_stream_tbl_lock, flags); | ||
250 | list_for_each_entry(tx_ba_tsr_tbl, &priv->tx_ba_stream_tbl_ptr, list) { | ||
251 | if (tx_ba_tsr_tbl->ba_status == ba_status) { | ||
252 | spin_unlock_irqrestore(&priv->tx_ba_stream_tbl_lock, | ||
253 | flags); | ||
254 | return tx_ba_tsr_tbl; | ||
255 | } | ||
256 | } | ||
257 | spin_unlock_irqrestore(&priv->tx_ba_stream_tbl_lock, flags); | ||
258 | return NULL; | ||
259 | } | ||
260 | |||
261 | /* | ||
262 | * This function handles the command response of delete a block | ||
263 | * ack request. | ||
264 | * | ||
265 | * The function checks the response success status and takes action | ||
266 | * accordingly (send an add BA request in case of success, or recreate | ||
267 | * the deleted stream in case of failure, if the add BA was also | ||
268 | * initiated by us). | ||
269 | */ | ||
270 | int mwifiex_ret_11n_delba(struct mwifiex_private *priv, | ||
271 | struct host_cmd_ds_command *resp) | ||
272 | { | ||
273 | int tid; | ||
274 | struct mwifiex_tx_ba_stream_tbl *tx_ba_tbl; | ||
275 | struct host_cmd_ds_11n_delba *del_ba = | ||
276 | (struct host_cmd_ds_11n_delba *) &resp->params.del_ba; | ||
277 | uint16_t del_ba_param_set = le16_to_cpu(del_ba->del_ba_param_set); | ||
278 | |||
279 | tid = del_ba_param_set >> DELBA_TID_POS; | ||
280 | if (del_ba->del_result == BA_RESULT_SUCCESS) { | ||
281 | mwifiex_11n_delete_ba_stream_tbl(priv, tid, | ||
282 | del_ba->peer_mac_addr, TYPE_DELBA_SENT, | ||
283 | INITIATOR_BIT(del_ba_param_set)); | ||
284 | |||
285 | tx_ba_tbl = mwifiex_11n_get_tx_ba_stream_status(priv, | ||
286 | BA_STREAM_SETUP_INPROGRESS); | ||
287 | if (tx_ba_tbl) | ||
288 | mwifiex_send_addba(priv, tx_ba_tbl->tid, | ||
289 | tx_ba_tbl->ra); | ||
290 | } else { /* | ||
291 | * In case of failure, recreate the deleted stream in case | ||
292 | * we initiated the ADDBA | ||
293 | */ | ||
294 | if (INITIATOR_BIT(del_ba_param_set)) { | ||
295 | mwifiex_11n_create_tx_ba_stream_tbl(priv, | ||
296 | del_ba->peer_mac_addr, tid, | ||
297 | BA_STREAM_SETUP_INPROGRESS); | ||
298 | |||
299 | tx_ba_tbl = mwifiex_11n_get_tx_ba_stream_status(priv, | ||
300 | BA_STREAM_SETUP_INPROGRESS); | ||
301 | if (tx_ba_tbl) | ||
302 | mwifiex_11n_delete_ba_stream_tbl(priv, | ||
303 | tx_ba_tbl->tid, tx_ba_tbl->ra, | ||
304 | TYPE_DELBA_SENT, true); | ||
305 | } | ||
306 | } | ||
307 | |||
308 | return 0; | ||
309 | } | ||
310 | |||
311 | /* | ||
312 | * This function handles the command response of add a block | ||
313 | * ack request. | ||
314 | * | ||
315 | * Handling includes changing the header fields to CPU formats, checking | ||
316 | * the response success status and taking actions accordingly (delete the | ||
317 | * BA stream table in case of failure). | ||
318 | */ | ||
319 | int mwifiex_ret_11n_addba_req(struct mwifiex_private *priv, | ||
320 | struct host_cmd_ds_command *resp) | ||
321 | { | ||
322 | int tid; | ||
323 | struct host_cmd_ds_11n_addba_rsp *add_ba_rsp = | ||
324 | (struct host_cmd_ds_11n_addba_rsp *) &resp->params.add_ba_rsp; | ||
325 | struct mwifiex_tx_ba_stream_tbl *tx_ba_tbl; | ||
326 | |||
327 | add_ba_rsp->ssn = cpu_to_le16((le16_to_cpu(add_ba_rsp->ssn)) | ||
328 | & SSN_MASK); | ||
329 | |||
330 | tid = (le16_to_cpu(add_ba_rsp->block_ack_param_set) | ||
331 | & IEEE80211_ADDBA_PARAM_TID_MASK) | ||
332 | >> BLOCKACKPARAM_TID_POS; | ||
333 | if (le16_to_cpu(add_ba_rsp->status_code) == BA_RESULT_SUCCESS) { | ||
334 | tx_ba_tbl = mwifiex_11n_get_tx_ba_stream_tbl(priv, tid, | ||
335 | add_ba_rsp->peer_mac_addr); | ||
336 | if (tx_ba_tbl) { | ||
337 | dev_dbg(priv->adapter->dev, "info: BA stream complete\n"); | ||
338 | tx_ba_tbl->ba_status = BA_STREAM_SETUP_COMPLETE; | ||
339 | } else { | ||
340 | dev_err(priv->adapter->dev, "BA stream not created\n"); | ||
341 | } | ||
342 | } else { | ||
343 | mwifiex_11n_delete_ba_stream_tbl(priv, tid, | ||
344 | add_ba_rsp->peer_mac_addr, | ||
345 | TYPE_DELBA_SENT, true); | ||
346 | if (add_ba_rsp->add_rsp_result != BA_RESULT_TIMEOUT) | ||
347 | priv->aggr_prio_tbl[tid].ampdu_ap = | ||
348 | BA_STREAM_NOT_ALLOWED; | ||
349 | } | ||
350 | |||
351 | return 0; | ||
352 | } | ||
353 | |||
354 | /* | ||
355 | * This function handles the command response of 11n configuration request. | ||
356 | * | ||
357 | * Handling includes changing the header fields into CPU format. | ||
358 | */ | ||
359 | int mwifiex_ret_11n_cfg(struct mwifiex_private *priv, | ||
360 | struct host_cmd_ds_command *resp, | ||
361 | void *data_buf) | ||
362 | { | ||
363 | struct mwifiex_ds_11n_tx_cfg *tx_cfg = NULL; | ||
364 | struct host_cmd_ds_11n_cfg *htcfg = &resp->params.htcfg; | ||
365 | |||
366 | if (data_buf) { | ||
367 | tx_cfg = (struct mwifiex_ds_11n_tx_cfg *) data_buf; | ||
368 | tx_cfg->tx_htcap = le16_to_cpu(htcfg->ht_tx_cap); | ||
369 | tx_cfg->tx_htinfo = le16_to_cpu(htcfg->ht_tx_info); | ||
370 | } | ||
371 | return 0; | ||
372 | } | ||
373 | |||
374 | /* | ||
375 | * This function prepares command of reconfigure Tx buffer. | ||
376 | * | ||
377 | * Preparation includes - | ||
378 | * - Setting command ID, action and proper size | ||
379 | * - Setting Tx buffer size (for SET only) | ||
380 | * - Ensuring correct endian-ness | ||
381 | */ | ||
382 | int mwifiex_cmd_recfg_tx_buf(struct mwifiex_private *priv, | ||
383 | struct host_cmd_ds_command *cmd, int cmd_action, | ||
384 | void *data_buf) | ||
385 | { | ||
386 | struct host_cmd_ds_txbuf_cfg *tx_buf = &cmd->params.tx_buf; | ||
387 | u16 action = (u16) cmd_action; | ||
388 | u16 buf_size = *((u16 *) data_buf); | ||
389 | |||
390 | cmd->command = cpu_to_le16(HostCmd_CMD_RECONFIGURE_TX_BUFF); | ||
391 | cmd->size = | ||
392 | cpu_to_le16(sizeof(struct host_cmd_ds_txbuf_cfg) + S_DS_GEN); | ||
393 | tx_buf->action = cpu_to_le16(action); | ||
394 | switch (action) { | ||
395 | case HostCmd_ACT_GEN_SET: | ||
396 | dev_dbg(priv->adapter->dev, "cmd: set tx_buf=%d\n", buf_size); | ||
397 | tx_buf->buff_size = cpu_to_le16(buf_size); | ||
398 | break; | ||
399 | case HostCmd_ACT_GEN_GET: | ||
400 | default: | ||
401 | tx_buf->buff_size = 0; | ||
402 | break; | ||
403 | } | ||
404 | return 0; | ||
405 | } | ||
406 | |||
407 | /* | ||
408 | * This function prepares command of AMSDU aggregation control. | ||
409 | * | ||
410 | * Preparation includes - | ||
411 | * - Setting command ID, action and proper size | ||
412 | * - Setting AMSDU control parameters (for SET only) | ||
413 | * - Ensuring correct endian-ness | ||
414 | */ | ||
415 | int mwifiex_cmd_amsdu_aggr_ctrl(struct mwifiex_private *priv, | ||
416 | struct host_cmd_ds_command *cmd, | ||
417 | int cmd_action, void *data_buf) | ||
418 | { | ||
419 | struct host_cmd_ds_amsdu_aggr_ctrl *amsdu_ctrl = | ||
420 | &cmd->params.amsdu_aggr_ctrl; | ||
421 | u16 action = (u16) cmd_action; | ||
422 | struct mwifiex_ds_11n_amsdu_aggr_ctrl *aa_ctrl = | ||
423 | (struct mwifiex_ds_11n_amsdu_aggr_ctrl *) data_buf; | ||
424 | |||
425 | cmd->command = cpu_to_le16(HostCmd_CMD_AMSDU_AGGR_CTRL); | ||
426 | cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_amsdu_aggr_ctrl) | ||
427 | + S_DS_GEN); | ||
428 | amsdu_ctrl->action = cpu_to_le16(action); | ||
429 | switch (action) { | ||
430 | case HostCmd_ACT_GEN_SET: | ||
431 | amsdu_ctrl->enable = cpu_to_le16(aa_ctrl->enable); | ||
432 | amsdu_ctrl->curr_buf_size = 0; | ||
433 | break; | ||
434 | case HostCmd_ACT_GEN_GET: | ||
435 | default: | ||
436 | amsdu_ctrl->curr_buf_size = 0; | ||
437 | break; | ||
438 | } | ||
439 | return 0; | ||
440 | } | ||
441 | |||
442 | /* | ||
443 | * This function handles the command response of AMSDU aggregation | ||
444 | * control request. | ||
445 | * | ||
446 | * Handling includes changing the header fields into CPU format. | ||
447 | */ | ||
448 | int mwifiex_ret_amsdu_aggr_ctrl(struct mwifiex_private *priv, | ||
449 | struct host_cmd_ds_command *resp, | ||
450 | void *data_buf) | ||
451 | { | ||
452 | struct mwifiex_ds_11n_amsdu_aggr_ctrl *amsdu_aggr_ctrl = NULL; | ||
453 | struct host_cmd_ds_amsdu_aggr_ctrl *amsdu_ctrl = | ||
454 | &resp->params.amsdu_aggr_ctrl; | ||
455 | |||
456 | if (data_buf) { | ||
457 | amsdu_aggr_ctrl = | ||
458 | (struct mwifiex_ds_11n_amsdu_aggr_ctrl *) data_buf; | ||
459 | amsdu_aggr_ctrl->enable = le16_to_cpu(amsdu_ctrl->enable); | ||
460 | amsdu_aggr_ctrl->curr_buf_size = | ||
461 | le16_to_cpu(amsdu_ctrl->curr_buf_size); | ||
462 | } | ||
463 | return 0; | ||
464 | } | ||
465 | |||
466 | /* | ||
467 | * This function prepares 11n configuration command. | ||
468 | * | ||
469 | * Preparation includes - | ||
470 | * - Setting command ID, action and proper size | ||
471 | * - Setting HT Tx capability and HT Tx information fields | ||
472 | * - Ensuring correct endian-ness | ||
473 | */ | ||
474 | int mwifiex_cmd_11n_cfg(struct mwifiex_private *priv, | ||
475 | struct host_cmd_ds_command *cmd, | ||
476 | u16 cmd_action, void *data_buf) | ||
477 | { | ||
478 | struct host_cmd_ds_11n_cfg *htcfg = &cmd->params.htcfg; | ||
479 | struct mwifiex_ds_11n_tx_cfg *txcfg = | ||
480 | (struct mwifiex_ds_11n_tx_cfg *) data_buf; | ||
481 | |||
482 | cmd->command = cpu_to_le16(HostCmd_CMD_11N_CFG); | ||
483 | cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_11n_cfg) + S_DS_GEN); | ||
484 | htcfg->action = cpu_to_le16(cmd_action); | ||
485 | htcfg->ht_tx_cap = cpu_to_le16(txcfg->tx_htcap); | ||
486 | htcfg->ht_tx_info = cpu_to_le16(txcfg->tx_htinfo); | ||
487 | return 0; | ||
488 | } | ||
489 | |||
490 | /* | ||
491 | * This function appends an 11n TLV to a buffer. | ||
492 | * | ||
493 | * Buffer allocation is responsibility of the calling | ||
494 | * function. No size validation is made here. | ||
495 | * | ||
496 | * The function fills up the following sections, if applicable - | ||
497 | * - HT capability IE | ||
498 | * - HT information IE (with channel list) | ||
499 | * - 20/40 BSS Coexistence IE | ||
500 | * - HT Extended Capabilities IE | ||
501 | */ | ||
502 | int | ||
503 | mwifiex_cmd_append_11n_tlv(struct mwifiex_private *priv, | ||
504 | struct mwifiex_bssdescriptor *bss_desc, | ||
505 | u8 **buffer) | ||
506 | { | ||
507 | struct mwifiex_ie_types_htcap *ht_cap; | ||
508 | struct mwifiex_ie_types_htinfo *ht_info; | ||
509 | struct mwifiex_ie_types_chan_list_param_set *chan_list; | ||
510 | struct mwifiex_ie_types_2040bssco *bss_co_2040; | ||
511 | struct mwifiex_ie_types_extcap *ext_cap; | ||
512 | int ret_len = 0; | ||
513 | |||
514 | if (!buffer || !*buffer) | ||
515 | return ret_len; | ||
516 | |||
517 | if (bss_desc->bcn_ht_cap) { | ||
518 | ht_cap = (struct mwifiex_ie_types_htcap *) *buffer; | ||
519 | memset(ht_cap, 0, sizeof(struct mwifiex_ie_types_htcap)); | ||
520 | ht_cap->header.type = cpu_to_le16(WLAN_EID_HT_CAPABILITY); | ||
521 | ht_cap->header.len = | ||
522 | cpu_to_le16(sizeof(struct ieee80211_ht_cap)); | ||
523 | memcpy((u8 *) ht_cap + sizeof(struct mwifiex_ie_types_header), | ||
524 | (u8 *) bss_desc->bcn_ht_cap + | ||
525 | sizeof(struct ieee_types_header), | ||
526 | le16_to_cpu(ht_cap->header.len)); | ||
527 | |||
528 | mwifiex_fill_cap_info(priv, ht_cap); | ||
529 | |||
530 | *buffer += sizeof(struct mwifiex_ie_types_htcap); | ||
531 | ret_len += sizeof(struct mwifiex_ie_types_htcap); | ||
532 | } | ||
533 | |||
534 | if (bss_desc->bcn_ht_info) { | ||
535 | if (priv->bss_mode == MWIFIEX_BSS_MODE_IBSS) { | ||
536 | ht_info = (struct mwifiex_ie_types_htinfo *) *buffer; | ||
537 | memset(ht_info, 0, | ||
538 | sizeof(struct mwifiex_ie_types_htinfo)); | ||
539 | ht_info->header.type = | ||
540 | cpu_to_le16(WLAN_EID_HT_INFORMATION); | ||
541 | ht_info->header.len = | ||
542 | cpu_to_le16(sizeof(struct ieee80211_ht_info)); | ||
543 | |||
544 | memcpy((u8 *) ht_info + | ||
545 | sizeof(struct mwifiex_ie_types_header), | ||
546 | (u8 *) bss_desc->bcn_ht_info + | ||
547 | sizeof(struct ieee_types_header), | ||
548 | le16_to_cpu(ht_info->header.len)); | ||
549 | |||
550 | if (!ISSUPP_CHANWIDTH40 | ||
551 | (priv->adapter->hw_dot_11n_dev_cap) | ||
552 | || !ISSUPP_CHANWIDTH40(priv->adapter-> | ||
553 | usr_dot_11n_dev_cap)) | ||
554 | RESET_CHANWIDTH40(ht_info->ht_info.ht_param); | ||
555 | |||
556 | *buffer += sizeof(struct mwifiex_ie_types_htinfo); | ||
557 | ret_len += sizeof(struct mwifiex_ie_types_htinfo); | ||
558 | } | ||
559 | |||
560 | chan_list = | ||
561 | (struct mwifiex_ie_types_chan_list_param_set *) *buffer; | ||
562 | memset(chan_list, 0, | ||
563 | sizeof(struct mwifiex_ie_types_chan_list_param_set)); | ||
564 | chan_list->header.type = cpu_to_le16(TLV_TYPE_CHANLIST); | ||
565 | chan_list->header.len = cpu_to_le16( | ||
566 | sizeof(struct mwifiex_ie_types_chan_list_param_set) - | ||
567 | sizeof(struct mwifiex_ie_types_header)); | ||
568 | chan_list->chan_scan_param[0].chan_number = | ||
569 | bss_desc->bcn_ht_info->control_chan; | ||
570 | chan_list->chan_scan_param[0].radio_type = | ||
571 | mwifiex_band_to_radio_type((u8) bss_desc->bss_band); | ||
572 | |||
573 | if ((ISSUPP_CHANWIDTH40(priv->adapter->hw_dot_11n_dev_cap) && | ||
574 | ISSUPP_CHANWIDTH40(priv->adapter->usr_dot_11n_dev_cap)) | ||
575 | && ISALLOWED_CHANWIDTH40(bss_desc->bcn_ht_info->ht_param)) | ||
576 | SET_SECONDARYCHAN(chan_list->chan_scan_param[0]. | ||
577 | radio_type, | ||
578 | GET_SECONDARYCHAN(bss_desc-> | ||
579 | bcn_ht_info->ht_param)); | ||
580 | |||
581 | *buffer += sizeof(struct mwifiex_ie_types_chan_list_param_set); | ||
582 | ret_len += sizeof(struct mwifiex_ie_types_chan_list_param_set); | ||
583 | } | ||
584 | |||
585 | if (bss_desc->bcn_bss_co_2040) { | ||
586 | bss_co_2040 = (struct mwifiex_ie_types_2040bssco *) *buffer; | ||
587 | memset(bss_co_2040, 0, | ||
588 | sizeof(struct mwifiex_ie_types_2040bssco)); | ||
589 | bss_co_2040->header.type = cpu_to_le16(WLAN_EID_BSS_COEX_2040); | ||
590 | bss_co_2040->header.len = | ||
591 | cpu_to_le16(sizeof(bss_co_2040->bss_co_2040)); | ||
592 | |||
593 | memcpy((u8 *) bss_co_2040 + | ||
594 | sizeof(struct mwifiex_ie_types_header), | ||
595 | (u8 *) bss_desc->bcn_bss_co_2040 + | ||
596 | sizeof(struct ieee_types_header), | ||
597 | le16_to_cpu(bss_co_2040->header.len)); | ||
598 | |||
599 | *buffer += sizeof(struct mwifiex_ie_types_2040bssco); | ||
600 | ret_len += sizeof(struct mwifiex_ie_types_2040bssco); | ||
601 | } | ||
602 | |||
603 | if (bss_desc->bcn_ext_cap) { | ||
604 | ext_cap = (struct mwifiex_ie_types_extcap *) *buffer; | ||
605 | memset(ext_cap, 0, sizeof(struct mwifiex_ie_types_extcap)); | ||
606 | ext_cap->header.type = cpu_to_le16(WLAN_EID_EXT_CAPABILITY); | ||
607 | ext_cap->header.len = cpu_to_le16(sizeof(ext_cap->ext_cap)); | ||
608 | |||
609 | memcpy((u8 *) ext_cap + | ||
610 | sizeof(struct mwifiex_ie_types_header), | ||
611 | (u8 *) bss_desc->bcn_ext_cap + | ||
612 | sizeof(struct ieee_types_header), | ||
613 | le16_to_cpu(ext_cap->header.len)); | ||
614 | |||
615 | *buffer += sizeof(struct mwifiex_ie_types_extcap); | ||
616 | ret_len += sizeof(struct mwifiex_ie_types_extcap); | ||
617 | } | ||
618 | |||
619 | return ret_len; | ||
620 | } | ||
621 | |||
622 | /* | ||
623 | * This function reconfigures the Tx buffer size in firmware. | ||
624 | * | ||
625 | * This function prepares a firmware command and issues it, if | ||
626 | * the current Tx buffer size is different from the one requested. | ||
627 | * Maximum configurable Tx buffer size is limited by the HT capability | ||
628 | * field value. | ||
629 | */ | ||
630 | void | ||
631 | mwifiex_cfg_tx_buf(struct mwifiex_private *priv, | ||
632 | struct mwifiex_bssdescriptor *bss_desc) | ||
633 | { | ||
634 | u16 max_amsdu = MWIFIEX_TX_DATA_BUF_SIZE_2K; | ||
635 | u16 tx_buf = 0; | ||
636 | u16 curr_tx_buf_size = 0; | ||
637 | |||
638 | if (bss_desc->bcn_ht_cap) { | ||
639 | if (GETHT_MAXAMSDU(le16_to_cpu(bss_desc->bcn_ht_cap->cap_info))) | ||
640 | max_amsdu = MWIFIEX_TX_DATA_BUF_SIZE_8K; | ||
641 | else | ||
642 | max_amsdu = MWIFIEX_TX_DATA_BUF_SIZE_4K; | ||
643 | } | ||
644 | |||
645 | tx_buf = min(priv->adapter->max_tx_buf_size, max_amsdu); | ||
646 | |||
647 | dev_dbg(priv->adapter->dev, "info: max_amsdu=%d, max_tx_buf=%d\n", | ||
648 | max_amsdu, priv->adapter->max_tx_buf_size); | ||
649 | |||
650 | if (priv->adapter->curr_tx_buf_size <= MWIFIEX_TX_DATA_BUF_SIZE_2K) | ||
651 | curr_tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_2K; | ||
652 | else if (priv->adapter->curr_tx_buf_size <= MWIFIEX_TX_DATA_BUF_SIZE_4K) | ||
653 | curr_tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_4K; | ||
654 | else if (priv->adapter->curr_tx_buf_size <= MWIFIEX_TX_DATA_BUF_SIZE_8K) | ||
655 | curr_tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_8K; | ||
656 | if (curr_tx_buf_size != tx_buf) | ||
657 | mwifiex_prepare_cmd(priv, HostCmd_CMD_RECONFIGURE_TX_BUFF, | ||
658 | HostCmd_ACT_GEN_SET, 0, | ||
659 | NULL, &tx_buf); | ||
660 | |||
661 | return; | ||
662 | } | ||
663 | |||
664 | /* | ||
665 | * This function checks if the given pointer is valid entry of | ||
666 | * Tx BA Stream table. | ||
667 | */ | ||
668 | static int mwifiex_is_tx_ba_stream_ptr_valid(struct mwifiex_private *priv, | ||
669 | struct mwifiex_tx_ba_stream_tbl *tx_tbl_ptr) | ||
670 | { | ||
671 | struct mwifiex_tx_ba_stream_tbl *tx_ba_tsr_tbl; | ||
672 | |||
673 | list_for_each_entry(tx_ba_tsr_tbl, &priv->tx_ba_stream_tbl_ptr, list) { | ||
674 | if (tx_ba_tsr_tbl == tx_tbl_ptr) | ||
675 | return true; | ||
676 | } | ||
677 | |||
678 | return false; | ||
679 | } | ||
680 | |||
681 | /* | ||
682 | * This function deletes the given entry in Tx BA Stream table. | ||
683 | * | ||
684 | * The function also performs a validity check on the supplied | ||
685 | * pointer before trying to delete. | ||
686 | */ | ||
687 | void mwifiex_11n_delete_tx_ba_stream_tbl_entry(struct mwifiex_private *priv, | ||
688 | struct mwifiex_tx_ba_stream_tbl *tx_ba_tsr_tbl) | ||
689 | { | ||
690 | if (!tx_ba_tsr_tbl && | ||
691 | mwifiex_is_tx_ba_stream_ptr_valid(priv, tx_ba_tsr_tbl)) | ||
692 | return; | ||
693 | |||
694 | dev_dbg(priv->adapter->dev, "info: tx_ba_tsr_tbl %p\n", tx_ba_tsr_tbl); | ||
695 | |||
696 | list_del(&tx_ba_tsr_tbl->list); | ||
697 | |||
698 | kfree(tx_ba_tsr_tbl); | ||
699 | |||
700 | return; | ||
701 | } | ||
702 | |||
703 | /* | ||
704 | * This function deletes all the entries in Tx BA Stream table. | ||
705 | */ | ||
706 | void mwifiex_11n_delete_all_tx_ba_stream_tbl(struct mwifiex_private *priv) | ||
707 | { | ||
708 | int i; | ||
709 | struct mwifiex_tx_ba_stream_tbl *del_tbl_ptr, *tmp_node; | ||
710 | unsigned long flags; | ||
711 | |||
712 | spin_lock_irqsave(&priv->tx_ba_stream_tbl_lock, flags); | ||
713 | list_for_each_entry_safe(del_tbl_ptr, tmp_node, | ||
714 | &priv->tx_ba_stream_tbl_ptr, list) | ||
715 | mwifiex_11n_delete_tx_ba_stream_tbl_entry(priv, del_tbl_ptr); | ||
716 | spin_unlock_irqrestore(&priv->tx_ba_stream_tbl_lock, flags); | ||
717 | |||
718 | INIT_LIST_HEAD(&priv->tx_ba_stream_tbl_ptr); | ||
719 | |||
720 | for (i = 0; i < MAX_NUM_TID; ++i) | ||
721 | priv->aggr_prio_tbl[i].ampdu_ap = | ||
722 | priv->aggr_prio_tbl[i].ampdu_user; | ||
723 | } | ||
724 | |||
725 | /* | ||
726 | * This function returns the pointer to an entry in BA Stream | ||
727 | * table which matches the given RA/TID pair. | ||
728 | */ | ||
729 | struct mwifiex_tx_ba_stream_tbl * | ||
730 | mwifiex_11n_get_tx_ba_stream_tbl(struct mwifiex_private *priv, | ||
731 | int tid, u8 *ra) | ||
732 | { | ||
733 | struct mwifiex_tx_ba_stream_tbl *tx_ba_tsr_tbl; | ||
734 | unsigned long flags; | ||
735 | |||
736 | spin_lock_irqsave(&priv->tx_ba_stream_tbl_lock, flags); | ||
737 | list_for_each_entry(tx_ba_tsr_tbl, &priv->tx_ba_stream_tbl_ptr, list) { | ||
738 | if ((!memcmp(tx_ba_tsr_tbl->ra, ra, ETH_ALEN)) | ||
739 | && (tx_ba_tsr_tbl->tid == tid)) { | ||
740 | spin_unlock_irqrestore(&priv->tx_ba_stream_tbl_lock, | ||
741 | flags); | ||
742 | return tx_ba_tsr_tbl; | ||
743 | } | ||
744 | } | ||
745 | spin_unlock_irqrestore(&priv->tx_ba_stream_tbl_lock, flags); | ||
746 | return NULL; | ||
747 | } | ||
748 | |||
749 | /* | ||
750 | * This function creates an entry in Tx BA stream table for the | ||
751 | * given RA/TID pair. | ||
752 | */ | ||
753 | void mwifiex_11n_create_tx_ba_stream_tbl(struct mwifiex_private *priv, | ||
754 | u8 *ra, int tid, | ||
755 | enum mwifiex_ba_status ba_status) | ||
756 | { | ||
757 | struct mwifiex_tx_ba_stream_tbl *new_node; | ||
758 | unsigned long flags; | ||
759 | |||
760 | if (!mwifiex_11n_get_tx_ba_stream_tbl(priv, tid, ra)) { | ||
761 | new_node = kzalloc(sizeof(struct mwifiex_tx_ba_stream_tbl), | ||
762 | GFP_ATOMIC); | ||
763 | if (!new_node) { | ||
764 | dev_err(priv->adapter->dev, | ||
765 | "%s: failed to alloc new_node\n", __func__); | ||
766 | return; | ||
767 | } | ||
768 | |||
769 | INIT_LIST_HEAD(&new_node->list); | ||
770 | |||
771 | new_node->tid = tid; | ||
772 | new_node->ba_status = ba_status; | ||
773 | memcpy(new_node->ra, ra, ETH_ALEN); | ||
774 | |||
775 | spin_lock_irqsave(&priv->tx_ba_stream_tbl_lock, flags); | ||
776 | list_add_tail(&new_node->list, &priv->tx_ba_stream_tbl_ptr); | ||
777 | spin_unlock_irqrestore(&priv->tx_ba_stream_tbl_lock, flags); | ||
778 | } | ||
779 | |||
780 | return; | ||
781 | } | ||
782 | |||
783 | /* | ||
784 | * This function sends an add BA request to the given TID/RA pair. | ||
785 | */ | ||
786 | int mwifiex_send_addba(struct mwifiex_private *priv, int tid, u8 *peer_mac) | ||
787 | { | ||
788 | struct host_cmd_ds_11n_addba_req add_ba_req; | ||
789 | static u8 dialog_tok; | ||
790 | int ret; | ||
791 | |||
792 | dev_dbg(priv->adapter->dev, "cmd: %s: tid %d\n", __func__, tid); | ||
793 | |||
794 | add_ba_req.block_ack_param_set = cpu_to_le16( | ||
795 | (u16) ((tid << BLOCKACKPARAM_TID_POS) | | ||
796 | (priv->add_ba_param. | ||
797 | tx_win_size << BLOCKACKPARAM_WINSIZE_POS) | | ||
798 | IMMEDIATE_BLOCK_ACK)); | ||
799 | add_ba_req.block_ack_tmo = cpu_to_le16((u16)priv->add_ba_param.timeout); | ||
800 | |||
801 | ++dialog_tok; | ||
802 | |||
803 | if (dialog_tok == 0) | ||
804 | dialog_tok = 1; | ||
805 | |||
806 | add_ba_req.dialog_token = dialog_tok; | ||
807 | memcpy(&add_ba_req.peer_mac_addr, peer_mac, ETH_ALEN); | ||
808 | |||
809 | /* We don't wait for the response of this command */ | ||
810 | ret = mwifiex_prepare_cmd(priv, HostCmd_CMD_11N_ADDBA_REQ, | ||
811 | 0, 0, NULL, &add_ba_req); | ||
812 | |||
813 | return ret; | ||
814 | } | ||
815 | |||
816 | /* | ||
817 | * This function sends a delete BA request to the given TID/RA pair. | ||
818 | */ | ||
819 | int mwifiex_send_delba(struct mwifiex_private *priv, int tid, u8 *peer_mac, | ||
820 | int initiator) | ||
821 | { | ||
822 | struct host_cmd_ds_11n_delba delba; | ||
823 | int ret; | ||
824 | uint16_t del_ba_param_set; | ||
825 | |||
826 | memset(&delba, 0, sizeof(delba)); | ||
827 | delba.del_ba_param_set = cpu_to_le16(tid << DELBA_TID_POS); | ||
828 | |||
829 | del_ba_param_set = le16_to_cpu(delba.del_ba_param_set); | ||
830 | if (initiator) | ||
831 | del_ba_param_set |= IEEE80211_DELBA_PARAM_INITIATOR_MASK; | ||
832 | else | ||
833 | del_ba_param_set &= ~IEEE80211_DELBA_PARAM_INITIATOR_MASK; | ||
834 | |||
835 | memcpy(&delba.peer_mac_addr, peer_mac, ETH_ALEN); | ||
836 | |||
837 | /* We don't wait for the response of this command */ | ||
838 | ret = mwifiex_prepare_cmd(priv, HostCmd_CMD_11N_DELBA, | ||
839 | HostCmd_ACT_GEN_SET, 0, NULL, &delba); | ||
840 | |||
841 | return ret; | ||
842 | } | ||
843 | |||
844 | /* | ||
845 | * This function handles the command response of a delete BA request. | ||
846 | */ | ||
847 | void mwifiex_11n_delete_ba_stream(struct mwifiex_private *priv, u8 *del_ba) | ||
848 | { | ||
849 | struct host_cmd_ds_11n_delba *cmd_del_ba = | ||
850 | (struct host_cmd_ds_11n_delba *) del_ba; | ||
851 | uint16_t del_ba_param_set = le16_to_cpu(cmd_del_ba->del_ba_param_set); | ||
852 | int tid; | ||
853 | |||
854 | tid = del_ba_param_set >> DELBA_TID_POS; | ||
855 | |||
856 | mwifiex_11n_delete_ba_stream_tbl(priv, tid, cmd_del_ba->peer_mac_addr, | ||
857 | TYPE_DELBA_RECEIVE, | ||
858 | INITIATOR_BIT(del_ba_param_set)); | ||
859 | } | ||
860 | |||
861 | /* | ||
862 | * This function retrieves the Rx reordering table. | ||
863 | */ | ||
864 | int mwifiex_get_rx_reorder_tbl(struct mwifiex_private *priv, | ||
865 | struct mwifiex_ds_rx_reorder_tbl *buf) | ||
866 | { | ||
867 | int i; | ||
868 | struct mwifiex_ds_rx_reorder_tbl *rx_reo_tbl = buf; | ||
869 | struct mwifiex_rx_reorder_tbl *rx_reorder_tbl_ptr; | ||
870 | int count = 0; | ||
871 | unsigned long flags; | ||
872 | |||
873 | spin_lock_irqsave(&priv->rx_reorder_tbl_lock, flags); | ||
874 | list_for_each_entry(rx_reorder_tbl_ptr, &priv->rx_reorder_tbl_ptr, | ||
875 | list) { | ||
876 | rx_reo_tbl->tid = (u16) rx_reorder_tbl_ptr->tid; | ||
877 | memcpy(rx_reo_tbl->ta, rx_reorder_tbl_ptr->ta, ETH_ALEN); | ||
878 | rx_reo_tbl->start_win = rx_reorder_tbl_ptr->start_win; | ||
879 | rx_reo_tbl->win_size = rx_reorder_tbl_ptr->win_size; | ||
880 | for (i = 0; i < rx_reorder_tbl_ptr->win_size; ++i) { | ||
881 | if (rx_reorder_tbl_ptr->rx_reorder_ptr[i]) | ||
882 | rx_reo_tbl->buffer[i] = true; | ||
883 | else | ||
884 | rx_reo_tbl->buffer[i] = false; | ||
885 | } | ||
886 | rx_reo_tbl++; | ||
887 | count++; | ||
888 | |||
889 | if (count >= MWIFIEX_MAX_RX_BASTREAM_SUPPORTED) | ||
890 | break; | ||
891 | } | ||
892 | spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, flags); | ||
893 | |||
894 | return count; | ||
895 | } | ||
896 | |||
897 | /* | ||
898 | * This function retrieves the Tx BA stream table. | ||
899 | */ | ||
900 | int mwifiex_get_tx_ba_stream_tbl(struct mwifiex_private *priv, | ||
901 | struct mwifiex_ds_tx_ba_stream_tbl *buf) | ||
902 | { | ||
903 | struct mwifiex_tx_ba_stream_tbl *tx_ba_tsr_tbl; | ||
904 | struct mwifiex_ds_tx_ba_stream_tbl *rx_reo_tbl = buf; | ||
905 | int count = 0; | ||
906 | unsigned long flags; | ||
907 | |||
908 | spin_lock_irqsave(&priv->tx_ba_stream_tbl_lock, flags); | ||
909 | list_for_each_entry(tx_ba_tsr_tbl, &priv->tx_ba_stream_tbl_ptr, list) { | ||
910 | rx_reo_tbl->tid = (u16) tx_ba_tsr_tbl->tid; | ||
911 | dev_dbg(priv->adapter->dev, "data: %s tid=%d\n", | ||
912 | __func__, rx_reo_tbl->tid); | ||
913 | memcpy(rx_reo_tbl->ra, tx_ba_tsr_tbl->ra, ETH_ALEN); | ||
914 | rx_reo_tbl++; | ||
915 | count++; | ||
916 | if (count >= MWIFIEX_MAX_TX_BASTREAM_SUPPORTED) | ||
917 | break; | ||
918 | } | ||
919 | spin_unlock_irqrestore(&priv->tx_ba_stream_tbl_lock, flags); | ||
920 | |||
921 | return count; | ||
922 | } | ||
diff --git a/drivers/net/wireless/mwifiex/11n.h b/drivers/net/wireless/mwifiex/11n.h new file mode 100644 index 000000000000..769a27f2b2c3 --- /dev/null +++ b/drivers/net/wireless/mwifiex/11n.h | |||
@@ -0,0 +1,178 @@ | |||
1 | /* | ||
2 | * Marvell Wireless LAN device driver: 802.11n | ||
3 | * | ||
4 | * Copyright (C) 2011, Marvell International Ltd. | ||
5 | * | ||
6 | * This software file (the "File") is distributed by Marvell International | ||
7 | * Ltd. under the terms of the GNU General Public License Version 2, June 1991 | ||
8 | * (the "License"). You may use, redistribute and/or modify this File in | ||
9 | * accordance with the terms and conditions of the License, a copy of which | ||
10 | * is available by writing to the Free Software Foundation, Inc., | ||
11 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the | ||
12 | * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. | ||
13 | * | ||
14 | * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE | ||
15 | * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE | ||
16 | * ARE EXPRESSLY DISCLAIMED. The License provides additional details about | ||
17 | * this warranty disclaimer. | ||
18 | */ | ||
19 | |||
20 | #ifndef _MWIFIEX_11N_H_ | ||
21 | #define _MWIFIEX_11N_H_ | ||
22 | |||
23 | #include "11n_aggr.h" | ||
24 | #include "11n_rxreorder.h" | ||
25 | #include "wmm.h" | ||
26 | |||
27 | void mwifiex_show_dot_11n_dev_cap(struct mwifiex_adapter *adapter, u32 cap); | ||
28 | void mwifiex_show_dev_mcs_support(struct mwifiex_adapter *adapter, u8 support); | ||
29 | int mwifiex_ret_11n_delba(struct mwifiex_private *priv, | ||
30 | struct host_cmd_ds_command *resp); | ||
31 | int mwifiex_ret_11n_addba_req(struct mwifiex_private *priv, | ||
32 | struct host_cmd_ds_command *resp); | ||
33 | int mwifiex_ret_11n_cfg(struct mwifiex_private *priv, | ||
34 | struct host_cmd_ds_command *resp, | ||
35 | void *data_buf); | ||
36 | int mwifiex_cmd_11n_cfg(struct mwifiex_private *priv, | ||
37 | struct host_cmd_ds_command *cmd, | ||
38 | u16 cmd_action, void *data_buf); | ||
39 | |||
40 | int mwifiex_cmd_11n_cfg(struct mwifiex_private *priv, | ||
41 | struct host_cmd_ds_command *cmd, | ||
42 | u16 cmd_action, void *data_buf); | ||
43 | |||
44 | int mwifiex_cmd_append_11n_tlv(struct mwifiex_private *priv, | ||
45 | struct mwifiex_bssdescriptor *bss_desc, | ||
46 | u8 **buffer); | ||
47 | void mwifiex_cfg_tx_buf(struct mwifiex_private *priv, | ||
48 | struct mwifiex_bssdescriptor *bss_desc); | ||
49 | void mwifiex_fill_cap_info(struct mwifiex_private *, | ||
50 | struct mwifiex_ie_types_htcap *); | ||
51 | int mwifiex_set_get_11n_htcap_cfg(struct mwifiex_private *priv, | ||
52 | u16 action, int *htcap_cfg); | ||
53 | void mwifiex_11n_delete_tx_ba_stream_tbl_entry(struct mwifiex_private *priv, | ||
54 | struct mwifiex_tx_ba_stream_tbl | ||
55 | *tx_tbl); | ||
56 | void mwifiex_11n_delete_all_tx_ba_stream_tbl(struct mwifiex_private *priv); | ||
57 | struct mwifiex_tx_ba_stream_tbl *mwifiex_11n_get_tx_ba_stream_tbl(struct | ||
58 | mwifiex_private | ||
59 | *priv, int tid, | ||
60 | u8 *ra); | ||
61 | void mwifiex_11n_create_tx_ba_stream_tbl(struct mwifiex_private *priv, u8 *ra, | ||
62 | int tid, | ||
63 | enum mwifiex_ba_status ba_status); | ||
64 | int mwifiex_send_addba(struct mwifiex_private *priv, int tid, u8 *peer_mac); | ||
65 | int mwifiex_send_delba(struct mwifiex_private *priv, int tid, u8 *peer_mac, | ||
66 | int initiator); | ||
67 | void mwifiex_11n_delete_ba_stream(struct mwifiex_private *priv, u8 *del_ba); | ||
68 | int mwifiex_get_rx_reorder_tbl(struct mwifiex_private *priv, | ||
69 | struct mwifiex_ds_rx_reorder_tbl *buf); | ||
70 | int mwifiex_get_tx_ba_stream_tbl(struct mwifiex_private *priv, | ||
71 | struct mwifiex_ds_tx_ba_stream_tbl *buf); | ||
72 | int mwifiex_ret_amsdu_aggr_ctrl(struct mwifiex_private *priv, | ||
73 | struct host_cmd_ds_command | ||
74 | *resp, | ||
75 | void *data_buf); | ||
76 | int mwifiex_cmd_recfg_tx_buf(struct mwifiex_private *priv, | ||
77 | struct host_cmd_ds_command *cmd, | ||
78 | int cmd_action, void *data_buf); | ||
79 | int mwifiex_cmd_amsdu_aggr_ctrl(struct mwifiex_private *priv, | ||
80 | struct host_cmd_ds_command *cmd, | ||
81 | int cmd_action, | ||
82 | void *data_buf); | ||
83 | |||
84 | /* | ||
85 | * This function checks whether AMPDU is allowed or not for a particular TID. | ||
86 | */ | ||
87 | static inline u8 | ||
88 | mwifiex_is_ampdu_allowed(struct mwifiex_private *priv, | ||
89 | struct mwifiex_ra_list_tbl *ptr, int tid) | ||
90 | { | ||
91 | return ((priv->aggr_prio_tbl[tid].ampdu_ap != BA_STREAM_NOT_ALLOWED) | ||
92 | ? true : false); | ||
93 | } | ||
94 | |||
95 | /* | ||
96 | * This function checks whether AMSDU is allowed or not for a particular TID. | ||
97 | */ | ||
98 | static inline u8 | ||
99 | mwifiex_is_amsdu_allowed(struct mwifiex_private *priv, | ||
100 | struct mwifiex_ra_list_tbl *ptr, int tid) | ||
101 | { | ||
102 | return (((priv->aggr_prio_tbl[tid].amsdu != BA_STREAM_NOT_ALLOWED) | ||
103 | && ((priv->is_data_rate_auto) | ||
104 | || !((priv->bitmap_rates[2]) & 0x03))) | ||
105 | ? true : false); | ||
106 | } | ||
107 | |||
108 | /* | ||
109 | * This function checks whether a BA stream is available or not. | ||
110 | */ | ||
111 | static inline u8 | ||
112 | mwifiex_is_ba_stream_avail(struct mwifiex_private *priv) | ||
113 | { | ||
114 | struct mwifiex_private *pmpriv = NULL; | ||
115 | u8 i = 0; | ||
116 | u32 ba_stream_num = 0; | ||
117 | |||
118 | for (i = 0; i < priv->adapter->priv_num; i++) { | ||
119 | pmpriv = priv->adapter->priv[i]; | ||
120 | if (pmpriv) | ||
121 | ba_stream_num += | ||
122 | mwifiex_wmm_list_len(priv->adapter, | ||
123 | (struct list_head | ||
124 | *) &pmpriv-> | ||
125 | tx_ba_stream_tbl_ptr); | ||
126 | } | ||
127 | |||
128 | return ((ba_stream_num < | ||
129 | MWIFIEX_MAX_TX_BASTREAM_SUPPORTED) ? true : false); | ||
130 | } | ||
131 | |||
132 | /* | ||
133 | * This function finds the correct Tx BA stream to delete. | ||
134 | * | ||
135 | * Upon successfully locating, both the TID and the RA are returned. | ||
136 | */ | ||
137 | static inline u8 | ||
138 | mwifiex_find_stream_to_delete(struct mwifiex_private *priv, | ||
139 | struct mwifiex_ra_list_tbl *ptr, int ptr_tid, | ||
140 | int *ptid, u8 *ra) | ||
141 | { | ||
142 | int tid; | ||
143 | u8 ret = false; | ||
144 | struct mwifiex_tx_ba_stream_tbl *tx_tbl; | ||
145 | unsigned long flags; | ||
146 | |||
147 | tid = priv->aggr_prio_tbl[ptr_tid].ampdu_user; | ||
148 | |||
149 | spin_lock_irqsave(&priv->tx_ba_stream_tbl_lock, flags); | ||
150 | list_for_each_entry(tx_tbl, &priv->tx_ba_stream_tbl_ptr, list) { | ||
151 | if (tid > priv->aggr_prio_tbl[tx_tbl->tid].ampdu_user) { | ||
152 | tid = priv->aggr_prio_tbl[tx_tbl->tid].ampdu_user; | ||
153 | *ptid = tx_tbl->tid; | ||
154 | memcpy(ra, tx_tbl->ra, ETH_ALEN); | ||
155 | ret = true; | ||
156 | } | ||
157 | } | ||
158 | spin_unlock_irqrestore(&priv->tx_ba_stream_tbl_lock, flags); | ||
159 | |||
160 | return ret; | ||
161 | } | ||
162 | |||
163 | /* | ||
164 | * This function checks whether BA stream is set up or not. | ||
165 | */ | ||
166 | static inline int | ||
167 | mwifiex_is_ba_stream_setup(struct mwifiex_private *priv, | ||
168 | struct mwifiex_ra_list_tbl *ptr, int tid) | ||
169 | { | ||
170 | struct mwifiex_tx_ba_stream_tbl *tx_tbl; | ||
171 | |||
172 | tx_tbl = mwifiex_11n_get_tx_ba_stream_tbl(priv, tid, ptr->ra); | ||
173 | if (tx_tbl && IS_BASTREAM_SETUP(tx_tbl)) | ||
174 | return true; | ||
175 | |||
176 | return false; | ||
177 | } | ||
178 | #endif /* !_MWIFIEX_11N_H_ */ | ||
diff --git a/drivers/net/wireless/mwifiex/11n_aggr.c b/drivers/net/wireless/mwifiex/11n_aggr.c new file mode 100644 index 000000000000..c2abced66957 --- /dev/null +++ b/drivers/net/wireless/mwifiex/11n_aggr.c | |||
@@ -0,0 +1,423 @@ | |||
1 | /* | ||
2 | * Marvell Wireless LAN device driver: 802.11n Aggregation | ||
3 | * | ||
4 | * Copyright (C) 2011, Marvell International Ltd. | ||
5 | * | ||
6 | * This software file (the "File") is distributed by Marvell International | ||
7 | * Ltd. under the terms of the GNU General Public License Version 2, June 1991 | ||
8 | * (the "License"). You may use, redistribute and/or modify this File in | ||
9 | * accordance with the terms and conditions of the License, a copy of which | ||
10 | * is available by writing to the Free Software Foundation, Inc., | ||
11 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the | ||
12 | * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. | ||
13 | * | ||
14 | * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE | ||
15 | * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE | ||
16 | * ARE EXPRESSLY DISCLAIMED. The License provides additional details about | ||
17 | * this warranty disclaimer. | ||
18 | */ | ||
19 | |||
20 | #include "decl.h" | ||
21 | #include "ioctl.h" | ||
22 | #include "util.h" | ||
23 | #include "fw.h" | ||
24 | #include "main.h" | ||
25 | #include "wmm.h" | ||
26 | #include "11n.h" | ||
27 | #include "11n_aggr.h" | ||
28 | |||
29 | /* | ||
30 | * Creates an AMSDU subframe for aggregation into one AMSDU packet. | ||
31 | * | ||
32 | * The resultant AMSDU subframe format is - | ||
33 | * | ||
34 | * +---- ~ -----+---- ~ ------+---- ~ -----+----- ~ -----+---- ~ -----+ | ||
35 | * | DA | SA | Length | SNAP header | MSDU | | ||
36 | * | data[0..5] | data[6..11] | | | data[14..] | | ||
37 | * +---- ~ -----+---- ~ ------+---- ~ -----+----- ~ -----+---- ~ -----+ | ||
38 | * <--6-bytes--> <--6-bytes--> <--2-bytes--><--8-bytes--> <--n-bytes--> | ||
39 | * | ||
40 | * This function also computes the amount of padding required to make the | ||
41 | * buffer length multiple of 4 bytes. | ||
42 | * | ||
43 | * Data => |DA|SA|SNAP-TYPE|........ .| | ||
44 | * MSDU => |DA|SA|Length|SNAP|...... ..| | ||
45 | */ | ||
46 | static int | ||
47 | mwifiex_11n_form_amsdu_pkt(struct mwifiex_adapter *adapter, | ||
48 | struct sk_buff *skb_aggr, | ||
49 | struct sk_buff *skb_src, int *pad) | ||
50 | |||
51 | { | ||
52 | int dt_offset; | ||
53 | struct rfc_1042_hdr snap = { | ||
54 | 0xaa, /* LLC DSAP */ | ||
55 | 0xaa, /* LLC SSAP */ | ||
56 | 0x03, /* LLC CTRL */ | ||
57 | {0x00, 0x00, 0x00}, /* SNAP OUI */ | ||
58 | 0x0000 /* SNAP type */ | ||
59 | /* | ||
60 | * This field will be overwritten | ||
61 | * later with ethertype | ||
62 | */ | ||
63 | }; | ||
64 | struct tx_packet_hdr *tx_header = NULL; | ||
65 | |||
66 | skb_put(skb_aggr, sizeof(*tx_header)); | ||
67 | |||
68 | tx_header = (struct tx_packet_hdr *) skb_aggr->data; | ||
69 | |||
70 | /* Copy DA and SA */ | ||
71 | dt_offset = 2 * ETH_ALEN; | ||
72 | memcpy(&tx_header->eth803_hdr, skb_src->data, dt_offset); | ||
73 | |||
74 | /* Copy SNAP header */ | ||
75 | snap.snap_type = *(u16 *) ((u8 *)skb_src->data + dt_offset); | ||
76 | dt_offset += sizeof(u16); | ||
77 | |||
78 | memcpy(&tx_header->rfc1042_hdr, &snap, sizeof(struct rfc_1042_hdr)); | ||
79 | |||
80 | skb_pull(skb_src, dt_offset); | ||
81 | |||
82 | /* Update Length field */ | ||
83 | tx_header->eth803_hdr.h_proto = htons(skb_src->len + LLC_SNAP_LEN); | ||
84 | |||
85 | /* Add payload */ | ||
86 | skb_put(skb_aggr, skb_src->len); | ||
87 | memcpy(skb_aggr->data + sizeof(*tx_header), skb_src->data, | ||
88 | skb_src->len); | ||
89 | *pad = (((skb_src->len + LLC_SNAP_LEN) & 3)) ? (4 - (((skb_src->len + | ||
90 | LLC_SNAP_LEN)) & 3)) : 0; | ||
91 | skb_put(skb_aggr, *pad); | ||
92 | |||
93 | return skb_aggr->len + *pad; | ||
94 | } | ||
95 | |||
96 | /* | ||
97 | * Adds TxPD to AMSDU header. | ||
98 | * | ||
99 | * Each AMSDU packet will contain one TxPD at the beginning, | ||
100 | * followed by multiple AMSDU subframes. | ||
101 | */ | ||
102 | static void | ||
103 | mwifiex_11n_form_amsdu_txpd(struct mwifiex_private *priv, | ||
104 | struct sk_buff *skb) | ||
105 | { | ||
106 | struct txpd *local_tx_pd; | ||
107 | |||
108 | skb_push(skb, sizeof(*local_tx_pd)); | ||
109 | |||
110 | local_tx_pd = (struct txpd *) skb->data; | ||
111 | memset(local_tx_pd, 0, sizeof(struct txpd)); | ||
112 | |||
113 | /* Original priority has been overwritten */ | ||
114 | local_tx_pd->priority = (u8) skb->priority; | ||
115 | local_tx_pd->pkt_delay_2ms = | ||
116 | mwifiex_wmm_compute_drv_pkt_delay(priv, skb); | ||
117 | local_tx_pd->bss_num = priv->bss_num; | ||
118 | local_tx_pd->bss_type = priv->bss_type; | ||
119 | /* Always zero as the data is followed by struct txpd */ | ||
120 | local_tx_pd->tx_pkt_offset = cpu_to_le16(sizeof(struct txpd)); | ||
121 | local_tx_pd->tx_pkt_type = cpu_to_le16(PKT_TYPE_AMSDU); | ||
122 | local_tx_pd->tx_pkt_length = cpu_to_le16(skb->len - | ||
123 | sizeof(*local_tx_pd)); | ||
124 | |||
125 | if (local_tx_pd->tx_control == 0) | ||
126 | /* TxCtrl set by user or default */ | ||
127 | local_tx_pd->tx_control = cpu_to_le32(priv->pkt_tx_ctrl); | ||
128 | |||
129 | if ((GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA) && | ||
130 | (priv->adapter->pps_uapsd_mode)) { | ||
131 | if (true == mwifiex_check_last_packet_indication(priv)) { | ||
132 | priv->adapter->tx_lock_flag = true; | ||
133 | local_tx_pd->flags = | ||
134 | MWIFIEX_TxPD_POWER_MGMT_LAST_PACKET; | ||
135 | } | ||
136 | } | ||
137 | } | ||
138 | |||
139 | /* | ||
140 | * Counts the number of subframes in an aggregate packet. | ||
141 | * | ||
142 | * This function parses an aggregate packet buffer, looking for | ||
143 | * subframes and counting the number of such subframe found. The | ||
144 | * function automatically skips the DA/SA fields at the beginning | ||
145 | * of each subframe and padding at the end. | ||
146 | */ | ||
147 | static int | ||
148 | mwifiex_11n_get_num_aggr_pkts(u8 *data, int total_pkt_len) | ||
149 | { | ||
150 | int pkt_count = 0, pkt_len, pad; | ||
151 | |||
152 | while (total_pkt_len > 0) { | ||
153 | /* Length will be in network format, change it to host */ | ||
154 | pkt_len = ntohs((*(__be16 *)(data + 2 * ETH_ALEN))); | ||
155 | pad = (((pkt_len + sizeof(struct ethhdr)) & 3)) ? | ||
156 | (4 - ((pkt_len + sizeof(struct ethhdr)) & 3)) : 0; | ||
157 | data += pkt_len + pad + sizeof(struct ethhdr); | ||
158 | total_pkt_len -= pkt_len + pad + sizeof(struct ethhdr); | ||
159 | ++pkt_count; | ||
160 | } | ||
161 | |||
162 | return pkt_count; | ||
163 | } | ||
164 | |||
165 | /* | ||
166 | * De-aggregate received packets. | ||
167 | * | ||
168 | * This function parses the received aggregate buffer, extracts each subframe, | ||
169 | * strips off the SNAP header from them and sends the data portion for further | ||
170 | * processing. | ||
171 | * | ||
172 | * Each subframe body is copied onto a separate buffer, which are freed by | ||
173 | * upper layer after processing. The function also performs sanity tests on | ||
174 | * the received buffer. | ||
175 | */ | ||
176 | int mwifiex_11n_deaggregate_pkt(struct mwifiex_private *priv, | ||
177 | struct sk_buff *skb) | ||
178 | { | ||
179 | u16 pkt_len; | ||
180 | int total_pkt_len; | ||
181 | u8 *data; | ||
182 | int pad; | ||
183 | struct mwifiex_rxinfo *rx_info = MWIFIEX_SKB_RXCB(skb); | ||
184 | struct rxpd *local_rx_pd = (struct rxpd *) skb->data; | ||
185 | struct sk_buff *skb_daggr; | ||
186 | struct mwifiex_rxinfo *rx_info_daggr = NULL; | ||
187 | int ret = -1; | ||
188 | struct rx_packet_hdr *rx_pkt_hdr; | ||
189 | struct mwifiex_adapter *adapter = priv->adapter; | ||
190 | u8 rfc1042_eth_hdr[ETH_ALEN] = { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00}; | ||
191 | |||
192 | data = (u8 *) (local_rx_pd + local_rx_pd->rx_pkt_offset); | ||
193 | total_pkt_len = local_rx_pd->rx_pkt_length; | ||
194 | |||
195 | /* Sanity test */ | ||
196 | if (total_pkt_len > MWIFIEX_RX_DATA_BUF_SIZE) { | ||
197 | dev_err(adapter->dev, "total pkt len greater than buffer" | ||
198 | " size %d\n", total_pkt_len); | ||
199 | return -1; | ||
200 | } | ||
201 | |||
202 | rx_info->use_count = mwifiex_11n_get_num_aggr_pkts(data, total_pkt_len); | ||
203 | |||
204 | while (total_pkt_len > 0) { | ||
205 | rx_pkt_hdr = (struct rx_packet_hdr *) data; | ||
206 | /* Length will be in network format, change it to host */ | ||
207 | pkt_len = ntohs((*(__be16 *) (data + 2 * ETH_ALEN))); | ||
208 | if (pkt_len > total_pkt_len) { | ||
209 | dev_err(adapter->dev, "pkt_len %d > total_pkt_len %d\n", | ||
210 | total_pkt_len, pkt_len); | ||
211 | break; | ||
212 | } | ||
213 | |||
214 | pad = (((pkt_len + sizeof(struct ethhdr)) & 3)) ? | ||
215 | (4 - ((pkt_len + sizeof(struct ethhdr)) & 3)) : 0; | ||
216 | |||
217 | total_pkt_len -= pkt_len + pad + sizeof(struct ethhdr); | ||
218 | |||
219 | if (memcmp(&rx_pkt_hdr->rfc1042_hdr, | ||
220 | rfc1042_eth_hdr, sizeof(rfc1042_eth_hdr)) == 0) { | ||
221 | memmove(data + LLC_SNAP_LEN, data, 2 * ETH_ALEN); | ||
222 | data += LLC_SNAP_LEN; | ||
223 | pkt_len += sizeof(struct ethhdr) - LLC_SNAP_LEN; | ||
224 | } else { | ||
225 | *(u16 *) (data + 2 * ETH_ALEN) = (u16) 0; | ||
226 | pkt_len += sizeof(struct ethhdr); | ||
227 | } | ||
228 | |||
229 | skb_daggr = dev_alloc_skb(pkt_len); | ||
230 | if (!skb_daggr) { | ||
231 | dev_err(adapter->dev, "%s: failed to alloc skb_daggr\n", | ||
232 | __func__); | ||
233 | return -1; | ||
234 | } | ||
235 | rx_info_daggr = MWIFIEX_SKB_RXCB(skb_daggr); | ||
236 | |||
237 | rx_info_daggr->bss_index = rx_info->bss_index; | ||
238 | skb_daggr->tstamp = skb->tstamp; | ||
239 | rx_info_daggr->parent = skb; | ||
240 | skb_daggr->priority = skb->priority; | ||
241 | skb_put(skb_daggr, pkt_len); | ||
242 | memcpy(skb_daggr->data, data, pkt_len); | ||
243 | |||
244 | ret = mwifiex_recv_packet(adapter, skb_daggr); | ||
245 | |||
246 | switch (ret) { | ||
247 | case -EINPROGRESS: | ||
248 | break; | ||
249 | case -1: | ||
250 | dev_err(adapter->dev, "deaggr: host_to_card failed\n"); | ||
251 | case 0: | ||
252 | mwifiex_recv_packet_complete(adapter, skb_daggr, ret); | ||
253 | break; | ||
254 | default: | ||
255 | break; | ||
256 | } | ||
257 | |||
258 | data += pkt_len + pad; | ||
259 | } | ||
260 | |||
261 | return ret; | ||
262 | } | ||
263 | |||
264 | /* | ||
265 | * Create aggregated packet. | ||
266 | * | ||
267 | * This function creates an aggregated MSDU packet, by combining buffers | ||
268 | * from the RA list. Each individual buffer is encapsulated as an AMSDU | ||
269 | * subframe and all such subframes are concatenated together to form the | ||
270 | * AMSDU packet. | ||
271 | * | ||
272 | * A TxPD is also added to the front of the resultant AMSDU packets for | ||
273 | * transmission. The resultant packets format is - | ||
274 | * | ||
275 | * +---- ~ ----+------ ~ ------+------ ~ ------+-..-+------ ~ ------+ | ||
276 | * | TxPD |AMSDU sub-frame|AMSDU sub-frame| .. |AMSDU sub-frame| | ||
277 | * | | 1 | 2 | .. | n | | ||
278 | * +---- ~ ----+------ ~ ------+------ ~ ------+ .. +------ ~ ------+ | ||
279 | */ | ||
280 | int | ||
281 | mwifiex_11n_aggregate_pkt(struct mwifiex_private *priv, | ||
282 | struct mwifiex_ra_list_tbl *pra_list, int headroom, | ||
283 | int ptrindex, unsigned long ra_list_flags) | ||
284 | __releases(&priv->wmm.ra_list_spinlock) | ||
285 | { | ||
286 | struct mwifiex_adapter *adapter = priv->adapter; | ||
287 | struct sk_buff *skb_aggr, *skb_src; | ||
288 | struct mwifiex_txinfo *tx_info_aggr, *tx_info_src; | ||
289 | int pad = 0; | ||
290 | int ret = 0; | ||
291 | struct mwifiex_tx_param tx_param; | ||
292 | struct txpd *ptx_pd = NULL; | ||
293 | |||
294 | if (skb_queue_empty(&pra_list->skb_head)) { | ||
295 | spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, | ||
296 | ra_list_flags); | ||
297 | return 0; | ||
298 | } | ||
299 | skb_src = skb_peek(&pra_list->skb_head); | ||
300 | tx_info_src = MWIFIEX_SKB_TXCB(skb_src); | ||
301 | skb_aggr = dev_alloc_skb(adapter->tx_buf_size); | ||
302 | if (!skb_aggr) { | ||
303 | dev_err(adapter->dev, "%s: alloc skb_aggr\n", __func__); | ||
304 | spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, | ||
305 | ra_list_flags); | ||
306 | return -1; | ||
307 | } | ||
308 | skb_reserve(skb_aggr, headroom + sizeof(struct txpd)); | ||
309 | tx_info_aggr = MWIFIEX_SKB_TXCB(skb_aggr); | ||
310 | |||
311 | tx_info_aggr->bss_index = tx_info_src->bss_index; | ||
312 | skb_aggr->priority = skb_src->priority; | ||
313 | |||
314 | while (skb_src && ((skb_headroom(skb_aggr) + skb_src->len | ||
315 | + LLC_SNAP_LEN) | ||
316 | <= adapter->tx_buf_size)) { | ||
317 | |||
318 | if (!skb_queue_empty(&pra_list->skb_head)) | ||
319 | skb_src = skb_dequeue(&pra_list->skb_head); | ||
320 | else | ||
321 | skb_src = NULL; | ||
322 | |||
323 | pra_list->total_pkts_size -= skb_src->len; | ||
324 | |||
325 | spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, | ||
326 | ra_list_flags); | ||
327 | mwifiex_11n_form_amsdu_pkt(adapter, skb_aggr, skb_src, &pad); | ||
328 | |||
329 | mwifiex_write_data_complete(adapter, skb_src, 0); | ||
330 | |||
331 | spin_lock_irqsave(&priv->wmm.ra_list_spinlock, ra_list_flags); | ||
332 | |||
333 | if (!mwifiex_is_ralist_valid(priv, pra_list, ptrindex)) { | ||
334 | spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, | ||
335 | ra_list_flags); | ||
336 | return -1; | ||
337 | } | ||
338 | |||
339 | if (!skb_queue_empty(&pra_list->skb_head)) | ||
340 | skb_src = skb_peek(&pra_list->skb_head); | ||
341 | else | ||
342 | skb_src = NULL; | ||
343 | } | ||
344 | |||
345 | spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, ra_list_flags); | ||
346 | |||
347 | /* Last AMSDU packet does not need padding */ | ||
348 | skb_trim(skb_aggr, skb_aggr->len - pad); | ||
349 | |||
350 | /* Form AMSDU */ | ||
351 | mwifiex_11n_form_amsdu_txpd(priv, skb_aggr); | ||
352 | if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA) | ||
353 | ptx_pd = (struct txpd *)skb_aggr->data; | ||
354 | |||
355 | skb_push(skb_aggr, headroom); | ||
356 | |||
357 | tx_param.next_pkt_len = ((pra_list->total_pkts_size) ? | ||
358 | (((pra_list->total_pkts_size) > | ||
359 | adapter->tx_buf_size) ? adapter-> | ||
360 | tx_buf_size : pra_list->total_pkts_size + | ||
361 | LLC_SNAP_LEN + sizeof(struct txpd)) : 0); | ||
362 | ret = adapter->if_ops.host_to_card(adapter, MWIFIEX_TYPE_DATA, | ||
363 | skb_aggr->data, | ||
364 | skb_aggr->len, &tx_param); | ||
365 | switch (ret) { | ||
366 | case -EBUSY: | ||
367 | spin_lock_irqsave(&priv->wmm.ra_list_spinlock, ra_list_flags); | ||
368 | if (!mwifiex_is_ralist_valid(priv, pra_list, ptrindex)) { | ||
369 | spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, | ||
370 | ra_list_flags); | ||
371 | mwifiex_write_data_complete(adapter, skb_aggr, -1); | ||
372 | return -1; | ||
373 | } | ||
374 | if ((GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA) && | ||
375 | (adapter->pps_uapsd_mode) && | ||
376 | (adapter->tx_lock_flag)) { | ||
377 | priv->adapter->tx_lock_flag = false; | ||
378 | ptx_pd->flags = 0; | ||
379 | } | ||
380 | |||
381 | skb_queue_tail(&pra_list->skb_head, skb_aggr); | ||
382 | |||
383 | pra_list->total_pkts_size += skb_aggr->len; | ||
384 | |||
385 | tx_info_aggr->flags |= MWIFIEX_BUF_FLAG_REQUEUED_PKT; | ||
386 | spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, | ||
387 | ra_list_flags); | ||
388 | dev_dbg(adapter->dev, "data: -EBUSY is returned\n"); | ||
389 | break; | ||
390 | case -1: | ||
391 | adapter->data_sent = false; | ||
392 | dev_err(adapter->dev, "%s: host_to_card failed: %#x\n", | ||
393 | __func__, ret); | ||
394 | adapter->dbg.num_tx_host_to_card_failure++; | ||
395 | mwifiex_write_data_complete(adapter, skb_aggr, ret); | ||
396 | return 0; | ||
397 | case -EINPROGRESS: | ||
398 | adapter->data_sent = false; | ||
399 | break; | ||
400 | case 0: | ||
401 | mwifiex_write_data_complete(adapter, skb_aggr, ret); | ||
402 | break; | ||
403 | default: | ||
404 | break; | ||
405 | } | ||
406 | if (ret != -EBUSY) { | ||
407 | spin_lock_irqsave(&priv->wmm.ra_list_spinlock, ra_list_flags); | ||
408 | if (mwifiex_is_ralist_valid(priv, pra_list, ptrindex)) { | ||
409 | priv->wmm.packets_out[ptrindex]++; | ||
410 | priv->wmm.tid_tbl_ptr[ptrindex].ra_list_curr = pra_list; | ||
411 | } | ||
412 | /* Now bss_prio_cur pointer points to next node */ | ||
413 | adapter->bss_prio_tbl[priv->bss_priority].bss_prio_cur = | ||
414 | list_first_entry( | ||
415 | &adapter->bss_prio_tbl[priv->bss_priority] | ||
416 | .bss_prio_cur->list, | ||
417 | struct mwifiex_bss_prio_node, list); | ||
418 | spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, | ||
419 | ra_list_flags); | ||
420 | } | ||
421 | |||
422 | return 0; | ||
423 | } | ||
diff --git a/drivers/net/wireless/mwifiex/11n_aggr.h b/drivers/net/wireless/mwifiex/11n_aggr.h new file mode 100644 index 000000000000..9c6dca7ab02c --- /dev/null +++ b/drivers/net/wireless/mwifiex/11n_aggr.h | |||
@@ -0,0 +1,32 @@ | |||
1 | /* | ||
2 | * Marvell Wireless LAN device driver: 802.11n Aggregation | ||
3 | * | ||
4 | * Copyright (C) 2011, Marvell International Ltd. | ||
5 | * | ||
6 | * This software file (the "File") is distributed by Marvell International | ||
7 | * Ltd. under the terms of the GNU General Public License Version 2, June 1991 | ||
8 | * (the "License"). You may use, redistribute and/or modify this File in | ||
9 | * accordance with the terms and conditions of the License, a copy of which | ||
10 | * is available by writing to the Free Software Foundation, Inc., | ||
11 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the | ||
12 | * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. | ||
13 | * | ||
14 | * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE | ||
15 | * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE | ||
16 | * ARE EXPRESSLY DISCLAIMED. The License provides additional details about | ||
17 | * this warranty disclaimer. | ||
18 | */ | ||
19 | |||
20 | #ifndef _MWIFIEX_11N_AGGR_H_ | ||
21 | #define _MWIFIEX_11N_AGGR_H_ | ||
22 | |||
23 | #define PKT_TYPE_AMSDU 0xE6 | ||
24 | |||
25 | int mwifiex_11n_deaggregate_pkt(struct mwifiex_private *priv, | ||
26 | struct sk_buff *skb); | ||
27 | int mwifiex_11n_aggregate_pkt(struct mwifiex_private *priv, | ||
28 | struct mwifiex_ra_list_tbl *ptr, int headroom, | ||
29 | int ptr_index, unsigned long flags) | ||
30 | __releases(&priv->wmm.ra_list_spinlock); | ||
31 | |||
32 | #endif /* !_MWIFIEX_11N_AGGR_H_ */ | ||
diff --git a/drivers/net/wireless/mwifiex/11n_rxreorder.c b/drivers/net/wireless/mwifiex/11n_rxreorder.c new file mode 100644 index 000000000000..8e94e620e6f4 --- /dev/null +++ b/drivers/net/wireless/mwifiex/11n_rxreorder.c | |||
@@ -0,0 +1,637 @@ | |||
1 | /* | ||
2 | * Marvell Wireless LAN device driver: 802.11n RX Re-ordering | ||
3 | * | ||
4 | * Copyright (C) 2011, Marvell International Ltd. | ||
5 | * | ||
6 | * This software file (the "File") is distributed by Marvell International | ||
7 | * Ltd. under the terms of the GNU General Public License Version 2, June 1991 | ||
8 | * (the "License"). You may use, redistribute and/or modify this File in | ||
9 | * accordance with the terms and conditions of the License, a copy of which | ||
10 | * is available by writing to the Free Software Foundation, Inc., | ||
11 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the | ||
12 | * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. | ||
13 | * | ||
14 | * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE | ||
15 | * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE | ||
16 | * ARE EXPRESSLY DISCLAIMED. The License provides additional details about | ||
17 | * this warranty disclaimer. | ||
18 | */ | ||
19 | |||
20 | #include "decl.h" | ||
21 | #include "ioctl.h" | ||
22 | #include "util.h" | ||
23 | #include "fw.h" | ||
24 | #include "main.h" | ||
25 | #include "wmm.h" | ||
26 | #include "11n.h" | ||
27 | #include "11n_rxreorder.h" | ||
28 | |||
29 | /* | ||
30 | * This function processes a received packet and forwards | ||
31 | * it to the kernel/upper layer. | ||
32 | */ | ||
33 | static int mwifiex_11n_dispatch_pkt(struct mwifiex_private *priv, void *payload) | ||
34 | { | ||
35 | int ret = 0; | ||
36 | struct mwifiex_adapter *adapter = priv->adapter; | ||
37 | |||
38 | ret = mwifiex_process_rx_packet(adapter, (struct sk_buff *) payload); | ||
39 | return ret; | ||
40 | } | ||
41 | |||
42 | /* | ||
43 | * This function dispatches all packets in the Rx reorder table. | ||
44 | * | ||
45 | * There could be holes in the buffer, which are skipped by the function. | ||
46 | * Since the buffer is linear, the function uses rotation to simulate | ||
47 | * circular buffer. | ||
48 | */ | ||
49 | static int | ||
50 | mwifiex_11n_dispatch_pkt_until_start_win(struct mwifiex_private *priv, | ||
51 | struct mwifiex_rx_reorder_tbl | ||
52 | *rx_reor_tbl_ptr, int start_win) | ||
53 | { | ||
54 | int no_pkt_to_send, i, xchg; | ||
55 | void *rx_tmp_ptr = NULL; | ||
56 | unsigned long flags; | ||
57 | |||
58 | no_pkt_to_send = (start_win > rx_reor_tbl_ptr->start_win) ? | ||
59 | min((start_win - rx_reor_tbl_ptr->start_win), | ||
60 | rx_reor_tbl_ptr->win_size) : rx_reor_tbl_ptr->win_size; | ||
61 | |||
62 | for (i = 0; i < no_pkt_to_send; ++i) { | ||
63 | spin_lock_irqsave(&priv->rx_pkt_lock, flags); | ||
64 | rx_tmp_ptr = NULL; | ||
65 | if (rx_reor_tbl_ptr->rx_reorder_ptr[i]) { | ||
66 | rx_tmp_ptr = rx_reor_tbl_ptr->rx_reorder_ptr[i]; | ||
67 | rx_reor_tbl_ptr->rx_reorder_ptr[i] = NULL; | ||
68 | } | ||
69 | spin_unlock_irqrestore(&priv->rx_pkt_lock, flags); | ||
70 | if (rx_tmp_ptr) | ||
71 | mwifiex_11n_dispatch_pkt(priv, rx_tmp_ptr); | ||
72 | } | ||
73 | |||
74 | spin_lock_irqsave(&priv->rx_pkt_lock, flags); | ||
75 | /* | ||
76 | * We don't have a circular buffer, hence use rotation to simulate | ||
77 | * circular buffer | ||
78 | */ | ||
79 | xchg = rx_reor_tbl_ptr->win_size - no_pkt_to_send; | ||
80 | for (i = 0; i < xchg; ++i) { | ||
81 | rx_reor_tbl_ptr->rx_reorder_ptr[i] = | ||
82 | rx_reor_tbl_ptr->rx_reorder_ptr[no_pkt_to_send + i]; | ||
83 | rx_reor_tbl_ptr->rx_reorder_ptr[no_pkt_to_send + i] = NULL; | ||
84 | } | ||
85 | |||
86 | rx_reor_tbl_ptr->start_win = start_win; | ||
87 | spin_unlock_irqrestore(&priv->rx_pkt_lock, flags); | ||
88 | |||
89 | return 0; | ||
90 | } | ||
91 | |||
92 | /* | ||
93 | * This function dispatches all packets in the Rx reorder table until | ||
94 | * a hole is found. | ||
95 | * | ||
96 | * The start window is adjusted automatically when a hole is located. | ||
97 | * Since the buffer is linear, the function uses rotation to simulate | ||
98 | * circular buffer. | ||
99 | */ | ||
100 | static int | ||
101 | mwifiex_11n_scan_and_dispatch(struct mwifiex_private *priv, | ||
102 | struct mwifiex_rx_reorder_tbl *rx_reor_tbl_ptr) | ||
103 | { | ||
104 | int i, j, xchg; | ||
105 | void *rx_tmp_ptr = NULL; | ||
106 | unsigned long flags; | ||
107 | |||
108 | for (i = 0; i < rx_reor_tbl_ptr->win_size; ++i) { | ||
109 | spin_lock_irqsave(&priv->rx_pkt_lock, flags); | ||
110 | if (!rx_reor_tbl_ptr->rx_reorder_ptr[i]) { | ||
111 | spin_unlock_irqrestore(&priv->rx_pkt_lock, flags); | ||
112 | break; | ||
113 | } | ||
114 | rx_tmp_ptr = rx_reor_tbl_ptr->rx_reorder_ptr[i]; | ||
115 | rx_reor_tbl_ptr->rx_reorder_ptr[i] = NULL; | ||
116 | spin_unlock_irqrestore(&priv->rx_pkt_lock, flags); | ||
117 | mwifiex_11n_dispatch_pkt(priv, rx_tmp_ptr); | ||
118 | } | ||
119 | |||
120 | spin_lock_irqsave(&priv->rx_pkt_lock, flags); | ||
121 | /* | ||
122 | * We don't have a circular buffer, hence use rotation to simulate | ||
123 | * circular buffer | ||
124 | */ | ||
125 | if (i > 0) { | ||
126 | xchg = rx_reor_tbl_ptr->win_size - i; | ||
127 | for (j = 0; j < xchg; ++j) { | ||
128 | rx_reor_tbl_ptr->rx_reorder_ptr[j] = | ||
129 | rx_reor_tbl_ptr->rx_reorder_ptr[i + j]; | ||
130 | rx_reor_tbl_ptr->rx_reorder_ptr[i + j] = NULL; | ||
131 | } | ||
132 | } | ||
133 | rx_reor_tbl_ptr->start_win = (rx_reor_tbl_ptr->start_win + i) | ||
134 | &(MAX_TID_VALUE - 1); | ||
135 | spin_unlock_irqrestore(&priv->rx_pkt_lock, flags); | ||
136 | return 0; | ||
137 | } | ||
138 | |||
139 | /* | ||
140 | * This function deletes the Rx reorder table and frees the memory. | ||
141 | * | ||
142 | * The function stops the associated timer and dispatches all the | ||
143 | * pending packets in the Rx reorder table before deletion. | ||
144 | */ | ||
145 | static void | ||
146 | mwifiex_11n_delete_rx_reorder_tbl_entry(struct mwifiex_private *priv, | ||
147 | struct mwifiex_rx_reorder_tbl | ||
148 | *rx_reor_tbl_ptr) | ||
149 | { | ||
150 | unsigned long flags; | ||
151 | |||
152 | if (!rx_reor_tbl_ptr) | ||
153 | return; | ||
154 | |||
155 | mwifiex_11n_dispatch_pkt_until_start_win(priv, rx_reor_tbl_ptr, | ||
156 | (rx_reor_tbl_ptr->start_win + | ||
157 | rx_reor_tbl_ptr->win_size) | ||
158 | &(MAX_TID_VALUE - 1)); | ||
159 | |||
160 | del_timer(&rx_reor_tbl_ptr->timer_context.timer); | ||
161 | |||
162 | spin_lock_irqsave(&priv->rx_reorder_tbl_lock, flags); | ||
163 | list_del(&rx_reor_tbl_ptr->list); | ||
164 | spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, flags); | ||
165 | |||
166 | kfree(rx_reor_tbl_ptr->rx_reorder_ptr); | ||
167 | kfree(rx_reor_tbl_ptr); | ||
168 | } | ||
169 | |||
170 | /* | ||
171 | * This function returns the pointer to an entry in Rx reordering | ||
172 | * table which matches the given TA/TID pair. | ||
173 | */ | ||
174 | static struct mwifiex_rx_reorder_tbl * | ||
175 | mwifiex_11n_get_rx_reorder_tbl(struct mwifiex_private *priv, int tid, u8 *ta) | ||
176 | { | ||
177 | struct mwifiex_rx_reorder_tbl *rx_reor_tbl_ptr; | ||
178 | unsigned long flags; | ||
179 | |||
180 | spin_lock_irqsave(&priv->rx_reorder_tbl_lock, flags); | ||
181 | list_for_each_entry(rx_reor_tbl_ptr, &priv->rx_reorder_tbl_ptr, list) { | ||
182 | if ((!memcmp(rx_reor_tbl_ptr->ta, ta, ETH_ALEN)) | ||
183 | && (rx_reor_tbl_ptr->tid == tid)) { | ||
184 | spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, | ||
185 | flags); | ||
186 | return rx_reor_tbl_ptr; | ||
187 | } | ||
188 | } | ||
189 | spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, flags); | ||
190 | |||
191 | return NULL; | ||
192 | } | ||
193 | |||
194 | /* | ||
195 | * This function finds the last sequence number used in the packets | ||
196 | * buffered in Rx reordering table. | ||
197 | */ | ||
198 | static int | ||
199 | mwifiex_11n_find_last_seq_num(struct mwifiex_rx_reorder_tbl *rx_reorder_tbl_ptr) | ||
200 | { | ||
201 | int i; | ||
202 | |||
203 | for (i = (rx_reorder_tbl_ptr->win_size - 1); i >= 0; --i) | ||
204 | if (rx_reorder_tbl_ptr->rx_reorder_ptr[i]) | ||
205 | return i; | ||
206 | |||
207 | return -1; | ||
208 | } | ||
209 | |||
210 | /* | ||
211 | * This function flushes all the packets in Rx reordering table. | ||
212 | * | ||
213 | * The function checks if any packets are currently buffered in the | ||
214 | * table or not. In case there are packets available, it dispatches | ||
215 | * them and then dumps the Rx reordering table. | ||
216 | */ | ||
217 | static void | ||
218 | mwifiex_flush_data(unsigned long context) | ||
219 | { | ||
220 | struct reorder_tmr_cnxt *reorder_cnxt = | ||
221 | (struct reorder_tmr_cnxt *) context; | ||
222 | int start_win; | ||
223 | |||
224 | start_win = mwifiex_11n_find_last_seq_num(reorder_cnxt->ptr); | ||
225 | if (start_win >= 0) { | ||
226 | dev_dbg(reorder_cnxt->priv->adapter->dev, | ||
227 | "info: flush data %d\n", start_win); | ||
228 | mwifiex_11n_dispatch_pkt_until_start_win(reorder_cnxt->priv, | ||
229 | reorder_cnxt->ptr, | ||
230 | ((reorder_cnxt->ptr->start_win + | ||
231 | start_win + 1) & (MAX_TID_VALUE - 1))); | ||
232 | } | ||
233 | } | ||
234 | |||
235 | /* | ||
236 | * This function creates an entry in Rx reordering table for the | ||
237 | * given TA/TID. | ||
238 | * | ||
239 | * The function also initializes the entry with sequence number, window | ||
240 | * size as well as initializes the timer. | ||
241 | * | ||
242 | * If the received TA/TID pair is already present, all the packets are | ||
243 | * dispatched and the window size is moved until the SSN. | ||
244 | */ | ||
245 | static void | ||
246 | mwifiex_11n_create_rx_reorder_tbl(struct mwifiex_private *priv, u8 *ta, | ||
247 | int tid, int win_size, int seq_num) | ||
248 | { | ||
249 | int i; | ||
250 | struct mwifiex_rx_reorder_tbl *rx_reor_tbl_ptr, *new_node; | ||
251 | u16 last_seq = 0; | ||
252 | unsigned long flags; | ||
253 | |||
254 | /* | ||
255 | * If we get a TID, ta pair which is already present dispatch all the | ||
256 | * the packets and move the window size until the ssn | ||
257 | */ | ||
258 | rx_reor_tbl_ptr = mwifiex_11n_get_rx_reorder_tbl(priv, tid, ta); | ||
259 | if (rx_reor_tbl_ptr) { | ||
260 | mwifiex_11n_dispatch_pkt_until_start_win(priv, rx_reor_tbl_ptr, | ||
261 | seq_num); | ||
262 | return; | ||
263 | } | ||
264 | /* if !rx_reor_tbl_ptr then create one */ | ||
265 | new_node = kzalloc(sizeof(struct mwifiex_rx_reorder_tbl), GFP_KERNEL); | ||
266 | if (!new_node) { | ||
267 | dev_err(priv->adapter->dev, "%s: failed to alloc new_node\n", | ||
268 | __func__); | ||
269 | return; | ||
270 | } | ||
271 | |||
272 | INIT_LIST_HEAD(&new_node->list); | ||
273 | new_node->tid = tid; | ||
274 | memcpy(new_node->ta, ta, ETH_ALEN); | ||
275 | new_node->start_win = seq_num; | ||
276 | if (mwifiex_queuing_ra_based(priv)) | ||
277 | /* TODO for adhoc */ | ||
278 | dev_dbg(priv->adapter->dev, | ||
279 | "info: ADHOC:last_seq=%d start_win=%d\n", | ||
280 | last_seq, new_node->start_win); | ||
281 | else | ||
282 | last_seq = priv->rx_seq[tid]; | ||
283 | |||
284 | if (last_seq >= new_node->start_win) | ||
285 | new_node->start_win = last_seq + 1; | ||
286 | |||
287 | new_node->win_size = win_size; | ||
288 | |||
289 | new_node->rx_reorder_ptr = kzalloc(sizeof(void *) * win_size, | ||
290 | GFP_KERNEL); | ||
291 | if (!new_node->rx_reorder_ptr) { | ||
292 | kfree((u8 *) new_node); | ||
293 | dev_err(priv->adapter->dev, | ||
294 | "%s: failed to alloc reorder_ptr\n", __func__); | ||
295 | return; | ||
296 | } | ||
297 | |||
298 | new_node->timer_context.ptr = new_node; | ||
299 | new_node->timer_context.priv = priv; | ||
300 | |||
301 | init_timer(&new_node->timer_context.timer); | ||
302 | new_node->timer_context.timer.function = mwifiex_flush_data; | ||
303 | new_node->timer_context.timer.data = | ||
304 | (unsigned long) &new_node->timer_context; | ||
305 | |||
306 | for (i = 0; i < win_size; ++i) | ||
307 | new_node->rx_reorder_ptr[i] = NULL; | ||
308 | |||
309 | spin_lock_irqsave(&priv->rx_reorder_tbl_lock, flags); | ||
310 | list_add_tail(&new_node->list, &priv->rx_reorder_tbl_ptr); | ||
311 | spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, flags); | ||
312 | |||
313 | return; | ||
314 | } | ||
315 | |||
316 | /* | ||
317 | * This function prepares command for adding a BA request. | ||
318 | * | ||
319 | * Preparation includes - | ||
320 | * - Setting command ID and proper size | ||
321 | * - Setting add BA request buffer | ||
322 | * - Ensuring correct endian-ness | ||
323 | */ | ||
324 | int mwifiex_cmd_11n_addba_req(struct mwifiex_private *priv, | ||
325 | struct host_cmd_ds_command *cmd, void *data_buf) | ||
326 | { | ||
327 | struct host_cmd_ds_11n_addba_req *add_ba_req = | ||
328 | (struct host_cmd_ds_11n_addba_req *) | ||
329 | &cmd->params.add_ba_req; | ||
330 | |||
331 | cmd->command = cpu_to_le16(HostCmd_CMD_11N_ADDBA_REQ); | ||
332 | cmd->size = cpu_to_le16(sizeof(*add_ba_req) + S_DS_GEN); | ||
333 | memcpy(add_ba_req, data_buf, sizeof(*add_ba_req)); | ||
334 | |||
335 | return 0; | ||
336 | } | ||
337 | |||
338 | /* | ||
339 | * This function prepares command for adding a BA response. | ||
340 | * | ||
341 | * Preparation includes - | ||
342 | * - Setting command ID and proper size | ||
343 | * - Setting add BA response buffer | ||
344 | * - Ensuring correct endian-ness | ||
345 | */ | ||
346 | int mwifiex_cmd_11n_addba_rsp_gen(struct mwifiex_private *priv, | ||
347 | struct host_cmd_ds_command *cmd, | ||
348 | void *data_buf) | ||
349 | { | ||
350 | struct host_cmd_ds_11n_addba_rsp *add_ba_rsp = | ||
351 | (struct host_cmd_ds_11n_addba_rsp *) | ||
352 | &cmd->params.add_ba_rsp; | ||
353 | struct host_cmd_ds_11n_addba_req *cmd_addba_req = | ||
354 | (struct host_cmd_ds_11n_addba_req *) data_buf; | ||
355 | u8 tid = 0; | ||
356 | int win_size = 0; | ||
357 | uint16_t block_ack_param_set; | ||
358 | |||
359 | cmd->command = cpu_to_le16(HostCmd_CMD_11N_ADDBA_RSP); | ||
360 | cmd->size = cpu_to_le16(sizeof(*add_ba_rsp) + S_DS_GEN); | ||
361 | |||
362 | memcpy(add_ba_rsp->peer_mac_addr, cmd_addba_req->peer_mac_addr, | ||
363 | ETH_ALEN); | ||
364 | add_ba_rsp->dialog_token = cmd_addba_req->dialog_token; | ||
365 | add_ba_rsp->block_ack_tmo = cmd_addba_req->block_ack_tmo; | ||
366 | add_ba_rsp->ssn = cmd_addba_req->ssn; | ||
367 | |||
368 | block_ack_param_set = le16_to_cpu(cmd_addba_req->block_ack_param_set); | ||
369 | tid = (block_ack_param_set & IEEE80211_ADDBA_PARAM_TID_MASK) | ||
370 | >> BLOCKACKPARAM_TID_POS; | ||
371 | add_ba_rsp->status_code = cpu_to_le16(ADDBA_RSP_STATUS_ACCEPT); | ||
372 | block_ack_param_set &= ~IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK; | ||
373 | /* We donot support AMSDU inside AMPDU, hence reset the bit */ | ||
374 | block_ack_param_set &= ~BLOCKACKPARAM_AMSDU_SUPP_MASK; | ||
375 | block_ack_param_set |= (priv->add_ba_param.rx_win_size << | ||
376 | BLOCKACKPARAM_WINSIZE_POS); | ||
377 | add_ba_rsp->block_ack_param_set = cpu_to_le16(block_ack_param_set); | ||
378 | win_size = (le16_to_cpu(add_ba_rsp->block_ack_param_set) | ||
379 | & IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK) | ||
380 | >> BLOCKACKPARAM_WINSIZE_POS; | ||
381 | cmd_addba_req->block_ack_param_set = cpu_to_le16(block_ack_param_set); | ||
382 | |||
383 | mwifiex_11n_create_rx_reorder_tbl(priv, cmd_addba_req->peer_mac_addr, | ||
384 | tid, win_size, le16_to_cpu(cmd_addba_req->ssn)); | ||
385 | return 0; | ||
386 | } | ||
387 | |||
388 | /* | ||
389 | * This function prepares command for deleting a BA request. | ||
390 | * | ||
391 | * Preparation includes - | ||
392 | * - Setting command ID and proper size | ||
393 | * - Setting del BA request buffer | ||
394 | * - Ensuring correct endian-ness | ||
395 | */ | ||
396 | int mwifiex_cmd_11n_delba(struct mwifiex_private *priv, | ||
397 | struct host_cmd_ds_command *cmd, void *data_buf) | ||
398 | { | ||
399 | struct host_cmd_ds_11n_delba *del_ba = (struct host_cmd_ds_11n_delba *) | ||
400 | &cmd->params.del_ba; | ||
401 | |||
402 | cmd->command = cpu_to_le16(HostCmd_CMD_11N_DELBA); | ||
403 | cmd->size = cpu_to_le16(sizeof(*del_ba) + S_DS_GEN); | ||
404 | memcpy(del_ba, data_buf, sizeof(*del_ba)); | ||
405 | |||
406 | return 0; | ||
407 | } | ||
408 | |||
409 | /* | ||
410 | * This function identifies if Rx reordering is needed for a received packet. | ||
411 | * | ||
412 | * In case reordering is required, the function will do the reordering | ||
413 | * before sending it to kernel. | ||
414 | * | ||
415 | * The Rx reorder table is checked first with the received TID/TA pair. If | ||
416 | * not found, the received packet is dispatched immediately. But if found, | ||
417 | * the packet is reordered and all the packets in the updated Rx reordering | ||
418 | * table is dispatched until a hole is found. | ||
419 | * | ||
420 | * For sequence number less than the starting window, the packet is dropped. | ||
421 | */ | ||
422 | int mwifiex_11n_rx_reorder_pkt(struct mwifiex_private *priv, | ||
423 | u16 seq_num, u16 tid, | ||
424 | u8 *ta, u8 pkt_type, void *payload) | ||
425 | { | ||
426 | struct mwifiex_rx_reorder_tbl *rx_reor_tbl_ptr; | ||
427 | int start_win, end_win, win_size; | ||
428 | int ret = 0; | ||
429 | u16 pkt_index = 0; | ||
430 | |||
431 | rx_reor_tbl_ptr = | ||
432 | mwifiex_11n_get_rx_reorder_tbl((struct mwifiex_private *) priv, | ||
433 | tid, ta); | ||
434 | if (!rx_reor_tbl_ptr) { | ||
435 | if (pkt_type != PKT_TYPE_BAR) | ||
436 | mwifiex_11n_dispatch_pkt(priv, payload); | ||
437 | return 0; | ||
438 | } | ||
439 | start_win = rx_reor_tbl_ptr->start_win; | ||
440 | win_size = rx_reor_tbl_ptr->win_size; | ||
441 | end_win = ((start_win + win_size) - 1) & (MAX_TID_VALUE - 1); | ||
442 | del_timer(&rx_reor_tbl_ptr->timer_context.timer); | ||
443 | mod_timer(&rx_reor_tbl_ptr->timer_context.timer, jiffies | ||
444 | + (MIN_FLUSH_TIMER_MS * win_size * HZ) / 1000); | ||
445 | |||
446 | /* | ||
447 | * If seq_num is less then starting win then ignore and drop the | ||
448 | * packet | ||
449 | */ | ||
450 | if ((start_win + TWOPOW11) > (MAX_TID_VALUE - 1)) {/* Wrap */ | ||
451 | if (seq_num >= ((start_win + (TWOPOW11)) & (MAX_TID_VALUE - 1)) | ||
452 | && (seq_num < start_win)) | ||
453 | return -1; | ||
454 | } else if ((seq_num < start_win) | ||
455 | || (seq_num > (start_win + (TWOPOW11)))) { | ||
456 | return -1; | ||
457 | } | ||
458 | |||
459 | /* | ||
460 | * If this packet is a BAR we adjust seq_num as | ||
461 | * WinStart = seq_num | ||
462 | */ | ||
463 | if (pkt_type == PKT_TYPE_BAR) | ||
464 | seq_num = ((seq_num + win_size) - 1) & (MAX_TID_VALUE - 1); | ||
465 | |||
466 | if (((end_win < start_win) | ||
467 | && (seq_num < (TWOPOW11 - (MAX_TID_VALUE - start_win))) | ||
468 | && (seq_num > end_win)) || ((end_win > start_win) | ||
469 | && ((seq_num > end_win) || (seq_num < start_win)))) { | ||
470 | end_win = seq_num; | ||
471 | if (((seq_num - win_size) + 1) >= 0) | ||
472 | start_win = (end_win - win_size) + 1; | ||
473 | else | ||
474 | start_win = (MAX_TID_VALUE - (win_size - seq_num)) + 1; | ||
475 | ret = mwifiex_11n_dispatch_pkt_until_start_win(priv, | ||
476 | rx_reor_tbl_ptr, start_win); | ||
477 | |||
478 | if (ret) | ||
479 | return ret; | ||
480 | } | ||
481 | |||
482 | if (pkt_type != PKT_TYPE_BAR) { | ||
483 | if (seq_num >= start_win) | ||
484 | pkt_index = seq_num - start_win; | ||
485 | else | ||
486 | pkt_index = (seq_num+MAX_TID_VALUE) - start_win; | ||
487 | |||
488 | if (rx_reor_tbl_ptr->rx_reorder_ptr[pkt_index]) | ||
489 | return -1; | ||
490 | |||
491 | rx_reor_tbl_ptr->rx_reorder_ptr[pkt_index] = payload; | ||
492 | } | ||
493 | |||
494 | /* | ||
495 | * Dispatch all packets sequentially from start_win until a | ||
496 | * hole is found and adjust the start_win appropriately | ||
497 | */ | ||
498 | ret = mwifiex_11n_scan_and_dispatch(priv, rx_reor_tbl_ptr); | ||
499 | |||
500 | return ret; | ||
501 | } | ||
502 | |||
503 | /* | ||
504 | * This function deletes an entry for a given TID/TA pair. | ||
505 | * | ||
506 | * The TID/TA are taken from del BA event body. | ||
507 | */ | ||
508 | void | ||
509 | mwifiex_11n_delete_ba_stream_tbl(struct mwifiex_private *priv, int tid, | ||
510 | u8 *peer_mac, u8 type, int initiator) | ||
511 | { | ||
512 | struct mwifiex_rx_reorder_tbl *rx_reor_tbl_ptr; | ||
513 | struct mwifiex_tx_ba_stream_tbl *ptx_tbl; | ||
514 | u8 cleanup_rx_reorder_tbl; | ||
515 | unsigned long flags; | ||
516 | |||
517 | if (type == TYPE_DELBA_RECEIVE) | ||
518 | cleanup_rx_reorder_tbl = (initiator) ? true : false; | ||
519 | else | ||
520 | cleanup_rx_reorder_tbl = (initiator) ? false : true; | ||
521 | |||
522 | dev_dbg(priv->adapter->dev, "event: DELBA: %pM tid=%d, " | ||
523 | "initiator=%d\n", peer_mac, tid, initiator); | ||
524 | |||
525 | if (cleanup_rx_reorder_tbl) { | ||
526 | rx_reor_tbl_ptr = mwifiex_11n_get_rx_reorder_tbl(priv, tid, | ||
527 | peer_mac); | ||
528 | if (!rx_reor_tbl_ptr) { | ||
529 | dev_dbg(priv->adapter->dev, | ||
530 | "event: TID, TA not found in table\n"); | ||
531 | return; | ||
532 | } | ||
533 | mwifiex_11n_delete_rx_reorder_tbl_entry(priv, rx_reor_tbl_ptr); | ||
534 | } else { | ||
535 | ptx_tbl = mwifiex_11n_get_tx_ba_stream_tbl(priv, tid, peer_mac); | ||
536 | if (!ptx_tbl) { | ||
537 | dev_dbg(priv->adapter->dev, | ||
538 | "event: TID, RA not found in table\n"); | ||
539 | return; | ||
540 | } | ||
541 | |||
542 | spin_lock_irqsave(&priv->tx_ba_stream_tbl_lock, flags); | ||
543 | mwifiex_11n_delete_tx_ba_stream_tbl_entry(priv, ptx_tbl); | ||
544 | spin_unlock_irqrestore(&priv->tx_ba_stream_tbl_lock, flags); | ||
545 | } | ||
546 | } | ||
547 | |||
548 | /* | ||
549 | * This function handles the command response of an add BA response. | ||
550 | * | ||
551 | * Handling includes changing the header fields into CPU format and | ||
552 | * creating the stream, provided the add BA is accepted. | ||
553 | */ | ||
554 | int mwifiex_ret_11n_addba_resp(struct mwifiex_private *priv, | ||
555 | struct host_cmd_ds_command *resp) | ||
556 | { | ||
557 | struct host_cmd_ds_11n_addba_rsp *add_ba_rsp = | ||
558 | (struct host_cmd_ds_11n_addba_rsp *) | ||
559 | &resp->params.add_ba_rsp; | ||
560 | int tid, win_size; | ||
561 | struct mwifiex_rx_reorder_tbl *rx_reor_tbl_ptr = NULL; | ||
562 | uint16_t block_ack_param_set; | ||
563 | |||
564 | block_ack_param_set = le16_to_cpu(add_ba_rsp->block_ack_param_set); | ||
565 | |||
566 | tid = (block_ack_param_set & IEEE80211_ADDBA_PARAM_TID_MASK) | ||
567 | >> BLOCKACKPARAM_TID_POS; | ||
568 | /* | ||
569 | * Check if we had rejected the ADDBA, if yes then do not create | ||
570 | * the stream | ||
571 | */ | ||
572 | if (le16_to_cpu(add_ba_rsp->status_code) == BA_RESULT_SUCCESS) { | ||
573 | win_size = (block_ack_param_set & | ||
574 | IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK) | ||
575 | >> BLOCKACKPARAM_WINSIZE_POS; | ||
576 | |||
577 | dev_dbg(priv->adapter->dev, "cmd: ADDBA RSP: %pM" | ||
578 | " tid=%d ssn=%d win_size=%d\n", | ||
579 | add_ba_rsp->peer_mac_addr, | ||
580 | tid, add_ba_rsp->ssn, win_size); | ||
581 | } else { | ||
582 | dev_err(priv->adapter->dev, "ADDBA RSP: failed %pM tid=%d)\n", | ||
583 | add_ba_rsp->peer_mac_addr, tid); | ||
584 | |||
585 | rx_reor_tbl_ptr = mwifiex_11n_get_rx_reorder_tbl(priv, | ||
586 | tid, add_ba_rsp->peer_mac_addr); | ||
587 | if (rx_reor_tbl_ptr) | ||
588 | mwifiex_11n_delete_rx_reorder_tbl_entry(priv, | ||
589 | rx_reor_tbl_ptr); | ||
590 | } | ||
591 | |||
592 | return 0; | ||
593 | } | ||
594 | |||
595 | /* | ||
596 | * This function handles BA stream timeout event by preparing and sending | ||
597 | * a command to the firmware. | ||
598 | */ | ||
599 | void mwifiex_11n_ba_stream_timeout(struct mwifiex_private *priv, | ||
600 | struct host_cmd_ds_11n_batimeout *event) | ||
601 | { | ||
602 | struct host_cmd_ds_11n_delba delba; | ||
603 | |||
604 | memset(&delba, 0, sizeof(struct host_cmd_ds_11n_delba)); | ||
605 | memcpy(delba.peer_mac_addr, event->peer_mac_addr, ETH_ALEN); | ||
606 | |||
607 | delba.del_ba_param_set |= | ||
608 | cpu_to_le16((u16) event->tid << DELBA_TID_POS); | ||
609 | delba.del_ba_param_set |= cpu_to_le16( | ||
610 | (u16) event->origninator << DELBA_INITIATOR_POS); | ||
611 | delba.reason_code = cpu_to_le16(WLAN_REASON_QSTA_TIMEOUT); | ||
612 | mwifiex_prepare_cmd(priv, HostCmd_CMD_11N_DELBA, 0, 0, NULL, &delba); | ||
613 | |||
614 | return; | ||
615 | } | ||
616 | |||
617 | /* | ||
618 | * This function cleans up the Rx reorder table by deleting all the entries | ||
619 | * and re-initializing. | ||
620 | */ | ||
621 | void mwifiex_11n_cleanup_reorder_tbl(struct mwifiex_private *priv) | ||
622 | { | ||
623 | struct mwifiex_rx_reorder_tbl *del_tbl_ptr, *tmp_node; | ||
624 | unsigned long flags; | ||
625 | |||
626 | spin_lock_irqsave(&priv->rx_reorder_tbl_lock, flags); | ||
627 | list_for_each_entry_safe(del_tbl_ptr, tmp_node, | ||
628 | &priv->rx_reorder_tbl_ptr, list) { | ||
629 | spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, flags); | ||
630 | mwifiex_11n_delete_rx_reorder_tbl_entry(priv, del_tbl_ptr); | ||
631 | spin_lock_irqsave(&priv->rx_reorder_tbl_lock, flags); | ||
632 | } | ||
633 | spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, flags); | ||
634 | |||
635 | INIT_LIST_HEAD(&priv->rx_reorder_tbl_ptr); | ||
636 | memset(priv->rx_seq, 0, sizeof(priv->rx_seq)); | ||
637 | } | ||
diff --git a/drivers/net/wireless/mwifiex/11n_rxreorder.h b/drivers/net/wireless/mwifiex/11n_rxreorder.h new file mode 100644 index 000000000000..42f569035745 --- /dev/null +++ b/drivers/net/wireless/mwifiex/11n_rxreorder.h | |||
@@ -0,0 +1,67 @@ | |||
1 | /* | ||
2 | * Marvell Wireless LAN device driver: 802.11n RX Re-ordering | ||
3 | * | ||
4 | * Copyright (C) 2011, Marvell International Ltd. | ||
5 | * | ||
6 | * This software file (the "File") is distributed by Marvell International | ||
7 | * Ltd. under the terms of the GNU General Public License Version 2, June 1991 | ||
8 | * (the "License"). You may use, redistribute and/or modify this File in | ||
9 | * accordance with the terms and conditions of the License, a copy of which | ||
10 | * is available by writing to the Free Software Foundation, Inc., | ||
11 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the | ||
12 | * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. | ||
13 | * | ||
14 | * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE | ||
15 | * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE | ||
16 | * ARE EXPRESSLY DISCLAIMED. The License provides additional details about | ||
17 | * this warranty disclaimer. | ||
18 | */ | ||
19 | |||
20 | #ifndef _MWIFIEX_11N_RXREORDER_H_ | ||
21 | #define _MWIFIEX_11N_RXREORDER_H_ | ||
22 | |||
23 | #define MIN_FLUSH_TIMER_MS 50 | ||
24 | |||
25 | #define PKT_TYPE_BAR 0xE7 | ||
26 | #define MAX_TID_VALUE (2 << 11) | ||
27 | #define TWOPOW11 (2 << 10) | ||
28 | |||
29 | #define BLOCKACKPARAM_TID_POS 2 | ||
30 | #define BLOCKACKPARAM_AMSDU_SUPP_MASK 0x1 | ||
31 | #define BLOCKACKPARAM_WINSIZE_POS 6 | ||
32 | #define DELBA_TID_POS 12 | ||
33 | #define DELBA_INITIATOR_POS 11 | ||
34 | #define TYPE_DELBA_SENT 1 | ||
35 | #define TYPE_DELBA_RECEIVE 2 | ||
36 | #define IMMEDIATE_BLOCK_ACK 0x2 | ||
37 | |||
38 | #define ADDBA_RSP_STATUS_ACCEPT 0 | ||
39 | |||
40 | int mwifiex_11n_rx_reorder_pkt(struct mwifiex_private *, | ||
41 | u16 seqNum, | ||
42 | u16 tid, u8 *ta, | ||
43 | u8 pkttype, void *payload); | ||
44 | void mwifiex_11n_delete_ba_stream_tbl(struct mwifiex_private *priv, int Tid, | ||
45 | u8 *PeerMACAddr, u8 type, | ||
46 | int initiator); | ||
47 | void mwifiex_11n_ba_stream_timeout(struct mwifiex_private *priv, | ||
48 | struct host_cmd_ds_11n_batimeout *event); | ||
49 | int mwifiex_ret_11n_addba_resp(struct mwifiex_private *priv, | ||
50 | struct host_cmd_ds_command | ||
51 | *resp); | ||
52 | int mwifiex_cmd_11n_delba(struct mwifiex_private *priv, | ||
53 | struct host_cmd_ds_command *cmd, | ||
54 | void *data_buf); | ||
55 | int mwifiex_cmd_11n_addba_rsp_gen(struct mwifiex_private *priv, | ||
56 | struct host_cmd_ds_command | ||
57 | *cmd, void *data_buf); | ||
58 | int mwifiex_cmd_11n_addba_req(struct mwifiex_private *priv, | ||
59 | struct host_cmd_ds_command *cmd, | ||
60 | void *data_buf); | ||
61 | void mwifiex_11n_cleanup_reorder_tbl(struct mwifiex_private *priv); | ||
62 | struct mwifiex_rx_reorder_tbl *mwifiex_11n_get_rxreorder_tbl(struct | ||
63 | mwifiex_private | ||
64 | *priv, int tid, | ||
65 | u8 *ta); | ||
66 | |||
67 | #endif /* _MWIFIEX_11N_RXREORDER_H_ */ | ||
diff --git a/drivers/net/wireless/mwifiex/Kconfig b/drivers/net/wireless/mwifiex/Kconfig new file mode 100644 index 000000000000..86962920cef3 --- /dev/null +++ b/drivers/net/wireless/mwifiex/Kconfig | |||
@@ -0,0 +1,21 @@ | |||
1 | config MWIFIEX | ||
2 | tristate "Marvell WiFi-Ex Driver" | ||
3 | depends on CFG80211 | ||
4 | select LIB80211 | ||
5 | ---help--- | ||
6 | This adds support for wireless adapters based on Marvell | ||
7 | 802.11n chipsets. | ||
8 | |||
9 | If you choose to build it as a module, it will be called | ||
10 | mwifiex. | ||
11 | |||
12 | config MWIFIEX_SDIO | ||
13 | tristate "Marvell WiFi-Ex Driver for SD8787" | ||
14 | depends on MWIFIEX && MMC | ||
15 | select FW_LOADER | ||
16 | ---help--- | ||
17 | This adds support for wireless adapters based on Marvell | ||
18 | 8787 chipset with SDIO interface. | ||
19 | |||
20 | If you choose to build it as a module, it will be called | ||
21 | mwifiex_sdio. | ||
diff --git a/drivers/net/wireless/mwifiex/Makefile b/drivers/net/wireless/mwifiex/Makefile new file mode 100644 index 000000000000..42cb733ea33a --- /dev/null +++ b/drivers/net/wireless/mwifiex/Makefile | |||
@@ -0,0 +1,41 @@ | |||
1 | # | ||
2 | # Copyright (C) 2011, Marvell International Ltd. | ||
3 | # | ||
4 | # This software file (the "File") is distributed by Marvell International | ||
5 | # Ltd. under the terms of the GNU General Public License Version 2, June 1991 | ||
6 | # (the "License"). You may use, redistribute and/or modify this File in | ||
7 | # accordance with the terms and conditions of the License, a copy of which | ||
8 | # is available by writing to the Free Software Foundation, Inc., | ||
9 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the | ||
10 | # worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. | ||
11 | # | ||
12 | # THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE | ||
13 | # IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE | ||
14 | # ARE EXPRESSLY DISCLAIMED. The License provides additional details about | ||
15 | # this warranty disclaimer. | ||
16 | |||
17 | |||
18 | mwifiex-y += main.o | ||
19 | mwifiex-y += init.o | ||
20 | mwifiex-y += cfp.o | ||
21 | mwifiex-y += cmdevt.o | ||
22 | mwifiex-y += util.o | ||
23 | mwifiex-y += txrx.o | ||
24 | mwifiex-y += wmm.o | ||
25 | mwifiex-y += 11n.o | ||
26 | mwifiex-y += 11n_aggr.o | ||
27 | mwifiex-y += 11n_rxreorder.o | ||
28 | mwifiex-y += scan.o | ||
29 | mwifiex-y += join.o | ||
30 | mwifiex-y += sta_ioctl.o | ||
31 | mwifiex-y += sta_cmd.o | ||
32 | mwifiex-y += sta_cmdresp.o | ||
33 | mwifiex-y += sta_event.o | ||
34 | mwifiex-y += sta_tx.o | ||
35 | mwifiex-y += sta_rx.o | ||
36 | mwifiex-y += cfg80211.o | ||
37 | mwifiex-$(CONFIG_DEBUG_FS) += debugfs.o | ||
38 | obj-$(CONFIG_MWIFIEX) += mwifiex.o | ||
39 | |||
40 | mwifiex_sdio-y += sdio.o | ||
41 | obj-$(CONFIG_MWIFIEX_SDIO) += mwifiex_sdio.o | ||
diff --git a/drivers/net/wireless/mwifiex/README b/drivers/net/wireless/mwifiex/README new file mode 100644 index 000000000000..338377f7093b --- /dev/null +++ b/drivers/net/wireless/mwifiex/README | |||
@@ -0,0 +1,204 @@ | |||
1 | # Copyright (C) 2011, Marvell International Ltd. | ||
2 | # | ||
3 | # This software file (the "File") is distributed by Marvell International | ||
4 | # Ltd. under the terms of the GNU General Public License Version 2, June 1991 | ||
5 | # (the "License"). You may use, redistribute and/or modify this File in | ||
6 | # accordance with the terms and conditions of the License, a copy of which | ||
7 | # is available by writing to the Free Software Foundation, Inc., | ||
8 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the | ||
9 | # worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. | ||
10 | # | ||
11 | # THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE | ||
12 | # IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE | ||
13 | # ARE EXPRESSLY DISCLAIMED. The License provides additional details about | ||
14 | # this warranty disclaimer. | ||
15 | |||
16 | |||
17 | =============================================================================== | ||
18 | U S E R M A N U A L | ||
19 | |||
20 | 1) FOR DRIVER INSTALL | ||
21 | |||
22 | a) Copy sd8787.bin to /lib/firmware/mrvl/ directory, | ||
23 | create the directory if it doesn't exist. | ||
24 | b) Install WLAN driver, | ||
25 | insmod mwifiex.ko | ||
26 | c) Uninstall WLAN driver, | ||
27 | ifconfig mlanX down | ||
28 | rmmod mwifiex | ||
29 | |||
30 | |||
31 | 2) FOR DRIVER CONFIGURATION AND INFO | ||
32 | The configurations can be done either using the 'iw' user space | ||
33 | utility or debugfs. | ||
34 | |||
35 | a) 'iw' utility commands | ||
36 | |||
37 | Following are some useful iw commands:- | ||
38 | |||
39 | iw dev mlan0 scan | ||
40 | |||
41 | This command will trigger a scan. | ||
42 | The command will then display the scan table entries | ||
43 | |||
44 | iw dev mlan0 connect -w <SSID> [<freq in MHz>] [<bssid>] [key 0:abcde d:1123456789a] | ||
45 | The above command can be used to connect to an AP with a particular SSID. | ||
46 | Ap's operating frequency can be specified or even the bssid. If the AP is using | ||
47 | WEP encryption, wep keys can be specified in the command. | ||
48 | Note: Every time before connecting to an AP scan command (iw dev mlan0 scan) should be used by user. | ||
49 | |||
50 | iw dev mlan0 disconnect | ||
51 | This command will be used to disconnect from an AP. | ||
52 | |||
53 | |||
54 | iw dev mlan0 ibss join <SSID> <freq in MHz> [fixed-freq] [fixed-bssid] [key 0:abcde] | ||
55 | The command will be used to join or create an ibss. Optionally, operating frequency, | ||
56 | bssid and the security related parameters can be specified while joining/creating | ||
57 | and ibss. | ||
58 | |||
59 | iw dev mlan0 ibss leave | ||
60 | The command will be used to leave an ibss network. | ||
61 | |||
62 | iw dev mlan0 link | ||
63 | The command will be used to get the connection status. The command will return parameters | ||
64 | such as SSID, operating frequency, rx/tx packets, signal strength, tx bitrate. | ||
65 | |||
66 | Apart from the iw utility all standard configurations using the 'iwconfig' utility are also supported. | ||
67 | |||
68 | b) Debugfs interface | ||
69 | |||
70 | The debugfs interface can be used for configurations and for getting | ||
71 | some useful information from the driver. | ||
72 | The section below explains the configurations that can be | ||
73 | done. | ||
74 | |||
75 | Mount debugfs to /debugfs mount point: | ||
76 | |||
77 | mkdir /debugfs | ||
78 | mount -t debugfs debugfs /debugfs | ||
79 | |||
80 | The information is provided in /debugfs/mwifiex/mlanX/: | ||
81 | |||
82 | iw reg set <country code> | ||
83 | The command will be used to change the regulatory domain. | ||
84 | |||
85 | iw reg get | ||
86 | The command will be used to get current regulatory domain. | ||
87 | |||
88 | info | ||
89 | This command is used to get driver info. | ||
90 | |||
91 | Usage: | ||
92 | cat info | ||
93 | |||
94 | driver_name = "mwifiex" | ||
95 | driver_version = <driver_name, driver_version, (firmware_version)> | ||
96 | interface_name = "mlanX" | ||
97 | bss_mode = "Ad-hoc" | "Managed" | "Auto" | "Unknown" | ||
98 | media_state = "Disconnected" | "Connected" | ||
99 | mac_address = <6-byte adapter MAC address> | ||
100 | multicase_count = <multicast address count> | ||
101 | essid = <current SSID> | ||
102 | bssid = <current BSSID> | ||
103 | channel = <current channel> | ||
104 | region_code = <current region code> | ||
105 | multicasr_address[n] = <multicast address> | ||
106 | num_tx_bytes = <number of bytes sent to device> | ||
107 | num_rx_bytes = <number of bytes received from device and sent to kernel> | ||
108 | num_tx_pkts = <number of packets sent to device> | ||
109 | num_rx_pkts = <number of packets received from device and sent to kernel> | ||
110 | num_tx_pkts_dropped = <number of Tx packets dropped by driver> | ||
111 | num_rx_pkts_dropped = <number of Rx packets dropped by driver> | ||
112 | num_tx_pkts_err = <number of Tx packets failed to send to device> | ||
113 | num_rx_pkts_err = <number of Rx packets failed to receive from device> | ||
114 | carrier "on" | "off" | ||
115 | tx queue "stopped" | "started" | ||
116 | |||
117 | The following debug info are provided in /debugfs/mwifiex/mlanX/debug: | ||
118 | |||
119 | int_counter = <interrupt count, cleared when interrupt handled> | ||
120 | wmm_ac_vo = <number of packets sent to device from WMM AcVo queue> | ||
121 | wmm_ac_vi = <number of packets sent to device from WMM AcVi queue> | ||
122 | wmm_ac_be = <number of packets sent to device from WMM AcBE queue> | ||
123 | wmm_ac_bk = <number of packets sent to device from WMM AcBK queue> | ||
124 | max_tx_buf_size = <maximum Tx buffer size> | ||
125 | tx_buf_size = <current Tx buffer size> | ||
126 | curr_tx_buf_size = <current Tx buffer size> | ||
127 | ps_mode = <0/1, CAM mode/PS mode> | ||
128 | ps_state = <0/1/2/3, full power state/awake state/pre-sleep state/sleep state> | ||
129 | is_deep_sleep = <0/1, not deep sleep state/deep sleep state> | ||
130 | wakeup_dev_req = <0/1, wakeup device not required/required> | ||
131 | wakeup_tries = <wakeup device count, cleared when device awake> | ||
132 | hs_configured = <0/1, host sleep not configured/configured> | ||
133 | hs_activated = <0/1, extended host sleep not activated/activated> | ||
134 | num_tx_timeout = <number of Tx timeout> | ||
135 | num_cmd_timeout = <number of timeout commands> | ||
136 | timeout_cmd_id = <command id of the last timeout command> | ||
137 | timeout_cmd_act = <command action of the last timeout command> | ||
138 | last_cmd_id = <command id of the last several commands sent to device> | ||
139 | last_cmd_act = <command action of the last several commands sent to device> | ||
140 | last_cmd_index = <0 based last command index> | ||
141 | last_cmd_resp_id = <command id of the last several command responses received from device> | ||
142 | last_cmd_resp_index = <0 based last command response index> | ||
143 | last_event = <event id of the last several events received from device> | ||
144 | last_event_index = <0 based last event index> | ||
145 | num_cmd_h2c_fail = <number of commands failed to send to device> | ||
146 | num_cmd_sleep_cfm_fail = <number of sleep confirm failed to send to device> | ||
147 | num_tx_h2c_fail = <number of data packets failed to send to device> | ||
148 | num_evt_deauth = <number of deauthenticated events received from device> | ||
149 | num_evt_disassoc = <number of disassociated events received from device> | ||
150 | num_evt_link_lost = <number of link lost events received from device> | ||
151 | num_cmd_deauth = <number of deauthenticate commands sent to device> | ||
152 | num_cmd_assoc_ok = <number of associate commands with success return> | ||
153 | num_cmd_assoc_fail = <number of associate commands with failure return> | ||
154 | cmd_sent = <0/1, send command resources available/sending command to device> | ||
155 | data_sent = <0/1, send data resources available/sending data to device> | ||
156 | mp_rd_bitmap = <SDIO multi-port read bitmap> | ||
157 | mp_wr_bitmap = <SDIO multi-port write bitmap> | ||
158 | cmd_resp_received = <0/1, no cmd response to process/response received and yet to process> | ||
159 | event_received = <0/1, no event to process/event received and yet to process> | ||
160 | ioctl_pending = <number of ioctl pending> | ||
161 | tx_pending = <number of Tx packet pending> | ||
162 | rx_pending = <number of Rx packet pending> | ||
163 | |||
164 | |||
165 | 3) FOR DRIVER CONFIGURATION | ||
166 | |||
167 | regrdwr | ||
168 | This command is used to read/write the adapter register. | ||
169 | |||
170 | Usage: | ||
171 | echo " <type> <offset> [value]" > regrdwr | ||
172 | cat regrdwr | ||
173 | |||
174 | where the parameters are, | ||
175 | <type>: 1:MAC/SOC, 2:BBP, 3:RF, 4:PMIC, 5:CAU | ||
176 | <offset>: offset of register | ||
177 | [value]: value to be written | ||
178 | |||
179 | Examples: | ||
180 | echo "1 0xa060" > regrdwr : Read the MAC register | ||
181 | echo "1 0xa060 0x12" > regrdwr : Write the MAC register | ||
182 | echo "1 0xa794 0x80000000" > regrdwr | ||
183 | : Write 0x80000000 to MAC register | ||
184 | rdeeprom | ||
185 | This command is used to read the EEPROM contents of the card. | ||
186 | |||
187 | Usage: | ||
188 | echo "<offset> <length>" > rdeeprom | ||
189 | cat rdeeprom | ||
190 | |||
191 | where the parameters are, | ||
192 | <offset>: multiples of 4 | ||
193 | <length>: 4-20, multiples of 4 | ||
194 | |||
195 | Example: | ||
196 | echo "0 20" > rdeeprom : Read 20 bytes of EEPROM data from offset 0 | ||
197 | |||
198 | getlog | ||
199 | This command is used to get the statistics available in the station. | ||
200 | Usage: | ||
201 | |||
202 | cat getlog | ||
203 | |||
204 | =============================================================================== | ||
diff --git a/drivers/net/wireless/mwifiex/cfg80211.c b/drivers/net/wireless/mwifiex/cfg80211.c new file mode 100644 index 000000000000..80f367f27efc --- /dev/null +++ b/drivers/net/wireless/mwifiex/cfg80211.c | |||
@@ -0,0 +1,1517 @@ | |||
1 | /* | ||
2 | * Marvell Wireless LAN device driver: CFG80211 | ||
3 | * | ||
4 | * Copyright (C) 2011, Marvell International Ltd. | ||
5 | * | ||
6 | * This software file (the "File") is distributed by Marvell International | ||
7 | * Ltd. under the terms of the GNU General Public License Version 2, June 1991 | ||
8 | * (the "License"). You may use, redistribute and/or modify this File in | ||
9 | * accordance with the terms and conditions of the License, a copy of which | ||
10 | * is available by writing to the Free Software Foundation, Inc., | ||
11 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the | ||
12 | * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. | ||
13 | * | ||
14 | * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE | ||
15 | * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE | ||
16 | * ARE EXPRESSLY DISCLAIMED. The License provides additional details about | ||
17 | * this warranty disclaimer. | ||
18 | */ | ||
19 | |||
20 | #include "cfg80211.h" | ||
21 | #include "main.h" | ||
22 | |||
23 | /* | ||
24 | * This function maps the nl802.11 channel type into driver channel type. | ||
25 | * | ||
26 | * The mapping is as follows - | ||
27 | * NL80211_CHAN_NO_HT -> NO_SEC_CHANNEL | ||
28 | * NL80211_CHAN_HT20 -> NO_SEC_CHANNEL | ||
29 | * NL80211_CHAN_HT40PLUS -> SEC_CHANNEL_ABOVE | ||
30 | * NL80211_CHAN_HT40MINUS -> SEC_CHANNEL_BELOW | ||
31 | * Others -> NO_SEC_CHANNEL | ||
32 | */ | ||
33 | static int | ||
34 | mwifiex_cfg80211_channel_type_to_mwifiex_channels(enum nl80211_channel_type | ||
35 | channel_type) | ||
36 | { | ||
37 | int channel; | ||
38 | switch (channel_type) { | ||
39 | case NL80211_CHAN_NO_HT: | ||
40 | case NL80211_CHAN_HT20: | ||
41 | channel = NO_SEC_CHANNEL; | ||
42 | break; | ||
43 | case NL80211_CHAN_HT40PLUS: | ||
44 | channel = SEC_CHANNEL_ABOVE; | ||
45 | break; | ||
46 | case NL80211_CHAN_HT40MINUS: | ||
47 | channel = SEC_CHANNEL_BELOW; | ||
48 | break; | ||
49 | default: | ||
50 | channel = NO_SEC_CHANNEL; | ||
51 | } | ||
52 | return channel; | ||
53 | } | ||
54 | |||
55 | /* | ||
56 | * This function maps the driver channel type into nl802.11 channel type. | ||
57 | * | ||
58 | * The mapping is as follows - | ||
59 | * NO_SEC_CHANNEL -> NL80211_CHAN_HT20 | ||
60 | * SEC_CHANNEL_ABOVE -> NL80211_CHAN_HT40PLUS | ||
61 | * SEC_CHANNEL_BELOW -> NL80211_CHAN_HT40MINUS | ||
62 | * Others -> NL80211_CHAN_HT20 | ||
63 | */ | ||
64 | static enum nl80211_channel_type | ||
65 | mwifiex_channels_to_cfg80211_channel_type(int channel_type) | ||
66 | { | ||
67 | int channel; | ||
68 | switch (channel_type) { | ||
69 | case NO_SEC_CHANNEL: | ||
70 | channel = NL80211_CHAN_HT20; | ||
71 | break; | ||
72 | case SEC_CHANNEL_ABOVE: | ||
73 | channel = NL80211_CHAN_HT40PLUS; | ||
74 | break; | ||
75 | case SEC_CHANNEL_BELOW: | ||
76 | channel = NL80211_CHAN_HT40MINUS; | ||
77 | break; | ||
78 | default: | ||
79 | channel = NL80211_CHAN_HT20; | ||
80 | } | ||
81 | return channel; | ||
82 | } | ||
83 | |||
84 | /* | ||
85 | * This function checks whether WEP is set. | ||
86 | */ | ||
87 | static int | ||
88 | mwifiex_is_alg_wep(u32 cipher) | ||
89 | { | ||
90 | int alg = 0; | ||
91 | |||
92 | switch (cipher) { | ||
93 | case MWIFIEX_ENCRYPTION_MODE_WEP40: | ||
94 | case MWIFIEX_ENCRYPTION_MODE_WEP104: | ||
95 | alg = 1; | ||
96 | break; | ||
97 | default: | ||
98 | alg = 0; | ||
99 | break; | ||
100 | } | ||
101 | return alg; | ||
102 | } | ||
103 | |||
104 | /* | ||
105 | * This function maps the given cipher type into driver specific type. | ||
106 | * | ||
107 | * It also sets a flag to indicate whether WPA is enabled or not. | ||
108 | * | ||
109 | * The mapping table is - | ||
110 | * Input cipher Driver cipher type WPA enabled? | ||
111 | * ------------ ------------------ ------------ | ||
112 | * IW_AUTH_CIPHER_NONE MWIFIEX_ENCRYPTION_MODE_NONE No | ||
113 | * WLAN_CIPHER_SUITE_WEP40 MWIFIEX_ENCRYPTION_MODE_WEP40 No | ||
114 | * WLAN_CIPHER_SUITE_WEP104 MWIFIEX_ENCRYPTION_MODE_WEP104 No | ||
115 | * WLAN_CIPHER_SUITE_TKIP MWIFIEX_ENCRYPTION_MODE_TKIP Yes | ||
116 | * WLAN_CIPHER_SUITE_CCMP MWIFIEX_ENCRYPTION_MODE_CCMP Yes | ||
117 | * Others -1 No | ||
118 | */ | ||
119 | static int | ||
120 | mwifiex_get_mwifiex_cipher(u32 cipher, int *wpa_enabled) | ||
121 | { | ||
122 | int encrypt_mode; | ||
123 | |||
124 | if (wpa_enabled) | ||
125 | *wpa_enabled = 0; | ||
126 | switch (cipher) { | ||
127 | case IW_AUTH_CIPHER_NONE: | ||
128 | encrypt_mode = MWIFIEX_ENCRYPTION_MODE_NONE; | ||
129 | break; | ||
130 | case WLAN_CIPHER_SUITE_WEP40: | ||
131 | encrypt_mode = MWIFIEX_ENCRYPTION_MODE_WEP40; | ||
132 | break; | ||
133 | case WLAN_CIPHER_SUITE_WEP104: | ||
134 | encrypt_mode = MWIFIEX_ENCRYPTION_MODE_WEP104; | ||
135 | break; | ||
136 | case WLAN_CIPHER_SUITE_TKIP: | ||
137 | encrypt_mode = MWIFIEX_ENCRYPTION_MODE_TKIP; | ||
138 | if (wpa_enabled) | ||
139 | *wpa_enabled = 1; | ||
140 | break; | ||
141 | case WLAN_CIPHER_SUITE_CCMP: | ||
142 | encrypt_mode = MWIFIEX_ENCRYPTION_MODE_CCMP; | ||
143 | if (wpa_enabled) | ||
144 | *wpa_enabled = 1; | ||
145 | break; | ||
146 | default: | ||
147 | encrypt_mode = -1; | ||
148 | } | ||
149 | |||
150 | return encrypt_mode; | ||
151 | } | ||
152 | |||
153 | /* | ||
154 | * This function retrieves the private structure from kernel wiphy structure. | ||
155 | */ | ||
156 | static void *mwifiex_cfg80211_get_priv(struct wiphy *wiphy) | ||
157 | { | ||
158 | return (void *) (*(unsigned long *) wiphy_priv(wiphy)); | ||
159 | } | ||
160 | |||
161 | /* | ||
162 | * CFG802.11 operation handler to delete a network key. | ||
163 | */ | ||
164 | static int | ||
165 | mwifiex_cfg80211_del_key(struct wiphy *wiphy, struct net_device *netdev, | ||
166 | u8 key_index, bool pairwise, const u8 *mac_addr) | ||
167 | { | ||
168 | struct mwifiex_private *priv = mwifiex_cfg80211_get_priv(wiphy); | ||
169 | int ret = 0; | ||
170 | |||
171 | ret = mwifiex_set_encode(priv, NULL, 0, key_index, 1); | ||
172 | if (ret) { | ||
173 | wiphy_err(wiphy, "deleting the crypto keys\n"); | ||
174 | return -EFAULT; | ||
175 | } | ||
176 | |||
177 | wiphy_dbg(wiphy, "info: crypto keys deleted\n"); | ||
178 | return 0; | ||
179 | } | ||
180 | |||
181 | /* | ||
182 | * CFG802.11 operation handler to set Tx power. | ||
183 | */ | ||
184 | static int | ||
185 | mwifiex_cfg80211_set_tx_power(struct wiphy *wiphy, | ||
186 | enum nl80211_tx_power_setting type, | ||
187 | int dbm) | ||
188 | { | ||
189 | int ret = 0; | ||
190 | struct mwifiex_private *priv = mwifiex_cfg80211_get_priv(wiphy); | ||
191 | |||
192 | ret = mwifiex_set_tx_power(priv, type, dbm); | ||
193 | |||
194 | return ret; | ||
195 | } | ||
196 | |||
197 | /* | ||
198 | * CFG802.11 operation handler to set Power Save option. | ||
199 | * | ||
200 | * The timeout value, if provided, is currently ignored. | ||
201 | */ | ||
202 | static int | ||
203 | mwifiex_cfg80211_set_power_mgmt(struct wiphy *wiphy, | ||
204 | struct net_device *dev, | ||
205 | bool enabled, int timeout) | ||
206 | { | ||
207 | int ret = 0; | ||
208 | struct mwifiex_private *priv = mwifiex_cfg80211_get_priv(wiphy); | ||
209 | |||
210 | if (timeout) | ||
211 | wiphy_dbg(wiphy, | ||
212 | "info: ignoring the timeout value" | ||
213 | " for IEEE power save\n"); | ||
214 | |||
215 | ret = mwifiex_drv_set_power(priv, enabled); | ||
216 | |||
217 | return ret; | ||
218 | } | ||
219 | |||
220 | /* | ||
221 | * CFG802.11 operation handler to set the default network key. | ||
222 | */ | ||
223 | static int | ||
224 | mwifiex_cfg80211_set_default_key(struct wiphy *wiphy, struct net_device *netdev, | ||
225 | u8 key_index, bool unicast, | ||
226 | bool multicast) | ||
227 | { | ||
228 | struct mwifiex_private *priv = mwifiex_cfg80211_get_priv(wiphy); | ||
229 | int ret; | ||
230 | |||
231 | ret = mwifiex_set_encode(priv, NULL, 0, key_index, 0); | ||
232 | |||
233 | wiphy_dbg(wiphy, "info: set default Tx key index\n"); | ||
234 | |||
235 | if (ret) | ||
236 | return -EFAULT; | ||
237 | |||
238 | return 0; | ||
239 | } | ||
240 | |||
241 | /* | ||
242 | * CFG802.11 operation handler to add a network key. | ||
243 | */ | ||
244 | static int | ||
245 | mwifiex_cfg80211_add_key(struct wiphy *wiphy, struct net_device *netdev, | ||
246 | u8 key_index, bool pairwise, const u8 *mac_addr, | ||
247 | struct key_params *params) | ||
248 | { | ||
249 | struct mwifiex_private *priv = mwifiex_cfg80211_get_priv(wiphy); | ||
250 | int ret = 0; | ||
251 | int encrypt_mode; | ||
252 | |||
253 | encrypt_mode = mwifiex_get_mwifiex_cipher(params->cipher, NULL); | ||
254 | |||
255 | if (encrypt_mode != -1) | ||
256 | ret = mwifiex_set_encode(priv, params->key, params->key_len, | ||
257 | key_index, 0); | ||
258 | |||
259 | wiphy_dbg(wiphy, "info: crypto keys added\n"); | ||
260 | |||
261 | if (ret) | ||
262 | return -EFAULT; | ||
263 | |||
264 | return 0; | ||
265 | } | ||
266 | |||
267 | /* | ||
268 | * This function sends domain information to the firmware. | ||
269 | * | ||
270 | * The following information are passed to the firmware - | ||
271 | * - Country codes | ||
272 | * - Sub bands (first channel, number of channels, maximum Tx power) | ||
273 | */ | ||
274 | static int mwifiex_send_domain_info_cmd_fw(struct wiphy *wiphy) | ||
275 | { | ||
276 | u8 no_of_triplet = 0; | ||
277 | struct ieee80211_country_ie_triplet *t; | ||
278 | u8 no_of_parsed_chan = 0; | ||
279 | u8 first_chan = 0, next_chan = 0, max_pwr = 0; | ||
280 | u8 i, flag = 0; | ||
281 | enum ieee80211_band band; | ||
282 | struct ieee80211_supported_band *sband; | ||
283 | struct ieee80211_channel *ch; | ||
284 | struct mwifiex_private *priv = mwifiex_cfg80211_get_priv(wiphy); | ||
285 | struct mwifiex_adapter *adapter = priv->adapter; | ||
286 | struct mwifiex_802_11d_domain_reg *domain_info = &adapter->domain_reg; | ||
287 | int ret = 0; | ||
288 | |||
289 | /* Set country code */ | ||
290 | domain_info->country_code[0] = priv->country_code[0]; | ||
291 | domain_info->country_code[1] = priv->country_code[1]; | ||
292 | domain_info->country_code[2] = ' '; | ||
293 | |||
294 | band = mwifiex_band_to_radio_type(adapter->config_bands); | ||
295 | if (!wiphy->bands[band]) { | ||
296 | wiphy_err(wiphy, "11D: setting domain info in FW\n"); | ||
297 | return -1; | ||
298 | } | ||
299 | |||
300 | sband = wiphy->bands[band]; | ||
301 | |||
302 | for (i = 0; i < sband->n_channels ; i++) { | ||
303 | ch = &sband->channels[i]; | ||
304 | if (ch->flags & IEEE80211_CHAN_DISABLED) | ||
305 | continue; | ||
306 | |||
307 | if (!flag) { | ||
308 | flag = 1; | ||
309 | first_chan = (u32) ch->hw_value; | ||
310 | next_chan = first_chan; | ||
311 | max_pwr = ch->max_power; | ||
312 | no_of_parsed_chan = 1; | ||
313 | continue; | ||
314 | } | ||
315 | |||
316 | if (ch->hw_value == next_chan + 1 && | ||
317 | ch->max_power == max_pwr) { | ||
318 | next_chan++; | ||
319 | no_of_parsed_chan++; | ||
320 | } else { | ||
321 | t = &domain_info->triplet[no_of_triplet]; | ||
322 | t->chans.first_channel = first_chan; | ||
323 | t->chans.num_channels = no_of_parsed_chan; | ||
324 | t->chans.max_power = max_pwr; | ||
325 | no_of_triplet++; | ||
326 | first_chan = (u32) ch->hw_value; | ||
327 | next_chan = first_chan; | ||
328 | max_pwr = ch->max_power; | ||
329 | no_of_parsed_chan = 1; | ||
330 | } | ||
331 | } | ||
332 | |||
333 | if (flag) { | ||
334 | t = &domain_info->triplet[no_of_triplet]; | ||
335 | t->chans.first_channel = first_chan; | ||
336 | t->chans.num_channels = no_of_parsed_chan; | ||
337 | t->chans.max_power = max_pwr; | ||
338 | no_of_triplet++; | ||
339 | } | ||
340 | |||
341 | domain_info->no_of_triplet = no_of_triplet; | ||
342 | /* Send cmd to FW to set domain info */ | ||
343 | ret = mwifiex_prepare_cmd(priv, HostCmd_CMD_802_11D_DOMAIN_INFO, | ||
344 | HostCmd_ACT_GEN_SET, 0, NULL, NULL); | ||
345 | if (ret) | ||
346 | wiphy_err(wiphy, "11D: setting domain info in FW\n"); | ||
347 | |||
348 | return ret; | ||
349 | } | ||
350 | |||
351 | /* | ||
352 | * CFG802.11 regulatory domain callback function. | ||
353 | * | ||
354 | * This function is called when the regulatory domain is changed due to the | ||
355 | * following reasons - | ||
356 | * - Set by driver | ||
357 | * - Set by system core | ||
358 | * - Set by user | ||
359 | * - Set bt Country IE | ||
360 | */ | ||
361 | static int mwifiex_reg_notifier(struct wiphy *wiphy, | ||
362 | struct regulatory_request *request) | ||
363 | { | ||
364 | struct mwifiex_private *priv = mwifiex_cfg80211_get_priv(wiphy); | ||
365 | |||
366 | wiphy_dbg(wiphy, "info: cfg80211 regulatory domain callback for domain" | ||
367 | " %c%c\n", request->alpha2[0], request->alpha2[1]); | ||
368 | |||
369 | memcpy(priv->country_code, request->alpha2, sizeof(request->alpha2)); | ||
370 | |||
371 | switch (request->initiator) { | ||
372 | case NL80211_REGDOM_SET_BY_DRIVER: | ||
373 | case NL80211_REGDOM_SET_BY_CORE: | ||
374 | case NL80211_REGDOM_SET_BY_USER: | ||
375 | break; | ||
376 | /* Todo: apply driver specific changes in channel flags based | ||
377 | on the request initiator if necessary. */ | ||
378 | case NL80211_REGDOM_SET_BY_COUNTRY_IE: | ||
379 | break; | ||
380 | } | ||
381 | mwifiex_send_domain_info_cmd_fw(wiphy); | ||
382 | |||
383 | return 0; | ||
384 | } | ||
385 | |||
386 | /* | ||
387 | * This function sets the RF channel. | ||
388 | * | ||
389 | * This function creates multiple IOCTL requests, populates them accordingly | ||
390 | * and issues them to set the band/channel and frequency. | ||
391 | */ | ||
392 | static int | ||
393 | mwifiex_set_rf_channel(struct mwifiex_private *priv, | ||
394 | struct ieee80211_channel *chan, | ||
395 | enum nl80211_channel_type channel_type) | ||
396 | { | ||
397 | struct mwifiex_chan_freq_power cfp; | ||
398 | int ret = 0; | ||
399 | int status = 0; | ||
400 | struct mwifiex_ds_band_cfg band_cfg; | ||
401 | int mode; | ||
402 | u8 wait_option = MWIFIEX_IOCTL_WAIT; | ||
403 | u32 config_bands = 0; | ||
404 | struct wiphy *wiphy = priv->wdev->wiphy; | ||
405 | |||
406 | mode = mwifiex_drv_get_mode(priv, wait_option); | ||
407 | |||
408 | if (chan) { | ||
409 | memset(&band_cfg, 0, sizeof(band_cfg)); | ||
410 | /* Set appropriate bands */ | ||
411 | if (chan->band == IEEE80211_BAND_2GHZ) | ||
412 | config_bands = BAND_B | BAND_G | BAND_GN; | ||
413 | else | ||
414 | config_bands = BAND_AN | BAND_A; | ||
415 | if (mode == MWIFIEX_BSS_MODE_INFRA | ||
416 | || mode == MWIFIEX_BSS_MODE_AUTO) { | ||
417 | band_cfg.config_bands = config_bands; | ||
418 | } else if (mode == MWIFIEX_BSS_MODE_IBSS) { | ||
419 | band_cfg.config_bands = config_bands; | ||
420 | band_cfg.adhoc_start_band = config_bands; | ||
421 | } | ||
422 | /* Set channel offset */ | ||
423 | band_cfg.sec_chan_offset = | ||
424 | mwifiex_cfg80211_channel_type_to_mwifiex_channels | ||
425 | (channel_type); | ||
426 | status = mwifiex_radio_ioctl_band_cfg(priv, HostCmd_ACT_GEN_SET, | ||
427 | &band_cfg); | ||
428 | |||
429 | if (status) | ||
430 | return -EFAULT; | ||
431 | mwifiex_send_domain_info_cmd_fw(wiphy); | ||
432 | } | ||
433 | |||
434 | wiphy_dbg(wiphy, "info: setting band %d, channel offset %d and " | ||
435 | "mode %d\n", config_bands, band_cfg.sec_chan_offset, mode); | ||
436 | if (!chan) | ||
437 | return ret; | ||
438 | |||
439 | memset(&cfp, 0, sizeof(cfp)); | ||
440 | cfp.freq = chan->center_freq; | ||
441 | /* Convert frequency to channel */ | ||
442 | cfp.channel = ieee80211_frequency_to_channel(chan->center_freq); | ||
443 | |||
444 | status = mwifiex_bss_ioctl_channel(priv, HostCmd_ACT_GEN_SET, &cfp); | ||
445 | if (status) | ||
446 | return -EFAULT; | ||
447 | |||
448 | ret = mwifiex_drv_change_adhoc_chan(priv, cfp.channel); | ||
449 | |||
450 | return ret; | ||
451 | } | ||
452 | |||
453 | /* | ||
454 | * CFG802.11 operation handler to set channel. | ||
455 | * | ||
456 | * This function can only be used when station is not connected. | ||
457 | */ | ||
458 | static int | ||
459 | mwifiex_cfg80211_set_channel(struct wiphy *wiphy, struct net_device *dev, | ||
460 | struct ieee80211_channel *chan, | ||
461 | enum nl80211_channel_type channel_type) | ||
462 | { | ||
463 | struct mwifiex_private *priv = mwifiex_cfg80211_get_priv(wiphy); | ||
464 | |||
465 | if (priv->media_connected) { | ||
466 | wiphy_err(wiphy, "This setting is valid only when station " | ||
467 | "is not connected\n"); | ||
468 | return -EINVAL; | ||
469 | } | ||
470 | |||
471 | return mwifiex_set_rf_channel(priv, chan, channel_type); | ||
472 | } | ||
473 | |||
474 | /* | ||
475 | * This function sets the fragmentation threshold. | ||
476 | * | ||
477 | * This function creates an IOCTL request, populates it accordingly | ||
478 | * and issues an IOCTL. | ||
479 | * | ||
480 | * The fragmentation threshold value must lies between MWIFIEX_FRAG_MIN_VALUE | ||
481 | * and MWIFIEX_FRAG_MAX_VALUE. | ||
482 | */ | ||
483 | static int | ||
484 | mwifiex_set_frag(struct mwifiex_private *priv, u32 frag_thr) | ||
485 | { | ||
486 | int ret = 0; | ||
487 | int status = 0; | ||
488 | struct mwifiex_wait_queue *wait = NULL; | ||
489 | u8 wait_option = MWIFIEX_IOCTL_WAIT; | ||
490 | |||
491 | if (frag_thr < MWIFIEX_FRAG_MIN_VALUE | ||
492 | || frag_thr > MWIFIEX_FRAG_MAX_VALUE) | ||
493 | return -EINVAL; | ||
494 | |||
495 | wait = mwifiex_alloc_fill_wait_queue(priv, wait_option); | ||
496 | if (!wait) | ||
497 | return -ENOMEM; | ||
498 | |||
499 | status = mwifiex_snmp_mib_ioctl(priv, wait, FRAG_THRESH_I, | ||
500 | HostCmd_ACT_GEN_SET, &frag_thr); | ||
501 | |||
502 | if (mwifiex_request_ioctl(priv, wait, status, wait_option)) | ||
503 | ret = -EFAULT; | ||
504 | |||
505 | kfree(wait); | ||
506 | return ret; | ||
507 | } | ||
508 | |||
509 | /* | ||
510 | * This function sets the RTS threshold. | ||
511 | * | ||
512 | * This function creates an IOCTL request, populates it accordingly | ||
513 | * and issues an IOCTL. | ||
514 | */ | ||
515 | static int | ||
516 | mwifiex_set_rts(struct mwifiex_private *priv, u32 rts_thr) | ||
517 | { | ||
518 | int ret = 0; | ||
519 | struct mwifiex_wait_queue *wait = NULL; | ||
520 | int status = 0; | ||
521 | u8 wait_option = MWIFIEX_IOCTL_WAIT; | ||
522 | |||
523 | if (rts_thr < MWIFIEX_RTS_MIN_VALUE || rts_thr > MWIFIEX_RTS_MAX_VALUE) | ||
524 | rts_thr = MWIFIEX_RTS_MAX_VALUE; | ||
525 | |||
526 | wait = mwifiex_alloc_fill_wait_queue(priv, wait_option); | ||
527 | if (!wait) | ||
528 | return -ENOMEM; | ||
529 | |||
530 | status = mwifiex_snmp_mib_ioctl(priv, wait, RTS_THRESH_I, | ||
531 | HostCmd_ACT_GEN_SET, &rts_thr); | ||
532 | |||
533 | if (mwifiex_request_ioctl(priv, wait, status, wait_option)) | ||
534 | ret = -EFAULT; | ||
535 | |||
536 | kfree(wait); | ||
537 | return ret; | ||
538 | } | ||
539 | |||
540 | /* | ||
541 | * CFG802.11 operation handler to set wiphy parameters. | ||
542 | * | ||
543 | * This function can be used to set the RTS threshold and the | ||
544 | * Fragmentation threshold of the driver. | ||
545 | */ | ||
546 | static int | ||
547 | mwifiex_cfg80211_set_wiphy_params(struct wiphy *wiphy, u32 changed) | ||
548 | { | ||
549 | struct mwifiex_private *priv = mwifiex_cfg80211_get_priv(wiphy); | ||
550 | |||
551 | int ret = 0; | ||
552 | |||
553 | if (changed & WIPHY_PARAM_RTS_THRESHOLD) | ||
554 | ret = mwifiex_set_rts(priv, wiphy->rts_threshold); | ||
555 | |||
556 | if (changed & WIPHY_PARAM_FRAG_THRESHOLD) | ||
557 | ret = mwifiex_set_frag(priv, wiphy->frag_threshold); | ||
558 | |||
559 | return ret; | ||
560 | } | ||
561 | |||
562 | /* | ||
563 | * CFG802.11 operation handler to change interface type. | ||
564 | * | ||
565 | * This function creates an IOCTL request, populates it accordingly | ||
566 | * and issues an IOCTL. | ||
567 | * | ||
568 | * The function also maps the CFG802.11 mode type into driver mode type. | ||
569 | * NL80211_IFTYPE_ADHOC -> MWIFIEX_BSS_MODE_IBSS | ||
570 | * NL80211_IFTYPE_STATION -> MWIFIEX_BSS_MODE_INFRA | ||
571 | * NL80211_IFTYPE_UNSPECIFIED -> MWIFIEX_BSS_MODE_AUTO | ||
572 | */ | ||
573 | static int | ||
574 | mwifiex_cfg80211_change_virtual_intf(struct wiphy *wiphy, | ||
575 | struct net_device *dev, | ||
576 | enum nl80211_iftype type, u32 *flags, | ||
577 | struct vif_params *params) | ||
578 | { | ||
579 | int ret = 0; | ||
580 | struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); | ||
581 | int mode = -1; | ||
582 | struct mwifiex_wait_queue *wait = NULL; | ||
583 | int status = 0; | ||
584 | |||
585 | wait = mwifiex_alloc_fill_wait_queue(priv, MWIFIEX_IOCTL_WAIT); | ||
586 | if (!wait) | ||
587 | return -ENOMEM; | ||
588 | |||
589 | switch (type) { | ||
590 | case NL80211_IFTYPE_ADHOC: | ||
591 | mode = MWIFIEX_BSS_MODE_IBSS; | ||
592 | dev->ieee80211_ptr->iftype = NL80211_IFTYPE_ADHOC; | ||
593 | wiphy_dbg(wiphy, "info: setting interface type to adhoc\n"); | ||
594 | break; | ||
595 | case NL80211_IFTYPE_STATION: | ||
596 | mode = MWIFIEX_BSS_MODE_INFRA; | ||
597 | dev->ieee80211_ptr->iftype = NL80211_IFTYPE_STATION; | ||
598 | wiphy_dbg(wiphy, "info: Setting interface type to managed\n"); | ||
599 | break; | ||
600 | case NL80211_IFTYPE_UNSPECIFIED: | ||
601 | mode = MWIFIEX_BSS_MODE_AUTO; | ||
602 | dev->ieee80211_ptr->iftype = NL80211_IFTYPE_STATION; | ||
603 | wiphy_dbg(wiphy, "info: setting interface type to auto\n"); | ||
604 | break; | ||
605 | default: | ||
606 | ret = -EINVAL; | ||
607 | } | ||
608 | if (ret) | ||
609 | goto done; | ||
610 | status = mwifiex_bss_ioctl_mode(priv, wait, HostCmd_ACT_GEN_SET, &mode); | ||
611 | |||
612 | if (mwifiex_request_ioctl(priv, wait, status, MWIFIEX_IOCTL_WAIT)) | ||
613 | ret = -EFAULT; | ||
614 | |||
615 | done: | ||
616 | kfree(wait); | ||
617 | return ret; | ||
618 | } | ||
619 | |||
620 | /* | ||
621 | * This function dumps the station information on a buffer. | ||
622 | * | ||
623 | * The following information are shown - | ||
624 | * - Total bytes transmitted | ||
625 | * - Total bytes received | ||
626 | * - Total packets transmitted | ||
627 | * - Total packets received | ||
628 | * - Signal quality level | ||
629 | * - Transmission rate | ||
630 | */ | ||
631 | static int | ||
632 | mwifiex_dump_station_info(struct mwifiex_private *priv, | ||
633 | struct station_info *sinfo) | ||
634 | { | ||
635 | struct mwifiex_ds_get_signal signal; | ||
636 | struct mwifiex_rate_cfg rate; | ||
637 | int ret = 0; | ||
638 | |||
639 | sinfo->filled = STATION_INFO_RX_BYTES | STATION_INFO_TX_BYTES | | ||
640 | STATION_INFO_RX_PACKETS | | ||
641 | STATION_INFO_TX_PACKETS | ||
642 | | STATION_INFO_SIGNAL | STATION_INFO_TX_BITRATE; | ||
643 | |||
644 | /* Get signal information from the firmware */ | ||
645 | memset(&signal, 0, sizeof(struct mwifiex_ds_get_signal)); | ||
646 | if (mwifiex_get_signal_info(priv, MWIFIEX_IOCTL_WAIT, &signal)) { | ||
647 | dev_err(priv->adapter->dev, "getting signal information\n"); | ||
648 | ret = -EFAULT; | ||
649 | } | ||
650 | |||
651 | if (mwifiex_drv_get_data_rate(priv, &rate)) { | ||
652 | dev_err(priv->adapter->dev, "getting data rate\n"); | ||
653 | ret = -EFAULT; | ||
654 | } | ||
655 | |||
656 | sinfo->rx_bytes = priv->stats.rx_bytes; | ||
657 | sinfo->tx_bytes = priv->stats.tx_bytes; | ||
658 | sinfo->rx_packets = priv->stats.rx_packets; | ||
659 | sinfo->tx_packets = priv->stats.tx_packets; | ||
660 | sinfo->signal = priv->w_stats.qual.level; | ||
661 | sinfo->txrate.legacy = rate.rate; | ||
662 | |||
663 | return ret; | ||
664 | } | ||
665 | |||
666 | /* | ||
667 | * CFG802.11 operation handler to get station information. | ||
668 | * | ||
669 | * This function only works in connected mode, and dumps the | ||
670 | * requested station information, if available. | ||
671 | */ | ||
672 | static int | ||
673 | mwifiex_cfg80211_get_station(struct wiphy *wiphy, struct net_device *dev, | ||
674 | u8 *mac, struct station_info *sinfo) | ||
675 | { | ||
676 | struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); | ||
677 | int ret = 0; | ||
678 | |||
679 | mwifiex_dump_station_info(priv, sinfo); | ||
680 | |||
681 | if (!priv->media_connected) | ||
682 | return -ENOENT; | ||
683 | if (memcmp(mac, priv->cfg_bssid, ETH_ALEN)) | ||
684 | return -ENOENT; | ||
685 | |||
686 | |||
687 | ret = mwifiex_dump_station_info(priv, sinfo); | ||
688 | |||
689 | return ret; | ||
690 | } | ||
691 | |||
692 | /* Supported rates to be advertised to the cfg80211 */ | ||
693 | |||
694 | static struct ieee80211_rate mwifiex_rates[] = { | ||
695 | {.bitrate = 10, .hw_value = 2, }, | ||
696 | {.bitrate = 20, .hw_value = 4, }, | ||
697 | {.bitrate = 55, .hw_value = 11, }, | ||
698 | {.bitrate = 110, .hw_value = 22, }, | ||
699 | {.bitrate = 220, .hw_value = 44, }, | ||
700 | {.bitrate = 60, .hw_value = 12, }, | ||
701 | {.bitrate = 90, .hw_value = 18, }, | ||
702 | {.bitrate = 120, .hw_value = 24, }, | ||
703 | {.bitrate = 180, .hw_value = 36, }, | ||
704 | {.bitrate = 240, .hw_value = 48, }, | ||
705 | {.bitrate = 360, .hw_value = 72, }, | ||
706 | {.bitrate = 480, .hw_value = 96, }, | ||
707 | {.bitrate = 540, .hw_value = 108, }, | ||
708 | {.bitrate = 720, .hw_value = 144, }, | ||
709 | }; | ||
710 | |||
711 | /* Channel definitions to be advertised to cfg80211 */ | ||
712 | |||
713 | static struct ieee80211_channel mwifiex_channels_2ghz[] = { | ||
714 | {.center_freq = 2412, .hw_value = 1, }, | ||
715 | {.center_freq = 2417, .hw_value = 2, }, | ||
716 | {.center_freq = 2422, .hw_value = 3, }, | ||
717 | {.center_freq = 2427, .hw_value = 4, }, | ||
718 | {.center_freq = 2432, .hw_value = 5, }, | ||
719 | {.center_freq = 2437, .hw_value = 6, }, | ||
720 | {.center_freq = 2442, .hw_value = 7, }, | ||
721 | {.center_freq = 2447, .hw_value = 8, }, | ||
722 | {.center_freq = 2452, .hw_value = 9, }, | ||
723 | {.center_freq = 2457, .hw_value = 10, }, | ||
724 | {.center_freq = 2462, .hw_value = 11, }, | ||
725 | {.center_freq = 2467, .hw_value = 12, }, | ||
726 | {.center_freq = 2472, .hw_value = 13, }, | ||
727 | {.center_freq = 2484, .hw_value = 14, }, | ||
728 | }; | ||
729 | |||
730 | static struct ieee80211_supported_band mwifiex_band_2ghz = { | ||
731 | .channels = mwifiex_channels_2ghz, | ||
732 | .n_channels = ARRAY_SIZE(mwifiex_channels_2ghz), | ||
733 | .bitrates = mwifiex_rates, | ||
734 | .n_bitrates = 14, | ||
735 | }; | ||
736 | |||
737 | static struct ieee80211_channel mwifiex_channels_5ghz[] = { | ||
738 | {.center_freq = 5040, .hw_value = 8, }, | ||
739 | {.center_freq = 5060, .hw_value = 12, }, | ||
740 | {.center_freq = 5080, .hw_value = 16, }, | ||
741 | {.center_freq = 5170, .hw_value = 34, }, | ||
742 | {.center_freq = 5190, .hw_value = 38, }, | ||
743 | {.center_freq = 5210, .hw_value = 42, }, | ||
744 | {.center_freq = 5230, .hw_value = 46, }, | ||
745 | {.center_freq = 5180, .hw_value = 36, }, | ||
746 | {.center_freq = 5200, .hw_value = 40, }, | ||
747 | {.center_freq = 5220, .hw_value = 44, }, | ||
748 | {.center_freq = 5240, .hw_value = 48, }, | ||
749 | {.center_freq = 5260, .hw_value = 52, }, | ||
750 | {.center_freq = 5280, .hw_value = 56, }, | ||
751 | {.center_freq = 5300, .hw_value = 60, }, | ||
752 | {.center_freq = 5320, .hw_value = 64, }, | ||
753 | {.center_freq = 5500, .hw_value = 100, }, | ||
754 | {.center_freq = 5520, .hw_value = 104, }, | ||
755 | {.center_freq = 5540, .hw_value = 108, }, | ||
756 | {.center_freq = 5560, .hw_value = 112, }, | ||
757 | {.center_freq = 5580, .hw_value = 116, }, | ||
758 | {.center_freq = 5600, .hw_value = 120, }, | ||
759 | {.center_freq = 5620, .hw_value = 124, }, | ||
760 | {.center_freq = 5640, .hw_value = 128, }, | ||
761 | {.center_freq = 5660, .hw_value = 132, }, | ||
762 | {.center_freq = 5680, .hw_value = 136, }, | ||
763 | {.center_freq = 5700, .hw_value = 140, }, | ||
764 | {.center_freq = 5745, .hw_value = 149, }, | ||
765 | {.center_freq = 5765, .hw_value = 153, }, | ||
766 | {.center_freq = 5785, .hw_value = 157, }, | ||
767 | {.center_freq = 5805, .hw_value = 161, }, | ||
768 | {.center_freq = 5825, .hw_value = 165, }, | ||
769 | }; | ||
770 | |||
771 | static struct ieee80211_supported_band mwifiex_band_5ghz = { | ||
772 | .channels = mwifiex_channels_5ghz, | ||
773 | .n_channels = ARRAY_SIZE(mwifiex_channels_5ghz), | ||
774 | .bitrates = mwifiex_rates - 4, | ||
775 | .n_bitrates = ARRAY_SIZE(mwifiex_rates) + 4, | ||
776 | }; | ||
777 | |||
778 | |||
779 | /* Supported crypto cipher suits to be advertised to cfg80211 */ | ||
780 | |||
781 | static const u32 mwifiex_cipher_suites[] = { | ||
782 | WLAN_CIPHER_SUITE_WEP40, | ||
783 | WLAN_CIPHER_SUITE_WEP104, | ||
784 | WLAN_CIPHER_SUITE_TKIP, | ||
785 | WLAN_CIPHER_SUITE_CCMP, | ||
786 | }; | ||
787 | |||
788 | /* | ||
789 | * CFG802.11 operation handler for disconnection request. | ||
790 | * | ||
791 | * This function does not work when there is already a disconnection | ||
792 | * procedure going on. | ||
793 | */ | ||
794 | static int | ||
795 | mwifiex_cfg80211_disconnect(struct wiphy *wiphy, struct net_device *dev, | ||
796 | u16 reason_code) | ||
797 | { | ||
798 | struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); | ||
799 | |||
800 | if (priv->disconnect) | ||
801 | return -EBUSY; | ||
802 | |||
803 | priv->disconnect = 1; | ||
804 | if (mwifiex_disconnect(priv, MWIFIEX_IOCTL_WAIT, NULL)) | ||
805 | return -EFAULT; | ||
806 | |||
807 | wiphy_dbg(wiphy, "info: successfully disconnected from %pM:" | ||
808 | " reason code %d\n", priv->cfg_bssid, reason_code); | ||
809 | |||
810 | queue_work(priv->workqueue, &priv->cfg_workqueue); | ||
811 | |||
812 | return 0; | ||
813 | } | ||
814 | |||
815 | /* | ||
816 | * This function informs the CFG802.11 subsystem of a new IBSS. | ||
817 | * | ||
818 | * The following information are sent to the CFG802.11 subsystem | ||
819 | * to register the new IBSS. If we do not register the new IBSS, | ||
820 | * a kernel panic will result. | ||
821 | * - SSID | ||
822 | * - SSID length | ||
823 | * - BSSID | ||
824 | * - Channel | ||
825 | */ | ||
826 | static int mwifiex_cfg80211_inform_ibss_bss(struct mwifiex_private *priv) | ||
827 | { | ||
828 | int ret = 0; | ||
829 | struct ieee80211_channel *chan; | ||
830 | struct mwifiex_bss_info bss_info; | ||
831 | int ie_len = 0; | ||
832 | u8 ie_buf[IEEE80211_MAX_SSID_LEN + sizeof(struct ieee_types_header)]; | ||
833 | |||
834 | ret = mwifiex_get_bss_info(priv, &bss_info); | ||
835 | if (ret) | ||
836 | return ret; | ||
837 | |||
838 | ie_buf[0] = WLAN_EID_SSID; | ||
839 | ie_buf[1] = bss_info.ssid.ssid_len; | ||
840 | |||
841 | memcpy(&ie_buf[sizeof(struct ieee_types_header)], | ||
842 | &bss_info.ssid.ssid, | ||
843 | bss_info.ssid.ssid_len); | ||
844 | ie_len = ie_buf[1] + sizeof(struct ieee_types_header); | ||
845 | |||
846 | chan = __ieee80211_get_channel(priv->wdev->wiphy, | ||
847 | ieee80211_channel_to_frequency(bss_info.bss_chan, | ||
848 | priv->curr_bss_params.band)); | ||
849 | |||
850 | cfg80211_inform_bss(priv->wdev->wiphy, chan, | ||
851 | bss_info.bssid, 0, WLAN_CAPABILITY_IBSS, | ||
852 | 0, ie_buf, ie_len, 0, GFP_KERNEL); | ||
853 | memcpy(priv->cfg_bssid, bss_info.bssid, ETH_ALEN); | ||
854 | |||
855 | return ret; | ||
856 | } | ||
857 | |||
858 | /* | ||
859 | * This function informs the CFG802.11 subsystem of a new BSS connection. | ||
860 | * | ||
861 | * The following information are sent to the CFG802.11 subsystem | ||
862 | * to register the new BSS connection. If we do not register the new BSS, | ||
863 | * a kernel panic will result. | ||
864 | * - MAC address | ||
865 | * - Capabilities | ||
866 | * - Beacon period | ||
867 | * - RSSI value | ||
868 | * - Channel | ||
869 | * - Supported rates IE | ||
870 | * - Extended capabilities IE | ||
871 | * - DS parameter set IE | ||
872 | * - HT Capability IE | ||
873 | * - Vendor Specific IE (221) | ||
874 | * - WPA IE | ||
875 | * - RSN IE | ||
876 | */ | ||
877 | static int mwifiex_inform_bss_from_scan_result(struct mwifiex_private *priv, | ||
878 | struct mwifiex_802_11_ssid *ssid) | ||
879 | { | ||
880 | struct mwifiex_scan_resp scan_resp; | ||
881 | struct mwifiex_bssdescriptor *scan_table; | ||
882 | int i, j; | ||
883 | struct ieee80211_channel *chan; | ||
884 | u8 *ie, *tmp, *ie_buf; | ||
885 | u32 ie_len; | ||
886 | u64 ts = 0; | ||
887 | u8 *beacon; | ||
888 | int beacon_size; | ||
889 | u8 element_id, element_len; | ||
890 | |||
891 | memset(&scan_resp, 0, sizeof(scan_resp)); | ||
892 | if (mwifiex_get_scan_table(priv, MWIFIEX_IOCTL_WAIT, &scan_resp)) | ||
893 | return -EFAULT; | ||
894 | |||
895 | #define MAX_IE_BUF 2048 | ||
896 | ie_buf = kzalloc(MAX_IE_BUF, GFP_KERNEL); | ||
897 | if (!ie_buf) { | ||
898 | dev_err(priv->adapter->dev, "%s: failed to alloc ie_buf\n", | ||
899 | __func__); | ||
900 | return -ENOMEM; | ||
901 | } | ||
902 | |||
903 | scan_table = (struct mwifiex_bssdescriptor *) scan_resp.scan_table; | ||
904 | for (i = 0; i < scan_resp.num_in_scan_table; i++) { | ||
905 | if (ssid) { | ||
906 | /* Inform specific BSS only */ | ||
907 | if (memcmp(ssid->ssid, scan_table[i].ssid.ssid, | ||
908 | ssid->ssid_len)) | ||
909 | continue; | ||
910 | } | ||
911 | memset(ie_buf, 0, MAX_IE_BUF); | ||
912 | ie_buf[0] = WLAN_EID_SSID; | ||
913 | ie_buf[1] = scan_table[i].ssid.ssid_len; | ||
914 | memcpy(&ie_buf[sizeof(struct ieee_types_header)], | ||
915 | scan_table[i].ssid.ssid, ie_buf[1]); | ||
916 | |||
917 | ie = ie_buf + ie_buf[1] + sizeof(struct ieee_types_header); | ||
918 | ie_len = ie_buf[1] + sizeof(struct ieee_types_header); | ||
919 | |||
920 | ie[0] = WLAN_EID_SUPP_RATES; | ||
921 | |||
922 | for (j = 0; j < sizeof(scan_table[i].supported_rates); j++) { | ||
923 | if (!scan_table[i].supported_rates[j]) | ||
924 | break; | ||
925 | else | ||
926 | ie[j + sizeof(struct ieee_types_header)] = | ||
927 | scan_table[i].supported_rates[j]; | ||
928 | } | ||
929 | |||
930 | ie[1] = j; | ||
931 | ie_len += ie[1] + sizeof(struct ieee_types_header); | ||
932 | |||
933 | beacon = scan_table[i].beacon_buf; | ||
934 | beacon_size = scan_table[i].beacon_buf_size; | ||
935 | |||
936 | /* Skip time stamp, beacon interval and capability */ | ||
937 | |||
938 | if (beacon) { | ||
939 | beacon += sizeof(scan_table[i].beacon_period) | ||
940 | + sizeof(scan_table[i].time_stamp) + | ||
941 | +sizeof(scan_table[i].cap_info_bitmap); | ||
942 | |||
943 | beacon_size -= sizeof(scan_table[i].beacon_period) | ||
944 | + sizeof(scan_table[i].time_stamp) | ||
945 | + sizeof(scan_table[i].cap_info_bitmap); | ||
946 | } | ||
947 | |||
948 | while (beacon_size >= sizeof(struct ieee_types_header)) { | ||
949 | ie = ie_buf + ie_len; | ||
950 | element_id = *beacon; | ||
951 | element_len = *(beacon + 1); | ||
952 | if (beacon_size < (int) element_len + | ||
953 | sizeof(struct ieee_types_header)) { | ||
954 | dev_err(priv->adapter->dev, "%s: in processing" | ||
955 | " IE, bytes left < IE length\n", | ||
956 | __func__); | ||
957 | break; | ||
958 | } | ||
959 | switch (element_id) { | ||
960 | case WLAN_EID_EXT_CAPABILITY: | ||
961 | case WLAN_EID_DS_PARAMS: | ||
962 | case WLAN_EID_HT_CAPABILITY: | ||
963 | case WLAN_EID_VENDOR_SPECIFIC: | ||
964 | case WLAN_EID_RSN: | ||
965 | case WLAN_EID_BSS_AC_ACCESS_DELAY: | ||
966 | ie[0] = element_id; | ||
967 | ie[1] = element_len; | ||
968 | tmp = (u8 *) beacon; | ||
969 | memcpy(&ie[sizeof(struct ieee_types_header)], | ||
970 | tmp + sizeof(struct ieee_types_header), | ||
971 | element_len); | ||
972 | ie_len += ie[1] + | ||
973 | sizeof(struct ieee_types_header); | ||
974 | break; | ||
975 | default: | ||
976 | break; | ||
977 | } | ||
978 | beacon += element_len + | ||
979 | sizeof(struct ieee_types_header); | ||
980 | beacon_size -= element_len + | ||
981 | sizeof(struct ieee_types_header); | ||
982 | } | ||
983 | chan = ieee80211_get_channel(priv->wdev->wiphy, | ||
984 | scan_table[i].freq); | ||
985 | cfg80211_inform_bss(priv->wdev->wiphy, chan, | ||
986 | scan_table[i].mac_address, | ||
987 | ts, scan_table[i].cap_info_bitmap, | ||
988 | scan_table[i].beacon_period, | ||
989 | ie_buf, ie_len, | ||
990 | scan_table[i].rssi, GFP_KERNEL); | ||
991 | } | ||
992 | |||
993 | kfree(ie_buf); | ||
994 | return 0; | ||
995 | } | ||
996 | |||
997 | /* | ||
998 | * This function connects with a BSS. | ||
999 | * | ||
1000 | * This function handles both Infra and Ad-Hoc modes. It also performs | ||
1001 | * validity checking on the provided parameters, disconnects from the | ||
1002 | * current BSS (if any), sets up the association/scan parameters, | ||
1003 | * including security settings, and performs specific SSID scan before | ||
1004 | * trying to connect. | ||
1005 | * | ||
1006 | * For Infra mode, the function returns failure if the specified SSID | ||
1007 | * is not found in scan table. However, for Ad-Hoc mode, it can create | ||
1008 | * the IBSS if it does not exist. On successful completion in either case, | ||
1009 | * the function notifies the CFG802.11 subsystem of the new BSS connection, | ||
1010 | * otherwise the kernel will panic. | ||
1011 | */ | ||
1012 | static int | ||
1013 | mwifiex_cfg80211_assoc(struct mwifiex_private *priv, size_t ssid_len, u8 *ssid, | ||
1014 | u8 *bssid, int mode, struct ieee80211_channel *channel, | ||
1015 | struct cfg80211_connect_params *sme, bool privacy) | ||
1016 | { | ||
1017 | struct mwifiex_802_11_ssid req_ssid; | ||
1018 | struct mwifiex_ssid_bssid ssid_bssid; | ||
1019 | int ret = 0; | ||
1020 | int auth_type = 0, pairwise_encrypt_mode = 0, wpa_enabled = 0; | ||
1021 | int group_encrypt_mode = 0; | ||
1022 | int alg_is_wep = 0; | ||
1023 | |||
1024 | memset(&req_ssid, 0, sizeof(struct mwifiex_802_11_ssid)); | ||
1025 | memset(&ssid_bssid, 0, sizeof(struct mwifiex_ssid_bssid)); | ||
1026 | |||
1027 | req_ssid.ssid_len = ssid_len; | ||
1028 | if (ssid_len > IEEE80211_MAX_SSID_LEN) { | ||
1029 | dev_err(priv->adapter->dev, "invalid SSID - aborting\n"); | ||
1030 | return -EINVAL; | ||
1031 | } | ||
1032 | |||
1033 | memcpy(req_ssid.ssid, ssid, ssid_len); | ||
1034 | if (!req_ssid.ssid_len || req_ssid.ssid[0] < 0x20) { | ||
1035 | dev_err(priv->adapter->dev, "invalid SSID - aborting\n"); | ||
1036 | return -EINVAL; | ||
1037 | } | ||
1038 | |||
1039 | /* disconnect before try to associate */ | ||
1040 | mwifiex_disconnect(priv, MWIFIEX_IOCTL_WAIT, NULL); | ||
1041 | |||
1042 | if (channel) | ||
1043 | ret = mwifiex_set_rf_channel(priv, channel, | ||
1044 | mwifiex_channels_to_cfg80211_channel_type | ||
1045 | (priv->adapter->chan_offset)); | ||
1046 | |||
1047 | ret = mwifiex_set_encode(priv, NULL, 0, 0, 1); /* Disable keys */ | ||
1048 | |||
1049 | if (mode == MWIFIEX_BSS_MODE_IBSS) { | ||
1050 | /* "privacy" is set only for ad-hoc mode */ | ||
1051 | if (privacy) { | ||
1052 | /* | ||
1053 | * Keep MWIFIEX_ENCRYPTION_MODE_WEP104 for now so that | ||
1054 | * the firmware can find a matching network from the | ||
1055 | * scan. The cfg80211 does not give us the encryption | ||
1056 | * mode at this stage so just setting it to WEP here. | ||
1057 | */ | ||
1058 | wpa_enabled = 0; | ||
1059 | auth_type = MWIFIEX_AUTH_MODE_OPEN; | ||
1060 | ret = mwifiex_set_auth(priv, | ||
1061 | MWIFIEX_ENCRYPTION_MODE_WEP104, | ||
1062 | auth_type, wpa_enabled); | ||
1063 | } | ||
1064 | |||
1065 | goto done; | ||
1066 | } | ||
1067 | |||
1068 | /* Now handle infra mode. "sme" is valid for infra mode only */ | ||
1069 | if (sme->auth_type == NL80211_AUTHTYPE_AUTOMATIC | ||
1070 | || sme->auth_type == NL80211_AUTHTYPE_OPEN_SYSTEM) | ||
1071 | auth_type = MWIFIEX_AUTH_MODE_OPEN; | ||
1072 | else if (sme->auth_type == NL80211_AUTHTYPE_SHARED_KEY) | ||
1073 | auth_type = MWIFIEX_AUTH_MODE_SHARED; | ||
1074 | |||
1075 | if (sme->crypto.n_ciphers_pairwise) { | ||
1076 | pairwise_encrypt_mode = mwifiex_get_mwifiex_cipher(sme->crypto. | ||
1077 | ciphers_pairwise[0], &wpa_enabled); | ||
1078 | ret = mwifiex_set_auth(priv, pairwise_encrypt_mode, auth_type, | ||
1079 | wpa_enabled); | ||
1080 | } | ||
1081 | |||
1082 | if (sme->crypto.cipher_group) { | ||
1083 | group_encrypt_mode = mwifiex_get_mwifiex_cipher(sme->crypto. | ||
1084 | cipher_group, &wpa_enabled); | ||
1085 | ret = mwifiex_set_auth(priv, group_encrypt_mode, auth_type, | ||
1086 | wpa_enabled); | ||
1087 | } | ||
1088 | if (sme->ie) | ||
1089 | ret = mwifiex_set_gen_ie(priv, sme->ie, sme->ie_len); | ||
1090 | |||
1091 | if (sme->key) { | ||
1092 | alg_is_wep = mwifiex_is_alg_wep(pairwise_encrypt_mode) | ||
1093 | | mwifiex_is_alg_wep(group_encrypt_mode); | ||
1094 | if (alg_is_wep) { | ||
1095 | dev_dbg(priv->adapter->dev, | ||
1096 | "info: setting wep encryption" | ||
1097 | " with key len %d\n", sme->key_len); | ||
1098 | ret = mwifiex_set_encode(priv, sme->key, sme->key_len, | ||
1099 | sme->key_idx, 0); | ||
1100 | } | ||
1101 | } | ||
1102 | done: | ||
1103 | /* Do specific SSID scanning */ | ||
1104 | if (mwifiex_request_scan(priv, MWIFIEX_IOCTL_WAIT, &req_ssid)) { | ||
1105 | dev_err(priv->adapter->dev, "scan error\n"); | ||
1106 | return -EFAULT; | ||
1107 | } | ||
1108 | |||
1109 | |||
1110 | memcpy(&ssid_bssid.ssid, &req_ssid, sizeof(struct mwifiex_802_11_ssid)); | ||
1111 | |||
1112 | if (mode != MWIFIEX_BSS_MODE_IBSS) { | ||
1113 | if (mwifiex_find_best_bss(priv, MWIFIEX_IOCTL_WAIT, | ||
1114 | &ssid_bssid)) | ||
1115 | return -EFAULT; | ||
1116 | /* Inform the BSS information to kernel, otherwise | ||
1117 | * kernel will give a panic after successful assoc */ | ||
1118 | if (mwifiex_inform_bss_from_scan_result(priv, &req_ssid)) | ||
1119 | return -EFAULT; | ||
1120 | } | ||
1121 | |||
1122 | dev_dbg(priv->adapter->dev, "info: trying to associate to %s and bssid %pM\n", | ||
1123 | (char *) req_ssid.ssid, ssid_bssid.bssid); | ||
1124 | |||
1125 | memcpy(&priv->cfg_bssid, ssid_bssid.bssid, 6); | ||
1126 | |||
1127 | /* Connect to BSS by ESSID */ | ||
1128 | memset(&ssid_bssid.bssid, 0, ETH_ALEN); | ||
1129 | |||
1130 | if (mwifiex_bss_start(priv, MWIFIEX_IOCTL_WAIT, &ssid_bssid)) | ||
1131 | return -EFAULT; | ||
1132 | |||
1133 | if (mode == MWIFIEX_BSS_MODE_IBSS) { | ||
1134 | /* Inform the BSS information to kernel, otherwise | ||
1135 | * kernel will give a panic after successful assoc */ | ||
1136 | if (mwifiex_cfg80211_inform_ibss_bss(priv)) | ||
1137 | return -EFAULT; | ||
1138 | } | ||
1139 | |||
1140 | return ret; | ||
1141 | } | ||
1142 | |||
1143 | /* | ||
1144 | * CFG802.11 operation handler for association request. | ||
1145 | * | ||
1146 | * This function does not work when the current mode is set to Ad-Hoc, or | ||
1147 | * when there is already an association procedure going on. The given BSS | ||
1148 | * information is used to associate. | ||
1149 | */ | ||
1150 | static int | ||
1151 | mwifiex_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev, | ||
1152 | struct cfg80211_connect_params *sme) | ||
1153 | { | ||
1154 | struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); | ||
1155 | int ret = 0; | ||
1156 | int mode = 0; | ||
1157 | |||
1158 | if (priv->assoc_request) | ||
1159 | return -EBUSY; | ||
1160 | |||
1161 | mode = mwifiex_drv_get_mode(priv, MWIFIEX_IOCTL_WAIT); | ||
1162 | |||
1163 | if (mode == MWIFIEX_BSS_MODE_IBSS) { | ||
1164 | wiphy_err(wiphy, "received infra assoc request " | ||
1165 | "when station is in ibss mode\n"); | ||
1166 | goto done; | ||
1167 | } | ||
1168 | |||
1169 | priv->assoc_request = 1; | ||
1170 | |||
1171 | wiphy_dbg(wiphy, "info: Trying to associate to %s and bssid %pM\n", | ||
1172 | (char *) sme->ssid, sme->bssid); | ||
1173 | |||
1174 | ret = mwifiex_cfg80211_assoc(priv, sme->ssid_len, sme->ssid, sme->bssid, | ||
1175 | mode, sme->channel, sme, 0); | ||
1176 | |||
1177 | done: | ||
1178 | priv->assoc_result = ret; | ||
1179 | queue_work(priv->workqueue, &priv->cfg_workqueue); | ||
1180 | return ret; | ||
1181 | } | ||
1182 | |||
1183 | /* | ||
1184 | * CFG802.11 operation handler to join an IBSS. | ||
1185 | * | ||
1186 | * This function does not work in any mode other than Ad-Hoc, or if | ||
1187 | * a join operation is already in progress. | ||
1188 | */ | ||
1189 | static int | ||
1190 | mwifiex_cfg80211_join_ibss(struct wiphy *wiphy, struct net_device *dev, | ||
1191 | struct cfg80211_ibss_params *params) | ||
1192 | { | ||
1193 | struct mwifiex_private *priv = mwifiex_cfg80211_get_priv(wiphy); | ||
1194 | int ret = 0; | ||
1195 | int mode = 0; | ||
1196 | |||
1197 | if (priv->ibss_join_request) | ||
1198 | return -EBUSY; | ||
1199 | |||
1200 | mode = mwifiex_drv_get_mode(priv, MWIFIEX_IOCTL_WAIT); | ||
1201 | if (mode != MWIFIEX_BSS_MODE_IBSS) { | ||
1202 | wiphy_err(wiphy, "request to join ibss received " | ||
1203 | "when station is not in ibss mode\n"); | ||
1204 | goto done; | ||
1205 | } | ||
1206 | |||
1207 | priv->ibss_join_request = 1; | ||
1208 | |||
1209 | wiphy_dbg(wiphy, "info: trying to join to %s and bssid %pM\n", | ||
1210 | (char *) params->ssid, params->bssid); | ||
1211 | |||
1212 | ret = mwifiex_cfg80211_assoc(priv, params->ssid_len, params->ssid, | ||
1213 | params->bssid, mode, params->channel, NULL, | ||
1214 | params->privacy); | ||
1215 | done: | ||
1216 | priv->ibss_join_result = ret; | ||
1217 | queue_work(priv->workqueue, &priv->cfg_workqueue); | ||
1218 | return ret; | ||
1219 | } | ||
1220 | |||
1221 | /* | ||
1222 | * CFG802.11 operation handler to leave an IBSS. | ||
1223 | * | ||
1224 | * This function does not work if a leave operation is | ||
1225 | * already in progress. | ||
1226 | */ | ||
1227 | static int | ||
1228 | mwifiex_cfg80211_leave_ibss(struct wiphy *wiphy, struct net_device *dev) | ||
1229 | { | ||
1230 | struct mwifiex_private *priv = mwifiex_cfg80211_get_priv(wiphy); | ||
1231 | |||
1232 | if (priv->disconnect) | ||
1233 | return -EBUSY; | ||
1234 | |||
1235 | priv->disconnect = 1; | ||
1236 | |||
1237 | wiphy_dbg(wiphy, "info: disconnecting from essid %pM\n", | ||
1238 | priv->cfg_bssid); | ||
1239 | if (mwifiex_disconnect(priv, MWIFIEX_IOCTL_WAIT, NULL)) | ||
1240 | return -EFAULT; | ||
1241 | |||
1242 | queue_work(priv->workqueue, &priv->cfg_workqueue); | ||
1243 | |||
1244 | return 0; | ||
1245 | } | ||
1246 | |||
1247 | /* | ||
1248 | * CFG802.11 operation handler for scan request. | ||
1249 | * | ||
1250 | * This function issues a scan request to the firmware based upon | ||
1251 | * the user specified scan configuration. On successfull completion, | ||
1252 | * it also informs the results. | ||
1253 | */ | ||
1254 | static int | ||
1255 | mwifiex_cfg80211_scan(struct wiphy *wiphy, struct net_device *dev, | ||
1256 | struct cfg80211_scan_request *request) | ||
1257 | { | ||
1258 | struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); | ||
1259 | |||
1260 | wiphy_dbg(wiphy, "info: received scan request on %s\n", dev->name); | ||
1261 | |||
1262 | if (priv->scan_request && priv->scan_request != request) | ||
1263 | return -EBUSY; | ||
1264 | |||
1265 | priv->scan_request = request; | ||
1266 | |||
1267 | queue_work(priv->workqueue, &priv->cfg_workqueue); | ||
1268 | return 0; | ||
1269 | } | ||
1270 | |||
1271 | /* | ||
1272 | * This function sets up the CFG802.11 specific HT capability fields | ||
1273 | * with default values. | ||
1274 | * | ||
1275 | * The following default values are set - | ||
1276 | * - HT Supported = True | ||
1277 | * - Maximum AMPDU length factor = 0x3 | ||
1278 | * - Minimum AMPDU spacing = 0x6 | ||
1279 | * - HT Capabilities map = IEEE80211_HT_CAP_SUP_WIDTH_20_40 (0x0002) | ||
1280 | * - MCS information, Rx mask = 0xff | ||
1281 | * - MCD information, Tx parameters = IEEE80211_HT_MCS_TX_DEFINED (0x01) | ||
1282 | */ | ||
1283 | static void | ||
1284 | mwifiex_setup_ht_caps(struct ieee80211_sta_ht_cap *ht_info, | ||
1285 | struct mwifiex_private *priv) | ||
1286 | { | ||
1287 | int rx_mcs_supp; | ||
1288 | struct ieee80211_mcs_info mcs_set; | ||
1289 | u8 *mcs = (u8 *)&mcs_set; | ||
1290 | struct mwifiex_adapter *adapter = priv->adapter; | ||
1291 | |||
1292 | ht_info->ht_supported = true; | ||
1293 | ht_info->ampdu_factor = 0x3; | ||
1294 | ht_info->ampdu_density = 0x6; | ||
1295 | |||
1296 | memset(&ht_info->mcs, 0, sizeof(ht_info->mcs)); | ||
1297 | ht_info->cap = IEEE80211_HT_CAP_SUP_WIDTH_20_40; | ||
1298 | |||
1299 | rx_mcs_supp = GET_RXMCSSUPP(priv->adapter->hw_dev_mcs_support); | ||
1300 | /* Set MCS for 1x1 */ | ||
1301 | memset(mcs, 0xff, rx_mcs_supp); | ||
1302 | /* Clear all the other values */ | ||
1303 | memset(&mcs[rx_mcs_supp], 0, | ||
1304 | sizeof(struct ieee80211_mcs_info) - rx_mcs_supp); | ||
1305 | if (priv->bss_mode == MWIFIEX_BSS_MODE_INFRA || | ||
1306 | (ISSUPP_CHANWIDTH40(adapter->hw_dot_11n_dev_cap) && | ||
1307 | ISSUPP_CHANWIDTH40(adapter->usr_dot_11n_dev_cap))) | ||
1308 | /* Set MCS32 for infra mode or ad-hoc mode with 40MHz support */ | ||
1309 | SETHT_MCS32(mcs_set.rx_mask); | ||
1310 | |||
1311 | memcpy((u8 *) &ht_info->mcs, mcs, sizeof(struct ieee80211_mcs_info)); | ||
1312 | |||
1313 | ht_info->mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED; | ||
1314 | } | ||
1315 | |||
1316 | /* station cfg80211 operations */ | ||
1317 | static struct cfg80211_ops mwifiex_cfg80211_ops = { | ||
1318 | .change_virtual_intf = mwifiex_cfg80211_change_virtual_intf, | ||
1319 | .scan = mwifiex_cfg80211_scan, | ||
1320 | .connect = mwifiex_cfg80211_connect, | ||
1321 | .disconnect = mwifiex_cfg80211_disconnect, | ||
1322 | .get_station = mwifiex_cfg80211_get_station, | ||
1323 | .set_wiphy_params = mwifiex_cfg80211_set_wiphy_params, | ||
1324 | .set_channel = mwifiex_cfg80211_set_channel, | ||
1325 | .join_ibss = mwifiex_cfg80211_join_ibss, | ||
1326 | .leave_ibss = mwifiex_cfg80211_leave_ibss, | ||
1327 | .add_key = mwifiex_cfg80211_add_key, | ||
1328 | .del_key = mwifiex_cfg80211_del_key, | ||
1329 | .set_default_key = mwifiex_cfg80211_set_default_key, | ||
1330 | .set_power_mgmt = mwifiex_cfg80211_set_power_mgmt, | ||
1331 | .set_tx_power = mwifiex_cfg80211_set_tx_power, | ||
1332 | }; | ||
1333 | |||
1334 | /* | ||
1335 | * This function registers the device with CFG802.11 subsystem. | ||
1336 | * | ||
1337 | * The function creates the wireless device/wiphy, populates it with | ||
1338 | * default parameters and handler function pointers, and finally | ||
1339 | * registers the device. | ||
1340 | */ | ||
1341 | int mwifiex_register_cfg80211(struct net_device *dev, u8 *mac, | ||
1342 | struct mwifiex_private *priv) | ||
1343 | { | ||
1344 | int ret = 0; | ||
1345 | void *wdev_priv = NULL; | ||
1346 | struct wireless_dev *wdev; | ||
1347 | |||
1348 | wdev = kzalloc(sizeof(struct wireless_dev), GFP_KERNEL); | ||
1349 | if (!wdev) { | ||
1350 | dev_err(priv->adapter->dev, "%s: allocating wireless device\n", | ||
1351 | __func__); | ||
1352 | return -ENOMEM; | ||
1353 | } | ||
1354 | wdev->wiphy = | ||
1355 | wiphy_new(&mwifiex_cfg80211_ops, | ||
1356 | sizeof(struct mwifiex_private *)); | ||
1357 | if (!wdev->wiphy) | ||
1358 | return -ENOMEM; | ||
1359 | wdev->iftype = NL80211_IFTYPE_STATION; | ||
1360 | wdev->wiphy->max_scan_ssids = 10; | ||
1361 | wdev->wiphy->interface_modes = | ||
1362 | BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_ADHOC); | ||
1363 | wdev->wiphy->bands[IEEE80211_BAND_2GHZ] = &mwifiex_band_2ghz; | ||
1364 | wdev->wiphy->bands[IEEE80211_BAND_5GHZ] = &mwifiex_band_5ghz; | ||
1365 | |||
1366 | /* Initialize cipher suits */ | ||
1367 | wdev->wiphy->cipher_suites = mwifiex_cipher_suites; | ||
1368 | wdev->wiphy->n_cipher_suites = ARRAY_SIZE(mwifiex_cipher_suites); | ||
1369 | |||
1370 | /* Initialize parameters for 2GHz band */ | ||
1371 | |||
1372 | mwifiex_setup_ht_caps(&wdev->wiphy->bands[IEEE80211_BAND_2GHZ]->ht_cap, | ||
1373 | priv); | ||
1374 | mwifiex_setup_ht_caps(&wdev->wiphy->bands[IEEE80211_BAND_5GHZ]->ht_cap, | ||
1375 | priv); | ||
1376 | |||
1377 | memcpy(wdev->wiphy->perm_addr, mac, 6); | ||
1378 | wdev->wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM; | ||
1379 | |||
1380 | /* We are using custom domains */ | ||
1381 | wdev->wiphy->flags |= WIPHY_FLAG_CUSTOM_REGULATORY; | ||
1382 | |||
1383 | wdev->wiphy->reg_notifier = mwifiex_reg_notifier; | ||
1384 | |||
1385 | /* Set struct mwifiex_private pointer in wiphy_priv */ | ||
1386 | wdev_priv = wiphy_priv(wdev->wiphy); | ||
1387 | |||
1388 | *(unsigned long *) wdev_priv = (unsigned long) priv; | ||
1389 | |||
1390 | ret = wiphy_register(wdev->wiphy); | ||
1391 | if (ret < 0) { | ||
1392 | dev_err(priv->adapter->dev, "%s: registering cfg80211 device\n", | ||
1393 | __func__); | ||
1394 | wiphy_free(wdev->wiphy); | ||
1395 | return ret; | ||
1396 | } else { | ||
1397 | dev_dbg(priv->adapter->dev, | ||
1398 | "info: successfully registered wiphy device\n"); | ||
1399 | } | ||
1400 | |||
1401 | dev_net_set(dev, wiphy_net(wdev->wiphy)); | ||
1402 | dev->ieee80211_ptr = wdev; | ||
1403 | memcpy(dev->dev_addr, wdev->wiphy->perm_addr, 6); | ||
1404 | memcpy(dev->perm_addr, wdev->wiphy->perm_addr, 6); | ||
1405 | SET_NETDEV_DEV(dev, wiphy_dev(wdev->wiphy)); | ||
1406 | priv->wdev = wdev; | ||
1407 | |||
1408 | dev->flags |= IFF_BROADCAST | IFF_MULTICAST; | ||
1409 | dev->watchdog_timeo = MWIFIEX_DEFAULT_WATCHDOG_TIMEOUT; | ||
1410 | dev->hard_header_len += MWIFIEX_MIN_DATA_HEADER_LEN; | ||
1411 | |||
1412 | return ret; | ||
1413 | } | ||
1414 | |||
1415 | /* | ||
1416 | * This function handles the result of different pending network operations. | ||
1417 | * | ||
1418 | * The following operations are handled and CFG802.11 subsystem is | ||
1419 | * notified accordingly - | ||
1420 | * - Scan request completion | ||
1421 | * - Association request completion | ||
1422 | * - IBSS join request completion | ||
1423 | * - Disconnect request completion | ||
1424 | */ | ||
1425 | void | ||
1426 | mwifiex_cfg80211_results(struct work_struct *work) | ||
1427 | { | ||
1428 | struct mwifiex_private *priv = | ||
1429 | container_of(work, struct mwifiex_private, cfg_workqueue); | ||
1430 | struct mwifiex_user_scan_cfg *scan_req; | ||
1431 | int ret = 0, i; | ||
1432 | struct ieee80211_channel *chan; | ||
1433 | |||
1434 | if (priv->scan_request) { | ||
1435 | scan_req = kzalloc(sizeof(struct mwifiex_user_scan_cfg), | ||
1436 | GFP_KERNEL); | ||
1437 | if (!scan_req) { | ||
1438 | dev_err(priv->adapter->dev, "failed to alloc " | ||
1439 | "scan_req\n"); | ||
1440 | return; | ||
1441 | } | ||
1442 | for (i = 0; i < priv->scan_request->n_ssids; i++) { | ||
1443 | memcpy(scan_req->ssid_list[i].ssid, | ||
1444 | priv->scan_request->ssids[i].ssid, | ||
1445 | priv->scan_request->ssids[i].ssid_len); | ||
1446 | scan_req->ssid_list[i].max_len = | ||
1447 | priv->scan_request->ssids[i].ssid_len; | ||
1448 | } | ||
1449 | for (i = 0; i < priv->scan_request->n_channels; i++) { | ||
1450 | chan = priv->scan_request->channels[i]; | ||
1451 | scan_req->chan_list[i].chan_number = chan->hw_value; | ||
1452 | scan_req->chan_list[i].radio_type = chan->band; | ||
1453 | if (chan->flags & IEEE80211_CHAN_DISABLED) | ||
1454 | scan_req->chan_list[i].scan_type = | ||
1455 | MWIFIEX_SCAN_TYPE_PASSIVE; | ||
1456 | else | ||
1457 | scan_req->chan_list[i].scan_type = | ||
1458 | MWIFIEX_SCAN_TYPE_ACTIVE; | ||
1459 | scan_req->chan_list[i].scan_time = 0; | ||
1460 | } | ||
1461 | if (mwifiex_set_user_scan_ioctl(priv, scan_req)) { | ||
1462 | ret = -EFAULT; | ||
1463 | goto done; | ||
1464 | } | ||
1465 | if (mwifiex_inform_bss_from_scan_result(priv, NULL)) | ||
1466 | ret = -EFAULT; | ||
1467 | done: | ||
1468 | priv->scan_result_status = ret; | ||
1469 | dev_dbg(priv->adapter->dev, "info: %s: sending scan results\n", | ||
1470 | __func__); | ||
1471 | cfg80211_scan_done(priv->scan_request, | ||
1472 | (priv->scan_result_status < 0)); | ||
1473 | priv->scan_request = NULL; | ||
1474 | kfree(scan_req); | ||
1475 | } | ||
1476 | |||
1477 | if (priv->assoc_request) { | ||
1478 | if (!priv->assoc_result) { | ||
1479 | cfg80211_connect_result(priv->netdev, priv->cfg_bssid, | ||
1480 | NULL, 0, NULL, 0, | ||
1481 | WLAN_STATUS_SUCCESS, | ||
1482 | GFP_KERNEL); | ||
1483 | dev_dbg(priv->adapter->dev, | ||
1484 | "info: associated to bssid %pM successfully\n", | ||
1485 | priv->cfg_bssid); | ||
1486 | } else { | ||
1487 | dev_dbg(priv->adapter->dev, | ||
1488 | "info: association to bssid %pM failed\n", | ||
1489 | priv->cfg_bssid); | ||
1490 | memset(priv->cfg_bssid, 0, ETH_ALEN); | ||
1491 | } | ||
1492 | priv->assoc_request = 0; | ||
1493 | priv->assoc_result = 0; | ||
1494 | } | ||
1495 | |||
1496 | if (priv->ibss_join_request) { | ||
1497 | if (!priv->ibss_join_result) { | ||
1498 | cfg80211_ibss_joined(priv->netdev, priv->cfg_bssid, | ||
1499 | GFP_KERNEL); | ||
1500 | dev_dbg(priv->adapter->dev, | ||
1501 | "info: joined/created adhoc network with bssid" | ||
1502 | " %pM successfully\n", priv->cfg_bssid); | ||
1503 | } else { | ||
1504 | dev_dbg(priv->adapter->dev, | ||
1505 | "info: failed creating/joining adhoc network\n"); | ||
1506 | } | ||
1507 | priv->ibss_join_request = 0; | ||
1508 | priv->ibss_join_result = 0; | ||
1509 | } | ||
1510 | |||
1511 | if (priv->disconnect) { | ||
1512 | memset(priv->cfg_bssid, 0, ETH_ALEN); | ||
1513 | priv->disconnect = 0; | ||
1514 | } | ||
1515 | |||
1516 | return; | ||
1517 | } | ||
diff --git a/drivers/net/wireless/mwifiex/cfg80211.h b/drivers/net/wireless/mwifiex/cfg80211.h new file mode 100644 index 000000000000..c4db8f36aa16 --- /dev/null +++ b/drivers/net/wireless/mwifiex/cfg80211.h | |||
@@ -0,0 +1,31 @@ | |||
1 | /* | ||
2 | * Marvell Wireless LAN device driver: CFG80211 | ||
3 | * | ||
4 | * Copyright (C) 2011, Marvell International Ltd. | ||
5 | * | ||
6 | * This software file (the "File") is distributed by Marvell International | ||
7 | * Ltd. under the terms of the GNU General Public License Version 2, June 1991 | ||
8 | * (the "License"). You may use, redistribute and/or modify this File in | ||
9 | * accordance with the terms and conditions of the License, a copy of which | ||
10 | * is available by writing to the Free Software Foundation, Inc., | ||
11 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the | ||
12 | * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. | ||
13 | * | ||
14 | * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE | ||
15 | * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE | ||
16 | * ARE EXPRESSLY DISCLAIMED. The License provides additional details about | ||
17 | * this warranty disclaimer. | ||
18 | */ | ||
19 | |||
20 | #ifndef __MWIFIEX_CFG80211__ | ||
21 | #define __MWIFIEX_CFG80211__ | ||
22 | |||
23 | #include <net/cfg80211.h> | ||
24 | |||
25 | #include "main.h" | ||
26 | |||
27 | int mwifiex_register_cfg80211(struct net_device *, u8 *, | ||
28 | struct mwifiex_private *); | ||
29 | |||
30 | void mwifiex_cfg80211_results(struct work_struct *work); | ||
31 | #endif | ||
diff --git a/drivers/net/wireless/mwifiex/cfp.c b/drivers/net/wireless/mwifiex/cfp.c new file mode 100644 index 000000000000..999ed81512fa --- /dev/null +++ b/drivers/net/wireless/mwifiex/cfp.c | |||
@@ -0,0 +1,368 @@ | |||
1 | /* | ||
2 | * Marvell Wireless LAN device driver: Channel, Frequence and Power | ||
3 | * | ||
4 | * Copyright (C) 2011, Marvell International Ltd. | ||
5 | * | ||
6 | * This software file (the "File") is distributed by Marvell International | ||
7 | * Ltd. under the terms of the GNU General Public License Version 2, June 1991 | ||
8 | * (the "License"). You may use, redistribute and/or modify this File in | ||
9 | * accordance with the terms and conditions of the License, a copy of which | ||
10 | * is available by writing to the Free Software Foundation, Inc., | ||
11 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the | ||
12 | * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. | ||
13 | * | ||
14 | * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE | ||
15 | * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE | ||
16 | * ARE EXPRESSLY DISCLAIMED. The License provides additional details about | ||
17 | * this warranty disclaimer. | ||
18 | */ | ||
19 | |||
20 | #include "decl.h" | ||
21 | #include "ioctl.h" | ||
22 | #include "util.h" | ||
23 | #include "fw.h" | ||
24 | #include "main.h" | ||
25 | #include "cfg80211.h" | ||
26 | |||
27 | /* 100mW */ | ||
28 | #define MWIFIEX_TX_PWR_DEFAULT 20 | ||
29 | /* 100mW */ | ||
30 | #define MWIFIEX_TX_PWR_US_DEFAULT 20 | ||
31 | /* 50mW */ | ||
32 | #define MWIFIEX_TX_PWR_JP_DEFAULT 16 | ||
33 | /* 100mW */ | ||
34 | #define MWIFIEX_TX_PWR_FR_100MW 20 | ||
35 | /* 10mW */ | ||
36 | #define MWIFIEX_TX_PWR_FR_10MW 10 | ||
37 | /* 100mW */ | ||
38 | #define MWIFIEX_TX_PWR_EMEA_DEFAULT 20 | ||
39 | |||
40 | static u8 adhoc_rates_b[B_SUPPORTED_RATES] = { 0x82, 0x84, 0x8b, 0x96, 0 }; | ||
41 | |||
42 | static u8 adhoc_rates_g[G_SUPPORTED_RATES] = { 0x8c, 0x12, 0x98, 0x24, | ||
43 | 0xb0, 0x48, 0x60, 0x6c, 0 }; | ||
44 | |||
45 | static u8 adhoc_rates_bg[BG_SUPPORTED_RATES] = { 0x82, 0x84, 0x8b, 0x96, | ||
46 | 0x0c, 0x12, 0x18, 0x24, | ||
47 | 0x30, 0x48, 0x60, 0x6c, 0 }; | ||
48 | |||
49 | static u8 adhoc_rates_a[A_SUPPORTED_RATES] = { 0x8c, 0x12, 0x98, 0x24, | ||
50 | 0xb0, 0x48, 0x60, 0x6c, 0 }; | ||
51 | u8 supported_rates_a[A_SUPPORTED_RATES] = { 0x0c, 0x12, 0x18, 0x24, | ||
52 | 0xb0, 0x48, 0x60, 0x6c, 0 }; | ||
53 | static u16 mwifiex_data_rates[MWIFIEX_SUPPORTED_RATES_EXT] = { 0x02, 0x04, | ||
54 | 0x0B, 0x16, 0x00, 0x0C, 0x12, 0x18, | ||
55 | 0x24, 0x30, 0x48, 0x60, 0x6C, 0x90, | ||
56 | 0x0D, 0x1A, 0x27, 0x34, 0x4E, 0x68, | ||
57 | 0x75, 0x82, 0x0C, 0x1B, 0x36, 0x51, | ||
58 | 0x6C, 0xA2, 0xD8, 0xF3, 0x10E, 0x00 }; | ||
59 | |||
60 | u8 supported_rates_b[B_SUPPORTED_RATES] = { 0x02, 0x04, 0x0b, 0x16, 0 }; | ||
61 | |||
62 | u8 supported_rates_g[G_SUPPORTED_RATES] = { 0x0c, 0x12, 0x18, 0x24, | ||
63 | 0x30, 0x48, 0x60, 0x6c, 0 }; | ||
64 | |||
65 | u8 supported_rates_bg[BG_SUPPORTED_RATES] = { 0x02, 0x04, 0x0b, 0x0c, | ||
66 | 0x12, 0x16, 0x18, 0x24, 0x30, 0x48, | ||
67 | 0x60, 0x6c, 0 }; | ||
68 | |||
69 | u16 region_code_index[MWIFIEX_MAX_REGION_CODE] = { 0x10, 0x20, 0x30, | ||
70 | 0x32, 0x40, 0x41, 0xff }; | ||
71 | |||
72 | u8 supported_rates_n[N_SUPPORTED_RATES] = { 0x02, 0x04, 0 }; | ||
73 | |||
74 | /* | ||
75 | * This function maps an index in supported rates table into | ||
76 | * the corresponding data rate. | ||
77 | */ | ||
78 | u32 mwifiex_index_to_data_rate(struct mwifiex_adapter *adapter, u8 index, | ||
79 | u8 ht_info) | ||
80 | { | ||
81 | u16 mcs_rate[4][8] = { | ||
82 | {0x1b, 0x36, 0x51, 0x6c, 0xa2, 0xd8, 0xf3, 0x10e} | ||
83 | , /* LG 40M */ | ||
84 | {0x1e, 0x3c, 0x5a, 0x78, 0xb4, 0xf0, 0x10e, 0x12c} | ||
85 | , /* SG 40M */ | ||
86 | {0x0d, 0x1a, 0x27, 0x34, 0x4e, 0x68, 0x75, 0x82} | ||
87 | , /* LG 20M */ | ||
88 | {0x0e, 0x1c, 0x2b, 0x39, 0x56, 0x73, 0x82, 0x90} | ||
89 | }; /* SG 20M */ | ||
90 | |||
91 | u32 rate; | ||
92 | |||
93 | if (ht_info & BIT(0)) { | ||
94 | if (index == MWIFIEX_RATE_BITMAP_MCS0) { | ||
95 | if (ht_info & BIT(2)) | ||
96 | rate = 0x0D; /* MCS 32 SGI rate */ | ||
97 | else | ||
98 | rate = 0x0C; /* MCS 32 LGI rate */ | ||
99 | } else if (index < 8) { | ||
100 | if (ht_info & BIT(1)) { | ||
101 | if (ht_info & BIT(2)) | ||
102 | /* SGI, 40M */ | ||
103 | rate = mcs_rate[1][index]; | ||
104 | else | ||
105 | /* LGI, 40M */ | ||
106 | rate = mcs_rate[0][index]; | ||
107 | } else { | ||
108 | if (ht_info & BIT(2)) | ||
109 | /* SGI, 20M */ | ||
110 | rate = mcs_rate[3][index]; | ||
111 | else | ||
112 | /* LGI, 20M */ | ||
113 | rate = mcs_rate[2][index]; | ||
114 | } | ||
115 | } else | ||
116 | rate = mwifiex_data_rates[0]; | ||
117 | } else { | ||
118 | if (index >= MWIFIEX_SUPPORTED_RATES_EXT) | ||
119 | index = 0; | ||
120 | rate = mwifiex_data_rates[index]; | ||
121 | } | ||
122 | return rate; | ||
123 | } | ||
124 | |||
125 | /* | ||
126 | * This function maps a data rate value into corresponding index in supported | ||
127 | * rates table. | ||
128 | */ | ||
129 | u8 mwifiex_data_rate_to_index(struct mwifiex_adapter *adapter, u32 rate) | ||
130 | { | ||
131 | u16 *ptr; | ||
132 | |||
133 | if (rate) { | ||
134 | ptr = memchr(mwifiex_data_rates, rate, | ||
135 | sizeof(mwifiex_data_rates)); | ||
136 | if (ptr) | ||
137 | return (u8) (ptr - mwifiex_data_rates); | ||
138 | } | ||
139 | return 0; | ||
140 | } | ||
141 | |||
142 | /* | ||
143 | * This function returns the current active data rates. | ||
144 | * | ||
145 | * The result may vary depending upon connection status. | ||
146 | */ | ||
147 | u32 mwifiex_get_active_data_rates(struct mwifiex_private *priv, u8 *rates) | ||
148 | { | ||
149 | u32 k; | ||
150 | |||
151 | if (!priv->media_connected) | ||
152 | k = mwifiex_get_supported_rates(priv, rates); | ||
153 | else | ||
154 | k = mwifiex_copy_rates(rates, 0, | ||
155 | priv->curr_bss_params.data_rates, | ||
156 | priv->curr_bss_params.num_of_rates); | ||
157 | |||
158 | return k; | ||
159 | } | ||
160 | |||
161 | /* | ||
162 | * This function locates the Channel-Frequency-Power triplet based upon | ||
163 | * band and channel parameters. | ||
164 | */ | ||
165 | struct mwifiex_chan_freq_power * | ||
166 | mwifiex_get_cfp_by_band_and_channel_from_cfg80211(struct mwifiex_private | ||
167 | *priv, u8 band, u16 channel) | ||
168 | { | ||
169 | struct mwifiex_chan_freq_power *cfp = NULL; | ||
170 | struct ieee80211_supported_band *sband; | ||
171 | struct ieee80211_channel *ch; | ||
172 | int i; | ||
173 | |||
174 | if (mwifiex_band_to_radio_type(band) == HostCmd_SCAN_RADIO_TYPE_BG) | ||
175 | sband = priv->wdev->wiphy->bands[IEEE80211_BAND_2GHZ]; | ||
176 | else | ||
177 | sband = priv->wdev->wiphy->bands[IEEE80211_BAND_5GHZ]; | ||
178 | |||
179 | if (!sband) { | ||
180 | dev_err(priv->adapter->dev, "%s: cannot find cfp by band %d" | ||
181 | " & channel %d\n", __func__, band, channel); | ||
182 | return cfp; | ||
183 | } | ||
184 | |||
185 | for (i = 0; i < sband->n_channels; i++) { | ||
186 | ch = &sband->channels[i]; | ||
187 | if (((ch->hw_value == channel) || | ||
188 | (channel == FIRST_VALID_CHANNEL)) | ||
189 | && !(ch->flags & IEEE80211_CHAN_DISABLED)) { | ||
190 | priv->cfp.channel = channel; | ||
191 | priv->cfp.freq = ch->center_freq; | ||
192 | priv->cfp.max_tx_power = ch->max_power; | ||
193 | cfp = &priv->cfp; | ||
194 | break; | ||
195 | } | ||
196 | } | ||
197 | if (i == sband->n_channels) | ||
198 | dev_err(priv->adapter->dev, "%s: cannot find cfp by band %d" | ||
199 | " & channel %d\n", __func__, band, channel); | ||
200 | |||
201 | return cfp; | ||
202 | } | ||
203 | |||
204 | /* | ||
205 | * This function locates the Channel-Frequency-Power triplet based upon | ||
206 | * band and frequency parameters. | ||
207 | */ | ||
208 | struct mwifiex_chan_freq_power * | ||
209 | mwifiex_get_cfp_by_band_and_freq_from_cfg80211(struct mwifiex_private *priv, | ||
210 | u8 band, u32 freq) | ||
211 | { | ||
212 | struct mwifiex_chan_freq_power *cfp = NULL; | ||
213 | struct ieee80211_supported_band *sband; | ||
214 | struct ieee80211_channel *ch; | ||
215 | int i; | ||
216 | |||
217 | if (mwifiex_band_to_radio_type(band) == HostCmd_SCAN_RADIO_TYPE_BG) | ||
218 | sband = priv->wdev->wiphy->bands[IEEE80211_BAND_2GHZ]; | ||
219 | else | ||
220 | sband = priv->wdev->wiphy->bands[IEEE80211_BAND_5GHZ]; | ||
221 | |||
222 | if (!sband) { | ||
223 | dev_err(priv->adapter->dev, "%s: cannot find cfp by band %d" | ||
224 | " & freq %d\n", __func__, band, freq); | ||
225 | return cfp; | ||
226 | } | ||
227 | |||
228 | for (i = 0; i < sband->n_channels; i++) { | ||
229 | ch = &sband->channels[i]; | ||
230 | if ((ch->center_freq == freq) && | ||
231 | !(ch->flags & IEEE80211_CHAN_DISABLED)) { | ||
232 | priv->cfp.channel = ch->hw_value; | ||
233 | priv->cfp.freq = freq; | ||
234 | priv->cfp.max_tx_power = ch->max_power; | ||
235 | cfp = &priv->cfp; | ||
236 | break; | ||
237 | } | ||
238 | } | ||
239 | if (i == sband->n_channels) | ||
240 | dev_err(priv->adapter->dev, "%s: cannot find cfp by band %d" | ||
241 | " & freq %d\n", __func__, band, freq); | ||
242 | |||
243 | return cfp; | ||
244 | } | ||
245 | |||
246 | /* | ||
247 | * This function checks if the data rate is set to auto. | ||
248 | */ | ||
249 | u8 | ||
250 | mwifiex_is_rate_auto(struct mwifiex_private *priv) | ||
251 | { | ||
252 | u32 i; | ||
253 | int rate_num = 0; | ||
254 | |||
255 | for (i = 0; i < ARRAY_SIZE(priv->bitmap_rates); i++) | ||
256 | if (priv->bitmap_rates[i]) | ||
257 | rate_num++; | ||
258 | |||
259 | if (rate_num > 1) | ||
260 | return true; | ||
261 | else | ||
262 | return false; | ||
263 | } | ||
264 | |||
265 | /* | ||
266 | * This function converts rate bitmap into rate index. | ||
267 | */ | ||
268 | int | ||
269 | mwifiex_get_rate_index(struct mwifiex_adapter *adapter, u16 *rate_bitmap, | ||
270 | int size) | ||
271 | { | ||
272 | int i; | ||
273 | |||
274 | for (i = 0; i < size * 8; i++) | ||
275 | if (rate_bitmap[i / 16] & (1 << (i % 16))) | ||
276 | return i; | ||
277 | |||
278 | return 0; | ||
279 | } | ||
280 | |||
281 | /* | ||
282 | * This function gets the supported data rates. | ||
283 | * | ||
284 | * The function works in both Ad-Hoc and infra mode by printing the | ||
285 | * band and returning the data rates. | ||
286 | */ | ||
287 | u32 mwifiex_get_supported_rates(struct mwifiex_private *priv, u8 *rates) | ||
288 | { | ||
289 | u32 k = 0; | ||
290 | struct mwifiex_adapter *adapter = priv->adapter; | ||
291 | if (priv->bss_mode == MWIFIEX_BSS_MODE_INFRA) { | ||
292 | /* Infra. mode */ | ||
293 | switch (adapter->config_bands) { | ||
294 | case BAND_B: | ||
295 | dev_dbg(adapter->dev, "info: infra band=%d " | ||
296 | "supported_rates_b\n", adapter->config_bands); | ||
297 | k = mwifiex_copy_rates(rates, k, supported_rates_b, | ||
298 | sizeof(supported_rates_b)); | ||
299 | break; | ||
300 | case BAND_G: | ||
301 | case BAND_G | BAND_GN: | ||
302 | dev_dbg(adapter->dev, "info: infra band=%d " | ||
303 | "supported_rates_g\n", adapter->config_bands); | ||
304 | k = mwifiex_copy_rates(rates, k, supported_rates_g, | ||
305 | sizeof(supported_rates_g)); | ||
306 | break; | ||
307 | case BAND_B | BAND_G: | ||
308 | case BAND_A | BAND_B | BAND_G: | ||
309 | case BAND_A | BAND_B: | ||
310 | case BAND_A | BAND_B | BAND_G | BAND_GN | BAND_AN: | ||
311 | case BAND_B | BAND_G | BAND_GN: | ||
312 | dev_dbg(adapter->dev, "info: infra band=%d " | ||
313 | "supported_rates_bg\n", adapter->config_bands); | ||
314 | k = mwifiex_copy_rates(rates, k, supported_rates_bg, | ||
315 | sizeof(supported_rates_bg)); | ||
316 | break; | ||
317 | case BAND_A: | ||
318 | case BAND_A | BAND_G: | ||
319 | dev_dbg(adapter->dev, "info: infra band=%d " | ||
320 | "supported_rates_a\n", adapter->config_bands); | ||
321 | k = mwifiex_copy_rates(rates, k, supported_rates_a, | ||
322 | sizeof(supported_rates_a)); | ||
323 | break; | ||
324 | case BAND_A | BAND_AN: | ||
325 | case BAND_A | BAND_G | BAND_AN | BAND_GN: | ||
326 | dev_dbg(adapter->dev, "info: infra band=%d " | ||
327 | "supported_rates_a\n", adapter->config_bands); | ||
328 | k = mwifiex_copy_rates(rates, k, supported_rates_a, | ||
329 | sizeof(supported_rates_a)); | ||
330 | break; | ||
331 | case BAND_GN: | ||
332 | dev_dbg(adapter->dev, "info: infra band=%d " | ||
333 | "supported_rates_n\n", adapter->config_bands); | ||
334 | k = mwifiex_copy_rates(rates, k, supported_rates_n, | ||
335 | sizeof(supported_rates_n)); | ||
336 | break; | ||
337 | } | ||
338 | } else { | ||
339 | /* Ad-hoc mode */ | ||
340 | switch (adapter->adhoc_start_band) { | ||
341 | case BAND_B: | ||
342 | dev_dbg(adapter->dev, "info: adhoc B\n"); | ||
343 | k = mwifiex_copy_rates(rates, k, adhoc_rates_b, | ||
344 | sizeof(adhoc_rates_b)); | ||
345 | break; | ||
346 | case BAND_G: | ||
347 | case BAND_G | BAND_GN: | ||
348 | dev_dbg(adapter->dev, "info: adhoc G only\n"); | ||
349 | k = mwifiex_copy_rates(rates, k, adhoc_rates_g, | ||
350 | sizeof(adhoc_rates_g)); | ||
351 | break; | ||
352 | case BAND_B | BAND_G: | ||
353 | case BAND_B | BAND_G | BAND_GN: | ||
354 | dev_dbg(adapter->dev, "info: adhoc BG\n"); | ||
355 | k = mwifiex_copy_rates(rates, k, adhoc_rates_bg, | ||
356 | sizeof(adhoc_rates_bg)); | ||
357 | break; | ||
358 | case BAND_A: | ||
359 | case BAND_A | BAND_AN: | ||
360 | dev_dbg(adapter->dev, "info: adhoc A\n"); | ||
361 | k = mwifiex_copy_rates(rates, k, adhoc_rates_a, | ||
362 | sizeof(adhoc_rates_a)); | ||
363 | break; | ||
364 | } | ||
365 | } | ||
366 | |||
367 | return k; | ||
368 | } | ||
diff --git a/drivers/net/wireless/mwifiex/cmdevt.c b/drivers/net/wireless/mwifiex/cmdevt.c new file mode 100644 index 000000000000..3a8fe1e122fb --- /dev/null +++ b/drivers/net/wireless/mwifiex/cmdevt.c | |||
@@ -0,0 +1,1463 @@ | |||
1 | /* | ||
2 | * Marvell Wireless LAN device driver: commands and events | ||
3 | * | ||
4 | * Copyright (C) 2011, Marvell International Ltd. | ||
5 | * | ||
6 | * This software file (the "File") is distributed by Marvell International | ||
7 | * Ltd. under the terms of the GNU General Public License Version 2, June 1991 | ||
8 | * (the "License"). You may use, redistribute and/or modify this File in | ||
9 | * accordance with the terms and conditions of the License, a copy of which | ||
10 | * is available by writing to the Free Software Foundation, Inc., | ||
11 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the | ||
12 | * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. | ||
13 | * | ||
14 | * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE | ||
15 | * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE | ||
16 | * ARE EXPRESSLY DISCLAIMED. The License provides additional details about | ||
17 | * this warranty disclaimer. | ||
18 | */ | ||
19 | |||
20 | #include "decl.h" | ||
21 | #include "ioctl.h" | ||
22 | #include "util.h" | ||
23 | #include "fw.h" | ||
24 | #include "main.h" | ||
25 | #include "wmm.h" | ||
26 | #include "11n.h" | ||
27 | |||
28 | /* | ||
29 | * This function initializes a command node. | ||
30 | * | ||
31 | * The actual allocation of the node is not done by this function. It only | ||
32 | * initiates a node by filling it with default parameters. Similarly, | ||
33 | * allocation of the different buffers used (IOCTL buffer, data buffer) are | ||
34 | * not done by this function either. | ||
35 | */ | ||
36 | static void | ||
37 | mwifiex_init_cmd_node(struct mwifiex_private *priv, | ||
38 | struct cmd_ctrl_node *cmd_node, | ||
39 | u32 cmd_oid, void *wait_queue, void *data_buf) | ||
40 | { | ||
41 | cmd_node->priv = priv; | ||
42 | cmd_node->cmd_oid = cmd_oid; | ||
43 | cmd_node->wq_buf = wait_queue; | ||
44 | cmd_node->data_buf = data_buf; | ||
45 | cmd_node->cmd_skb = cmd_node->skb; | ||
46 | } | ||
47 | |||
48 | /* | ||
49 | * This function returns a command node from the free queue depending upon | ||
50 | * availability. | ||
51 | */ | ||
52 | static struct cmd_ctrl_node * | ||
53 | mwifiex_get_cmd_node(struct mwifiex_adapter *adapter) | ||
54 | { | ||
55 | struct cmd_ctrl_node *cmd_node; | ||
56 | unsigned long flags; | ||
57 | |||
58 | spin_lock_irqsave(&adapter->cmd_free_q_lock, flags); | ||
59 | if (list_empty(&adapter->cmd_free_q)) { | ||
60 | dev_err(adapter->dev, "GET_CMD_NODE: cmd node not available\n"); | ||
61 | spin_unlock_irqrestore(&adapter->cmd_free_q_lock, flags); | ||
62 | return NULL; | ||
63 | } | ||
64 | cmd_node = list_first_entry(&adapter->cmd_free_q, | ||
65 | struct cmd_ctrl_node, list); | ||
66 | list_del(&cmd_node->list); | ||
67 | spin_unlock_irqrestore(&adapter->cmd_free_q_lock, flags); | ||
68 | |||
69 | return cmd_node; | ||
70 | } | ||
71 | |||
72 | /* | ||
73 | * This function cleans up a command node. | ||
74 | * | ||
75 | * The function resets the fields including the buffer pointers. | ||
76 | * This function does not try to free the buffers. They must be | ||
77 | * freed before calling this function. | ||
78 | * | ||
79 | * This function will however call the receive completion callback | ||
80 | * in case a response buffer is still available before resetting | ||
81 | * the pointer. | ||
82 | */ | ||
83 | static void | ||
84 | mwifiex_clean_cmd_node(struct mwifiex_adapter *adapter, | ||
85 | struct cmd_ctrl_node *cmd_node) | ||
86 | { | ||
87 | cmd_node->cmd_oid = 0; | ||
88 | cmd_node->cmd_flag = 0; | ||
89 | cmd_node->wq_buf = NULL; | ||
90 | cmd_node->data_buf = NULL; | ||
91 | |||
92 | if (cmd_node->resp_skb) { | ||
93 | mwifiex_recv_complete(adapter, cmd_node->resp_skb, 0); | ||
94 | cmd_node->resp_skb = NULL; | ||
95 | } | ||
96 | |||
97 | return; | ||
98 | } | ||
99 | |||
100 | /* | ||
101 | * This function returns a command node from the pending queue which | ||
102 | * matches the given IOCTL request. | ||
103 | */ | ||
104 | static struct cmd_ctrl_node * | ||
105 | mwifiex_get_pending_ioctl_cmd(struct mwifiex_adapter *adapter, | ||
106 | struct mwifiex_wait_queue *wait_queue) | ||
107 | { | ||
108 | unsigned long flags; | ||
109 | struct cmd_ctrl_node *cmd_node; | ||
110 | |||
111 | spin_lock_irqsave(&adapter->cmd_pending_q_lock, flags); | ||
112 | list_for_each_entry(cmd_node, &adapter->cmd_pending_q, list) { | ||
113 | if (cmd_node->wq_buf == wait_queue) { | ||
114 | spin_unlock_irqrestore(&adapter->cmd_pending_q_lock, | ||
115 | flags); | ||
116 | return cmd_node; | ||
117 | } | ||
118 | } | ||
119 | spin_unlock_irqrestore(&adapter->cmd_pending_q_lock, flags); | ||
120 | |||
121 | return NULL; | ||
122 | } | ||
123 | |||
124 | /* | ||
125 | * This function sends a host command to the firmware. | ||
126 | * | ||
127 | * The function copies the host command into the driver command | ||
128 | * buffer, which will be transferred to the firmware later by the | ||
129 | * main thread. | ||
130 | */ | ||
131 | static int mwifiex_cmd_host_cmd(struct mwifiex_private *priv, | ||
132 | struct host_cmd_ds_command *cmd, void *data_buf) | ||
133 | { | ||
134 | struct mwifiex_ds_misc_cmd *pcmd_ptr = | ||
135 | (struct mwifiex_ds_misc_cmd *) data_buf; | ||
136 | |||
137 | /* Copy the HOST command to command buffer */ | ||
138 | memcpy((void *) cmd, pcmd_ptr->cmd, pcmd_ptr->len); | ||
139 | dev_dbg(priv->adapter->dev, "cmd: host cmd size = %d\n", pcmd_ptr->len); | ||
140 | return 0; | ||
141 | } | ||
142 | |||
143 | /* | ||
144 | * This function downloads a command to the firmware. | ||
145 | * | ||
146 | * The function performs sanity tests, sets the command sequence | ||
147 | * number and size, converts the header fields to CPU format before | ||
148 | * sending. Afterwards, it logs the command ID and action for debugging | ||
149 | * and sets up the command timeout timer. | ||
150 | */ | ||
151 | static int mwifiex_dnld_cmd_to_fw(struct mwifiex_private *priv, | ||
152 | struct cmd_ctrl_node *cmd_node) | ||
153 | { | ||
154 | |||
155 | struct mwifiex_adapter *adapter = priv->adapter; | ||
156 | int ret = 0; | ||
157 | struct host_cmd_ds_command *host_cmd; | ||
158 | struct mwifiex_wait_queue *wait_queue = NULL; | ||
159 | uint16_t cmd_code; | ||
160 | uint16_t cmd_size; | ||
161 | struct timeval tstamp; | ||
162 | unsigned long flags; | ||
163 | |||
164 | if (!adapter || !cmd_node) | ||
165 | return -1; | ||
166 | |||
167 | host_cmd = (struct host_cmd_ds_command *) (cmd_node->cmd_skb->data); | ||
168 | if (cmd_node->wq_buf) | ||
169 | wait_queue = (struct mwifiex_wait_queue *) cmd_node->wq_buf; | ||
170 | |||
171 | /* Sanity test */ | ||
172 | if (host_cmd == NULL || host_cmd->size == 0) { | ||
173 | dev_err(adapter->dev, "DNLD_CMD: host_cmd is null" | ||
174 | " or cmd size is 0, not sending\n"); | ||
175 | if (wait_queue) | ||
176 | wait_queue->status = MWIFIEX_ERROR_CMD_DNLD_FAIL; | ||
177 | mwifiex_insert_cmd_to_free_q(adapter, cmd_node); | ||
178 | return -1; | ||
179 | } | ||
180 | |||
181 | /* Set command sequence number */ | ||
182 | adapter->seq_num++; | ||
183 | host_cmd->seq_num = cpu_to_le16(HostCmd_SET_SEQ_NO_BSS_INFO | ||
184 | (adapter->seq_num, cmd_node->priv->bss_num, | ||
185 | cmd_node->priv->bss_type)); | ||
186 | |||
187 | spin_lock_irqsave(&adapter->mwifiex_cmd_lock, flags); | ||
188 | adapter->curr_cmd = cmd_node; | ||
189 | spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, flags); | ||
190 | |||
191 | cmd_code = le16_to_cpu(host_cmd->command); | ||
192 | cmd_size = le16_to_cpu(host_cmd->size); | ||
193 | |||
194 | skb_trim(cmd_node->cmd_skb, cmd_size); | ||
195 | |||
196 | do_gettimeofday(&tstamp); | ||
197 | dev_dbg(adapter->dev, "cmd: DNLD_CMD: (%lu.%lu): %#x, act %#x, len %d," | ||
198 | " seqno %#x\n", | ||
199 | tstamp.tv_sec, tstamp.tv_usec, cmd_code, | ||
200 | le16_to_cpu(*(__le16 *) ((u8 *) host_cmd + S_DS_GEN)), cmd_size, | ||
201 | le16_to_cpu(host_cmd->seq_num)); | ||
202 | |||
203 | skb_push(cmd_node->cmd_skb, INTF_HEADER_LEN); | ||
204 | |||
205 | ret = adapter->if_ops.host_to_card(adapter, MWIFIEX_TYPE_CMD, | ||
206 | cmd_node->cmd_skb->data, | ||
207 | cmd_node->cmd_skb->len, NULL); | ||
208 | |||
209 | if (ret == -1) { | ||
210 | dev_err(adapter->dev, "DNLD_CMD: host to card failed\n"); | ||
211 | if (wait_queue) | ||
212 | wait_queue->status = MWIFIEX_ERROR_CMD_DNLD_FAIL; | ||
213 | mwifiex_insert_cmd_to_free_q(adapter, adapter->curr_cmd); | ||
214 | |||
215 | spin_lock_irqsave(&adapter->mwifiex_cmd_lock, flags); | ||
216 | adapter->curr_cmd = NULL; | ||
217 | spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, flags); | ||
218 | |||
219 | adapter->dbg.num_cmd_host_to_card_failure++; | ||
220 | return -1; | ||
221 | } | ||
222 | |||
223 | /* Save the last command id and action to debug log */ | ||
224 | adapter->dbg.last_cmd_index = | ||
225 | (adapter->dbg.last_cmd_index + 1) % DBG_CMD_NUM; | ||
226 | adapter->dbg.last_cmd_id[adapter->dbg.last_cmd_index] = cmd_code; | ||
227 | adapter->dbg.last_cmd_act[adapter->dbg.last_cmd_index] = | ||
228 | le16_to_cpu(*(__le16 *) ((u8 *) host_cmd + S_DS_GEN)); | ||
229 | |||
230 | /* Clear BSS_NO_BITS from HostCmd */ | ||
231 | cmd_code &= HostCmd_CMD_ID_MASK; | ||
232 | |||
233 | /* Setup the timer after transmit command */ | ||
234 | mod_timer(&adapter->cmd_timer, | ||
235 | jiffies + (MWIFIEX_TIMER_10S * HZ) / 1000); | ||
236 | |||
237 | return 0; | ||
238 | } | ||
239 | |||
240 | /* | ||
241 | * This function downloads a sleep confirm command to the firmware. | ||
242 | * | ||
243 | * The function performs sanity tests, sets the command sequence | ||
244 | * number and size, converts the header fields to CPU format before | ||
245 | * sending. | ||
246 | * | ||
247 | * No responses are needed for sleep confirm command. | ||
248 | */ | ||
249 | static int mwifiex_dnld_sleep_confirm_cmd(struct mwifiex_adapter *adapter) | ||
250 | { | ||
251 | int ret = 0; | ||
252 | u16 cmd_len = 0; | ||
253 | struct mwifiex_private *priv; | ||
254 | struct mwifiex_opt_sleep_confirm_buffer *sleep_cfm_buf = | ||
255 | (struct mwifiex_opt_sleep_confirm_buffer *) | ||
256 | adapter->sleep_cfm->data; | ||
257 | cmd_len = sizeof(struct mwifiex_opt_sleep_confirm); | ||
258 | priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY); | ||
259 | |||
260 | sleep_cfm_buf->ps_cfm_sleep.seq_num = | ||
261 | cpu_to_le16((HostCmd_SET_SEQ_NO_BSS_INFO | ||
262 | (adapter->seq_num, priv->bss_num, | ||
263 | priv->bss_type))); | ||
264 | adapter->seq_num++; | ||
265 | |||
266 | ret = adapter->if_ops.host_to_card(adapter, MWIFIEX_TYPE_CMD, | ||
267 | adapter->sleep_cfm->data, | ||
268 | adapter->sleep_cfm->len + | ||
269 | INTF_HEADER_LEN, NULL); | ||
270 | |||
271 | if (ret == -1) { | ||
272 | dev_err(adapter->dev, "SLEEP_CFM: failed\n"); | ||
273 | adapter->dbg.num_cmd_sleep_cfm_host_to_card_failure++; | ||
274 | return -1; | ||
275 | } | ||
276 | if (GET_BSS_ROLE(mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY)) | ||
277 | == MWIFIEX_BSS_ROLE_STA) { | ||
278 | if (!sleep_cfm_buf->ps_cfm_sleep.sleep_cfm.resp_ctrl) | ||
279 | /* Response is not needed for sleep | ||
280 | confirm command */ | ||
281 | adapter->ps_state = PS_STATE_SLEEP; | ||
282 | else | ||
283 | adapter->ps_state = PS_STATE_SLEEP_CFM; | ||
284 | |||
285 | if (!sleep_cfm_buf->ps_cfm_sleep.sleep_cfm.resp_ctrl | ||
286 | && (adapter->is_hs_configured | ||
287 | && !adapter->sleep_period.period)) { | ||
288 | adapter->pm_wakeup_card_req = true; | ||
289 | mwifiex_hs_activated_event(mwifiex_get_priv(adapter, | ||
290 | MWIFIEX_BSS_ROLE_STA), true); | ||
291 | } | ||
292 | } | ||
293 | |||
294 | return ret; | ||
295 | } | ||
296 | |||
297 | /* | ||
298 | * This function allocates the command buffers and links them to | ||
299 | * the command free queue. | ||
300 | * | ||
301 | * The driver uses a pre allocated number of command buffers, which | ||
302 | * are created at driver initializations and freed at driver cleanup. | ||
303 | * Every command needs to obtain a command buffer from this pool before | ||
304 | * it can be issued. The command free queue lists the command buffers | ||
305 | * currently free to use, while the command pending queue lists the | ||
306 | * command buffers already in use and awaiting handling. Command buffers | ||
307 | * are returned to the free queue after use. | ||
308 | */ | ||
309 | int mwifiex_alloc_cmd_buffer(struct mwifiex_adapter *adapter) | ||
310 | { | ||
311 | struct cmd_ctrl_node *cmd_array; | ||
312 | u32 buf_size; | ||
313 | u32 i; | ||
314 | |||
315 | /* Allocate and initialize struct cmd_ctrl_node */ | ||
316 | buf_size = sizeof(struct cmd_ctrl_node) * MWIFIEX_NUM_OF_CMD_BUFFER; | ||
317 | cmd_array = kzalloc(buf_size, GFP_KERNEL); | ||
318 | if (!cmd_array) { | ||
319 | dev_err(adapter->dev, "%s: failed to alloc cmd_array\n", | ||
320 | __func__); | ||
321 | return -1; | ||
322 | } | ||
323 | |||
324 | adapter->cmd_pool = cmd_array; | ||
325 | memset(adapter->cmd_pool, 0, buf_size); | ||
326 | |||
327 | /* Allocate and initialize command buffers */ | ||
328 | for (i = 0; i < MWIFIEX_NUM_OF_CMD_BUFFER; i++) { | ||
329 | cmd_array[i].skb = dev_alloc_skb(MWIFIEX_SIZE_OF_CMD_BUFFER); | ||
330 | if (!cmd_array[i].skb) { | ||
331 | dev_err(adapter->dev, "ALLOC_CMD_BUF: out of memory\n"); | ||
332 | return -1; | ||
333 | } | ||
334 | } | ||
335 | |||
336 | for (i = 0; i < MWIFIEX_NUM_OF_CMD_BUFFER; i++) | ||
337 | mwifiex_insert_cmd_to_free_q(adapter, &cmd_array[i]); | ||
338 | |||
339 | return 0; | ||
340 | } | ||
341 | |||
342 | /* | ||
343 | * This function frees the command buffers. | ||
344 | * | ||
345 | * The function calls the completion callback for all the command | ||
346 | * buffers that still have response buffers associated with them. | ||
347 | */ | ||
348 | int mwifiex_free_cmd_buffer(struct mwifiex_adapter *adapter) | ||
349 | { | ||
350 | struct cmd_ctrl_node *cmd_array; | ||
351 | u32 i; | ||
352 | |||
353 | /* Need to check if cmd pool is allocated or not */ | ||
354 | if (!adapter->cmd_pool) { | ||
355 | dev_dbg(adapter->dev, "info: FREE_CMD_BUF: cmd_pool is null\n"); | ||
356 | return 0; | ||
357 | } | ||
358 | |||
359 | cmd_array = adapter->cmd_pool; | ||
360 | |||
361 | /* Release shared memory buffers */ | ||
362 | for (i = 0; i < MWIFIEX_NUM_OF_CMD_BUFFER; i++) { | ||
363 | if (cmd_array[i].skb) { | ||
364 | dev_dbg(adapter->dev, "cmd: free cmd buffer %d\n", i); | ||
365 | dev_kfree_skb_any(cmd_array[i].skb); | ||
366 | } | ||
367 | if (!cmd_array[i].resp_skb) | ||
368 | continue; | ||
369 | mwifiex_recv_complete(adapter, cmd_array[i].resp_skb, 0); | ||
370 | } | ||
371 | /* Release struct cmd_ctrl_node */ | ||
372 | if (adapter->cmd_pool) { | ||
373 | dev_dbg(adapter->dev, "cmd: free cmd pool\n"); | ||
374 | kfree(adapter->cmd_pool); | ||
375 | adapter->cmd_pool = NULL; | ||
376 | } | ||
377 | |||
378 | return 0; | ||
379 | } | ||
380 | |||
381 | /* | ||
382 | * This function handles events generated by firmware. | ||
383 | * | ||
384 | * Event body of events received from firmware are not used (though they are | ||
385 | * saved), only the event ID is used. Some events are re-invoked by | ||
386 | * the driver, with a new event body. | ||
387 | * | ||
388 | * After processing, the function calls the completion callback | ||
389 | * for cleanup. | ||
390 | */ | ||
391 | int mwifiex_process_event(struct mwifiex_adapter *adapter) | ||
392 | { | ||
393 | int ret = 0; | ||
394 | struct mwifiex_private *priv = | ||
395 | mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY); | ||
396 | struct sk_buff *skb = adapter->event_skb; | ||
397 | u32 eventcause = adapter->event_cause; | ||
398 | struct timeval tstamp; | ||
399 | struct mwifiex_rxinfo *rx_info = NULL; | ||
400 | |||
401 | /* Save the last event to debug log */ | ||
402 | adapter->dbg.last_event_index = | ||
403 | (adapter->dbg.last_event_index + 1) % DBG_CMD_NUM; | ||
404 | adapter->dbg.last_event[adapter->dbg.last_event_index] = | ||
405 | (u16) eventcause; | ||
406 | |||
407 | /* Get BSS number and corresponding priv */ | ||
408 | priv = mwifiex_get_priv_by_id(adapter, EVENT_GET_BSS_NUM(eventcause), | ||
409 | EVENT_GET_BSS_TYPE(eventcause)); | ||
410 | if (!priv) | ||
411 | priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY); | ||
412 | /* Clear BSS_NO_BITS from event */ | ||
413 | eventcause &= EVENT_ID_MASK; | ||
414 | adapter->event_cause = eventcause; | ||
415 | |||
416 | if (skb) { | ||
417 | rx_info = MWIFIEX_SKB_RXCB(skb); | ||
418 | rx_info->bss_index = priv->bss_index; | ||
419 | } | ||
420 | |||
421 | if (eventcause != EVENT_PS_SLEEP && eventcause != EVENT_PS_AWAKE) { | ||
422 | do_gettimeofday(&tstamp); | ||
423 | dev_dbg(adapter->dev, "event: %lu.%lu: cause: %#x\n", | ||
424 | tstamp.tv_sec, tstamp.tv_usec, eventcause); | ||
425 | } | ||
426 | |||
427 | ret = mwifiex_process_sta_event(priv); | ||
428 | |||
429 | adapter->event_cause = 0; | ||
430 | adapter->event_skb = NULL; | ||
431 | |||
432 | mwifiex_recv_complete(adapter, skb, 0); | ||
433 | |||
434 | return ret; | ||
435 | } | ||
436 | |||
437 | /* | ||
438 | * This function prepares a command before sending it to the firmware. | ||
439 | * | ||
440 | * Preparation includes - | ||
441 | * - Sanity tests to make sure the card is still present or the FW | ||
442 | * is not reset | ||
443 | * - Getting a new command node from the command free queue | ||
444 | * - Initializing the command node for default parameters | ||
445 | * - Fill up the non-default parameters and buffer pointers | ||
446 | * - Add the command to pending queue | ||
447 | */ | ||
448 | int mwifiex_prepare_cmd(struct mwifiex_private *priv, uint16_t cmd_no, | ||
449 | u16 cmd_action, u32 cmd_oid, | ||
450 | void *wait_queue, void *data_buf) | ||
451 | { | ||
452 | int ret = 0; | ||
453 | struct mwifiex_adapter *adapter = priv->adapter; | ||
454 | struct cmd_ctrl_node *cmd_node = NULL; | ||
455 | struct host_cmd_ds_command *cmd_ptr = NULL; | ||
456 | |||
457 | if (!adapter) { | ||
458 | pr_err("PREP_CMD: adapter is NULL\n"); | ||
459 | return -1; | ||
460 | } | ||
461 | |||
462 | if (adapter->is_suspended) { | ||
463 | dev_err(adapter->dev, "PREP_CMD: device in suspended state\n"); | ||
464 | return -1; | ||
465 | } | ||
466 | |||
467 | if (adapter->surprise_removed) { | ||
468 | dev_err(adapter->dev, "PREP_CMD: card is removed\n"); | ||
469 | return -1; | ||
470 | } | ||
471 | |||
472 | if (adapter->hw_status == MWIFIEX_HW_STATUS_RESET) { | ||
473 | if (cmd_no != HostCmd_CMD_FUNC_INIT) { | ||
474 | dev_err(adapter->dev, "PREP_CMD: FW in reset state\n"); | ||
475 | return -1; | ||
476 | } | ||
477 | } | ||
478 | |||
479 | /* Get a new command node */ | ||
480 | cmd_node = mwifiex_get_cmd_node(adapter); | ||
481 | |||
482 | if (!cmd_node) { | ||
483 | dev_err(adapter->dev, "PREP_CMD: no free cmd node\n"); | ||
484 | return -1; | ||
485 | } | ||
486 | |||
487 | /* Initialize the command node */ | ||
488 | mwifiex_init_cmd_node(priv, cmd_node, cmd_oid, wait_queue, data_buf); | ||
489 | |||
490 | if (!cmd_node->cmd_skb) { | ||
491 | dev_err(adapter->dev, "PREP_CMD: no free cmd buf\n"); | ||
492 | return -1; | ||
493 | } | ||
494 | |||
495 | memset(skb_put(cmd_node->cmd_skb, sizeof(struct host_cmd_ds_command)), | ||
496 | 0, sizeof(struct host_cmd_ds_command)); | ||
497 | |||
498 | cmd_ptr = (struct host_cmd_ds_command *) (cmd_node->cmd_skb->data); | ||
499 | cmd_ptr->command = cpu_to_le16(cmd_no); | ||
500 | cmd_ptr->result = 0; | ||
501 | |||
502 | /* Prepare command */ | ||
503 | if (cmd_no) { | ||
504 | ret = mwifiex_sta_prepare_cmd(priv, cmd_no, cmd_action, | ||
505 | cmd_oid, data_buf, cmd_ptr); | ||
506 | } else { | ||
507 | ret = mwifiex_cmd_host_cmd(priv, cmd_ptr, data_buf); | ||
508 | cmd_node->cmd_flag |= CMD_F_HOSTCMD; | ||
509 | } | ||
510 | |||
511 | /* Return error, since the command preparation failed */ | ||
512 | if (ret) { | ||
513 | dev_err(adapter->dev, "PREP_CMD: cmd %#x preparation failed\n", | ||
514 | cmd_no); | ||
515 | mwifiex_insert_cmd_to_free_q(adapter, cmd_node); | ||
516 | return -1; | ||
517 | } | ||
518 | |||
519 | /* Send command */ | ||
520 | if (cmd_no == HostCmd_CMD_802_11_SCAN) | ||
521 | mwifiex_queue_scan_cmd(priv, cmd_node); | ||
522 | else | ||
523 | mwifiex_insert_cmd_to_pending_q(adapter, cmd_node, true); | ||
524 | |||
525 | return ret; | ||
526 | } | ||
527 | |||
528 | /* | ||
529 | * This function returns a command to the command free queue. | ||
530 | * | ||
531 | * The function also calls the completion callback if required, before | ||
532 | * cleaning the command node and re-inserting it into the free queue. | ||
533 | */ | ||
534 | void | ||
535 | mwifiex_insert_cmd_to_free_q(struct mwifiex_adapter *adapter, | ||
536 | struct cmd_ctrl_node *cmd_node) | ||
537 | { | ||
538 | struct mwifiex_wait_queue *wait_queue = NULL; | ||
539 | unsigned long flags; | ||
540 | |||
541 | if (cmd_node == NULL) | ||
542 | return; | ||
543 | if (cmd_node->wq_buf) { | ||
544 | wait_queue = (struct mwifiex_wait_queue *) cmd_node->wq_buf; | ||
545 | if (wait_queue->status != MWIFIEX_ERROR_NO_ERROR) | ||
546 | mwifiex_ioctl_complete(adapter, wait_queue, -1); | ||
547 | else | ||
548 | mwifiex_ioctl_complete(adapter, wait_queue, 0); | ||
549 | } | ||
550 | /* Clean the node */ | ||
551 | mwifiex_clean_cmd_node(adapter, cmd_node); | ||
552 | |||
553 | /* Insert node into cmd_free_q */ | ||
554 | spin_lock_irqsave(&adapter->cmd_free_q_lock, flags); | ||
555 | list_add_tail(&cmd_node->list, &adapter->cmd_free_q); | ||
556 | spin_unlock_irqrestore(&adapter->cmd_free_q_lock, flags); | ||
557 | |||
558 | return; | ||
559 | } | ||
560 | |||
561 | /* | ||
562 | * This function queues a command to the command pending queue. | ||
563 | * | ||
564 | * This in effect adds the command to the command list to be executed. | ||
565 | * Exit PS command is handled specially, by placing it always to the | ||
566 | * front of the command queue. | ||
567 | */ | ||
568 | void | ||
569 | mwifiex_insert_cmd_to_pending_q(struct mwifiex_adapter *adapter, | ||
570 | struct cmd_ctrl_node *cmd_node, u32 add_tail) | ||
571 | { | ||
572 | struct host_cmd_ds_command *host_cmd = NULL; | ||
573 | u16 command; | ||
574 | unsigned long flags; | ||
575 | |||
576 | host_cmd = (struct host_cmd_ds_command *) (cmd_node->cmd_skb->data); | ||
577 | if (!host_cmd) { | ||
578 | dev_err(adapter->dev, "QUEUE_CMD: host_cmd is NULL\n"); | ||
579 | return; | ||
580 | } | ||
581 | |||
582 | command = le16_to_cpu(host_cmd->command); | ||
583 | |||
584 | /* Exit_PS command needs to be queued in the header always. */ | ||
585 | if (command == HostCmd_CMD_802_11_PS_MODE_ENH) { | ||
586 | struct host_cmd_ds_802_11_ps_mode_enh *pm = | ||
587 | &host_cmd->params.psmode_enh; | ||
588 | if ((le16_to_cpu(pm->action) == DIS_PS) | ||
589 | || (le16_to_cpu(pm->action) == DIS_AUTO_PS)) { | ||
590 | if (adapter->ps_state != PS_STATE_AWAKE) | ||
591 | add_tail = false; | ||
592 | } | ||
593 | } | ||
594 | |||
595 | spin_lock_irqsave(&adapter->cmd_pending_q_lock, flags); | ||
596 | if (add_tail) | ||
597 | list_add_tail(&cmd_node->list, &adapter->cmd_pending_q); | ||
598 | else | ||
599 | list_add(&cmd_node->list, &adapter->cmd_pending_q); | ||
600 | spin_unlock_irqrestore(&adapter->cmd_pending_q_lock, flags); | ||
601 | |||
602 | dev_dbg(adapter->dev, "cmd: QUEUE_CMD: cmd=%#x is queued\n", command); | ||
603 | |||
604 | return; | ||
605 | } | ||
606 | |||
607 | /* | ||
608 | * This function executes the next command in command pending queue. | ||
609 | * | ||
610 | * This function will fail if a command is already in processing stage, | ||
611 | * otherwise it will dequeue the first command from the command pending | ||
612 | * queue and send to the firmware. | ||
613 | * | ||
614 | * If the device is currently in host sleep mode, any commands, except the | ||
615 | * host sleep configuration command will de-activate the host sleep. For PS | ||
616 | * mode, the function will put the firmware back to sleep if applicable. | ||
617 | */ | ||
618 | int mwifiex_exec_next_cmd(struct mwifiex_adapter *adapter) | ||
619 | { | ||
620 | struct mwifiex_private *priv = NULL; | ||
621 | struct cmd_ctrl_node *cmd_node = NULL; | ||
622 | int ret = 0; | ||
623 | struct host_cmd_ds_command *host_cmd; | ||
624 | unsigned long cmd_flags; | ||
625 | unsigned long cmd_pending_q_flags; | ||
626 | |||
627 | /* Check if already in processing */ | ||
628 | if (adapter->curr_cmd) { | ||
629 | dev_err(adapter->dev, "EXEC_NEXT_CMD: cmd in processing\n"); | ||
630 | return -1; | ||
631 | } | ||
632 | |||
633 | spin_lock_irqsave(&adapter->mwifiex_cmd_lock, cmd_flags); | ||
634 | /* Check if any command is pending */ | ||
635 | spin_lock_irqsave(&adapter->cmd_pending_q_lock, cmd_pending_q_flags); | ||
636 | if (list_empty(&adapter->cmd_pending_q)) { | ||
637 | spin_unlock_irqrestore(&adapter->cmd_pending_q_lock, | ||
638 | cmd_pending_q_flags); | ||
639 | spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, cmd_flags); | ||
640 | return 0; | ||
641 | } | ||
642 | cmd_node = list_first_entry(&adapter->cmd_pending_q, | ||
643 | struct cmd_ctrl_node, list); | ||
644 | spin_unlock_irqrestore(&adapter->cmd_pending_q_lock, | ||
645 | cmd_pending_q_flags); | ||
646 | |||
647 | host_cmd = (struct host_cmd_ds_command *) (cmd_node->cmd_skb->data); | ||
648 | priv = cmd_node->priv; | ||
649 | |||
650 | if (adapter->ps_state != PS_STATE_AWAKE) { | ||
651 | dev_err(adapter->dev, "%s: cannot send cmd in sleep state," | ||
652 | " this should not happen\n", __func__); | ||
653 | spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, cmd_flags); | ||
654 | return ret; | ||
655 | } | ||
656 | |||
657 | spin_lock_irqsave(&adapter->cmd_pending_q_lock, cmd_pending_q_flags); | ||
658 | list_del(&cmd_node->list); | ||
659 | spin_unlock_irqrestore(&adapter->cmd_pending_q_lock, | ||
660 | cmd_pending_q_flags); | ||
661 | |||
662 | spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, cmd_flags); | ||
663 | ret = mwifiex_dnld_cmd_to_fw(priv, cmd_node); | ||
664 | priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY); | ||
665 | /* Any command sent to the firmware when host is in sleep | ||
666 | * mode should de-configure host sleep. We should skip the | ||
667 | * host sleep configuration command itself though | ||
668 | */ | ||
669 | if (priv && (host_cmd->command != | ||
670 | cpu_to_le16(HostCmd_CMD_802_11_HS_CFG_ENH))) { | ||
671 | if (adapter->hs_activated) { | ||
672 | adapter->is_hs_configured = false; | ||
673 | mwifiex_hs_activated_event(priv, false); | ||
674 | } | ||
675 | } | ||
676 | |||
677 | return ret; | ||
678 | } | ||
679 | |||
680 | /* | ||
681 | * This function handles the command response. | ||
682 | * | ||
683 | * After processing, the function cleans the command node and puts | ||
684 | * it back to the command free queue. | ||
685 | */ | ||
686 | int mwifiex_process_cmdresp(struct mwifiex_adapter *adapter) | ||
687 | { | ||
688 | struct host_cmd_ds_command *resp = NULL; | ||
689 | struct mwifiex_private *priv = | ||
690 | mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY); | ||
691 | int ret = 0; | ||
692 | uint16_t orig_cmdresp_no; | ||
693 | uint16_t cmdresp_no; | ||
694 | uint16_t cmdresp_result; | ||
695 | struct mwifiex_wait_queue *wait_queue = NULL; | ||
696 | struct timeval tstamp; | ||
697 | unsigned long flags; | ||
698 | |||
699 | /* Now we got response from FW, cancel the command timer */ | ||
700 | del_timer(&adapter->cmd_timer); | ||
701 | |||
702 | if (!adapter->curr_cmd || !adapter->curr_cmd->resp_skb) { | ||
703 | resp = (struct host_cmd_ds_command *) adapter->upld_buf; | ||
704 | dev_err(adapter->dev, "CMD_RESP: NULL curr_cmd, %#x\n", | ||
705 | le16_to_cpu(resp->command)); | ||
706 | return -1; | ||
707 | } | ||
708 | |||
709 | if (adapter->curr_cmd->wq_buf) | ||
710 | wait_queue = (struct mwifiex_wait_queue *) | ||
711 | adapter->curr_cmd->wq_buf; | ||
712 | |||
713 | adapter->num_cmd_timeout = 0; | ||
714 | |||
715 | resp = (struct host_cmd_ds_command *) adapter->curr_cmd->resp_skb->data; | ||
716 | if (adapter->curr_cmd->cmd_flag & CMD_F_CANCELED) { | ||
717 | dev_err(adapter->dev, "CMD_RESP: %#x been canceled\n", | ||
718 | le16_to_cpu(resp->command)); | ||
719 | mwifiex_insert_cmd_to_free_q(adapter, adapter->curr_cmd); | ||
720 | spin_lock_irqsave(&adapter->mwifiex_cmd_lock, flags); | ||
721 | adapter->curr_cmd = NULL; | ||
722 | spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, flags); | ||
723 | return -1; | ||
724 | } | ||
725 | |||
726 | if (adapter->curr_cmd->cmd_flag & CMD_F_HOSTCMD) { | ||
727 | /* Copy original response back to response buffer */ | ||
728 | struct mwifiex_ds_misc_cmd *hostcmd = NULL; | ||
729 | uint16_t size = le16_to_cpu(resp->size); | ||
730 | dev_dbg(adapter->dev, "info: host cmd resp size = %d\n", size); | ||
731 | size = min_t(u16, size, MWIFIEX_SIZE_OF_CMD_BUFFER); | ||
732 | if (adapter->curr_cmd->data_buf) { | ||
733 | hostcmd = (struct mwifiex_ds_misc_cmd *) | ||
734 | adapter->curr_cmd->data_buf; | ||
735 | hostcmd->len = size; | ||
736 | memcpy(hostcmd->cmd, (void *) resp, size); | ||
737 | } | ||
738 | } | ||
739 | orig_cmdresp_no = le16_to_cpu(resp->command); | ||
740 | |||
741 | /* Get BSS number and corresponding priv */ | ||
742 | priv = mwifiex_get_priv_by_id(adapter, | ||
743 | HostCmd_GET_BSS_NO(le16_to_cpu(resp->seq_num)), | ||
744 | HostCmd_GET_BSS_TYPE(le16_to_cpu(resp->seq_num))); | ||
745 | if (!priv) | ||
746 | priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY); | ||
747 | /* Clear RET_BIT from HostCmd */ | ||
748 | resp->command = cpu_to_le16(orig_cmdresp_no & HostCmd_CMD_ID_MASK); | ||
749 | |||
750 | cmdresp_no = le16_to_cpu(resp->command); | ||
751 | cmdresp_result = le16_to_cpu(resp->result); | ||
752 | |||
753 | /* Save the last command response to debug log */ | ||
754 | adapter->dbg.last_cmd_resp_index = | ||
755 | (adapter->dbg.last_cmd_resp_index + 1) % DBG_CMD_NUM; | ||
756 | adapter->dbg.last_cmd_resp_id[adapter->dbg.last_cmd_resp_index] = | ||
757 | orig_cmdresp_no; | ||
758 | |||
759 | do_gettimeofday(&tstamp); | ||
760 | dev_dbg(adapter->dev, "cmd: CMD_RESP: (%lu.%lu): 0x%x, result %d," | ||
761 | " len %d, seqno 0x%x\n", | ||
762 | tstamp.tv_sec, tstamp.tv_usec, orig_cmdresp_no, cmdresp_result, | ||
763 | le16_to_cpu(resp->size), le16_to_cpu(resp->seq_num)); | ||
764 | |||
765 | if (!(orig_cmdresp_no & HostCmd_RET_BIT)) { | ||
766 | dev_err(adapter->dev, "CMD_RESP: invalid cmd resp\n"); | ||
767 | if (wait_queue) | ||
768 | wait_queue->status = MWIFIEX_ERROR_FW_CMDRESP; | ||
769 | |||
770 | mwifiex_insert_cmd_to_free_q(adapter, adapter->curr_cmd); | ||
771 | spin_lock_irqsave(&adapter->mwifiex_cmd_lock, flags); | ||
772 | adapter->curr_cmd = NULL; | ||
773 | spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, flags); | ||
774 | return -1; | ||
775 | } | ||
776 | |||
777 | if (adapter->curr_cmd->cmd_flag & CMD_F_HOSTCMD) { | ||
778 | adapter->curr_cmd->cmd_flag &= ~CMD_F_HOSTCMD; | ||
779 | if ((cmdresp_result == HostCmd_RESULT_OK) | ||
780 | && (cmdresp_no == HostCmd_CMD_802_11_HS_CFG_ENH)) | ||
781 | ret = mwifiex_ret_802_11_hs_cfg(priv, resp); | ||
782 | } else { | ||
783 | /* handle response */ | ||
784 | ret = mwifiex_process_sta_cmdresp(priv, cmdresp_no, resp, | ||
785 | wait_queue); | ||
786 | } | ||
787 | |||
788 | /* Check init command response */ | ||
789 | if (adapter->hw_status == MWIFIEX_HW_STATUS_INITIALIZING) { | ||
790 | if (ret == -1) { | ||
791 | dev_err(adapter->dev, "%s: cmd %#x failed during " | ||
792 | "initialization\n", __func__, cmdresp_no); | ||
793 | mwifiex_init_fw_complete(adapter); | ||
794 | return -1; | ||
795 | } else if (adapter->last_init_cmd == cmdresp_no) | ||
796 | adapter->hw_status = MWIFIEX_HW_STATUS_INIT_DONE; | ||
797 | } | ||
798 | |||
799 | if (adapter->curr_cmd) { | ||
800 | if (wait_queue && (!ret)) | ||
801 | wait_queue->status = MWIFIEX_ERROR_NO_ERROR; | ||
802 | else if (wait_queue && (ret == -1)) | ||
803 | wait_queue->status = MWIFIEX_ERROR_CMD_RESP_FAIL; | ||
804 | |||
805 | /* Clean up and put current command back to cmd_free_q */ | ||
806 | mwifiex_insert_cmd_to_free_q(adapter, adapter->curr_cmd); | ||
807 | |||
808 | spin_lock_irqsave(&adapter->mwifiex_cmd_lock, flags); | ||
809 | adapter->curr_cmd = NULL; | ||
810 | spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, flags); | ||
811 | } | ||
812 | |||
813 | return ret; | ||
814 | } | ||
815 | |||
816 | /* | ||
817 | * This function handles the timeout of command sending. | ||
818 | * | ||
819 | * It will re-send the same command again. | ||
820 | */ | ||
821 | void | ||
822 | mwifiex_cmd_timeout_func(unsigned long function_context) | ||
823 | { | ||
824 | struct mwifiex_adapter *adapter = | ||
825 | (struct mwifiex_adapter *) function_context; | ||
826 | struct cmd_ctrl_node *cmd_node = NULL; | ||
827 | struct mwifiex_wait_queue *wait_queue = NULL; | ||
828 | struct timeval tstamp; | ||
829 | |||
830 | adapter->num_cmd_timeout++; | ||
831 | adapter->dbg.num_cmd_timeout++; | ||
832 | if (!adapter->curr_cmd) { | ||
833 | dev_dbg(adapter->dev, "cmd: empty curr_cmd\n"); | ||
834 | return; | ||
835 | } | ||
836 | cmd_node = adapter->curr_cmd; | ||
837 | if (cmd_node->wq_buf) { | ||
838 | wait_queue = (struct mwifiex_wait_queue *) cmd_node->wq_buf; | ||
839 | wait_queue->status = MWIFIEX_ERROR_CMD_TIMEOUT; | ||
840 | } | ||
841 | |||
842 | if (cmd_node) { | ||
843 | adapter->dbg.timeout_cmd_id = | ||
844 | adapter->dbg.last_cmd_id[adapter->dbg.last_cmd_index]; | ||
845 | adapter->dbg.timeout_cmd_act = | ||
846 | adapter->dbg.last_cmd_act[adapter->dbg.last_cmd_index]; | ||
847 | do_gettimeofday(&tstamp); | ||
848 | dev_err(adapter->dev, "%s: Timeout cmd id (%lu.%lu) = %#x," | ||
849 | " act = %#x\n", __func__, | ||
850 | tstamp.tv_sec, tstamp.tv_usec, | ||
851 | adapter->dbg.timeout_cmd_id, | ||
852 | adapter->dbg.timeout_cmd_act); | ||
853 | |||
854 | dev_err(adapter->dev, "num_data_h2c_failure = %d\n", | ||
855 | adapter->dbg.num_tx_host_to_card_failure); | ||
856 | dev_err(adapter->dev, "num_cmd_h2c_failure = %d\n", | ||
857 | adapter->dbg.num_cmd_host_to_card_failure); | ||
858 | |||
859 | dev_err(adapter->dev, "num_cmd_timeout = %d\n", | ||
860 | adapter->dbg.num_cmd_timeout); | ||
861 | dev_err(adapter->dev, "num_tx_timeout = %d\n", | ||
862 | adapter->dbg.num_tx_timeout); | ||
863 | |||
864 | dev_err(adapter->dev, "last_cmd_index = %d\n", | ||
865 | adapter->dbg.last_cmd_index); | ||
866 | print_hex_dump_bytes("last_cmd_id: ", DUMP_PREFIX_OFFSET, | ||
867 | adapter->dbg.last_cmd_id, DBG_CMD_NUM); | ||
868 | print_hex_dump_bytes("last_cmd_act: ", DUMP_PREFIX_OFFSET, | ||
869 | adapter->dbg.last_cmd_act, DBG_CMD_NUM); | ||
870 | |||
871 | dev_err(adapter->dev, "last_cmd_resp_index = %d\n", | ||
872 | adapter->dbg.last_cmd_resp_index); | ||
873 | print_hex_dump_bytes("last_cmd_resp_id: ", DUMP_PREFIX_OFFSET, | ||
874 | adapter->dbg.last_cmd_resp_id, DBG_CMD_NUM); | ||
875 | |||
876 | dev_err(adapter->dev, "last_event_index = %d\n", | ||
877 | adapter->dbg.last_event_index); | ||
878 | print_hex_dump_bytes("last_event: ", DUMP_PREFIX_OFFSET, | ||
879 | adapter->dbg.last_event, DBG_CMD_NUM); | ||
880 | |||
881 | dev_err(adapter->dev, "data_sent=%d cmd_sent=%d\n", | ||
882 | adapter->data_sent, adapter->cmd_sent); | ||
883 | |||
884 | dev_err(adapter->dev, "ps_mode=%d ps_state=%d\n", | ||
885 | adapter->ps_mode, adapter->ps_state); | ||
886 | } | ||
887 | if (adapter->hw_status == MWIFIEX_HW_STATUS_INITIALIZING) | ||
888 | mwifiex_init_fw_complete(adapter); | ||
889 | |||
890 | return; | ||
891 | } | ||
892 | |||
893 | /* | ||
894 | * This function cancels all the pending commands. | ||
895 | * | ||
896 | * The current command, all commands in command pending queue and all scan | ||
897 | * commands in scan pending queue are cancelled. All the completion callbacks | ||
898 | * are called with failure status to ensure cleanup. | ||
899 | */ | ||
900 | void | ||
901 | mwifiex_cancel_all_pending_cmd(struct mwifiex_adapter *adapter) | ||
902 | { | ||
903 | struct cmd_ctrl_node *cmd_node = NULL, *tmp_node = NULL; | ||
904 | struct mwifiex_wait_queue *wait_queue = NULL; | ||
905 | unsigned long flags; | ||
906 | |||
907 | /* Cancel current cmd */ | ||
908 | if ((adapter->curr_cmd) && (adapter->curr_cmd->wq_buf)) { | ||
909 | wait_queue = | ||
910 | (struct mwifiex_wait_queue *) adapter->curr_cmd->wq_buf; | ||
911 | spin_lock_irqsave(&adapter->mwifiex_cmd_lock, flags); | ||
912 | adapter->curr_cmd->wq_buf = NULL; | ||
913 | spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, flags); | ||
914 | wait_queue->status = MWIFIEX_ERROR_CMD_CANCEL; | ||
915 | mwifiex_ioctl_complete(adapter, wait_queue, -1); | ||
916 | } | ||
917 | /* Cancel all pending command */ | ||
918 | spin_lock_irqsave(&adapter->cmd_pending_q_lock, flags); | ||
919 | list_for_each_entry_safe(cmd_node, tmp_node, | ||
920 | &adapter->cmd_pending_q, list) { | ||
921 | list_del(&cmd_node->list); | ||
922 | spin_unlock_irqrestore(&adapter->cmd_pending_q_lock, flags); | ||
923 | |||
924 | if (cmd_node->wq_buf) { | ||
925 | wait_queue = | ||
926 | (struct mwifiex_wait_queue *) cmd_node->wq_buf; | ||
927 | wait_queue->status = MWIFIEX_ERROR_CMD_CANCEL; | ||
928 | mwifiex_ioctl_complete(adapter, wait_queue, -1); | ||
929 | cmd_node->wq_buf = NULL; | ||
930 | } | ||
931 | mwifiex_insert_cmd_to_free_q(adapter, cmd_node); | ||
932 | spin_lock_irqsave(&adapter->cmd_pending_q_lock, flags); | ||
933 | } | ||
934 | spin_unlock_irqrestore(&adapter->cmd_pending_q_lock, flags); | ||
935 | |||
936 | /* Cancel all pending scan command */ | ||
937 | spin_lock_irqsave(&adapter->scan_pending_q_lock, flags); | ||
938 | list_for_each_entry_safe(cmd_node, tmp_node, | ||
939 | &adapter->scan_pending_q, list) { | ||
940 | list_del(&cmd_node->list); | ||
941 | spin_unlock_irqrestore(&adapter->scan_pending_q_lock, flags); | ||
942 | |||
943 | cmd_node->wq_buf = NULL; | ||
944 | mwifiex_insert_cmd_to_free_q(adapter, cmd_node); | ||
945 | spin_lock_irqsave(&adapter->scan_pending_q_lock, flags); | ||
946 | } | ||
947 | spin_unlock_irqrestore(&adapter->scan_pending_q_lock, flags); | ||
948 | |||
949 | spin_lock_irqsave(&adapter->mwifiex_cmd_lock, flags); | ||
950 | adapter->scan_processing = false; | ||
951 | spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, flags); | ||
952 | } | ||
953 | |||
954 | /* | ||
955 | * This function cancels all pending commands that matches with | ||
956 | * the given IOCTL request. | ||
957 | * | ||
958 | * Both the current command buffer and the pending command queue are | ||
959 | * searched for matching IOCTL request. The completion callback of | ||
960 | * the matched command is called with failure status to ensure cleanup. | ||
961 | * In case of scan commands, all pending commands in scan pending queue | ||
962 | * are cancelled. | ||
963 | */ | ||
964 | void | ||
965 | mwifiex_cancel_pending_ioctl(struct mwifiex_adapter *adapter, | ||
966 | struct mwifiex_wait_queue *wait_queue) | ||
967 | { | ||
968 | struct cmd_ctrl_node *cmd_node = NULL, *tmp_node = NULL; | ||
969 | unsigned long cmd_flags; | ||
970 | unsigned long cmd_pending_q_flags; | ||
971 | unsigned long scan_pending_q_flags; | ||
972 | uint16_t cancel_scan_cmd = false; | ||
973 | |||
974 | if ((adapter->curr_cmd) && | ||
975 | (adapter->curr_cmd->wq_buf == wait_queue)) { | ||
976 | spin_lock_irqsave(&adapter->mwifiex_cmd_lock, cmd_flags); | ||
977 | cmd_node = adapter->curr_cmd; | ||
978 | cmd_node->wq_buf = NULL; | ||
979 | cmd_node->cmd_flag |= CMD_F_CANCELED; | ||
980 | spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, cmd_flags); | ||
981 | } | ||
982 | |||
983 | spin_lock_irqsave(&adapter->mwifiex_cmd_lock, cmd_flags); | ||
984 | while (1) { | ||
985 | cmd_node = mwifiex_get_pending_ioctl_cmd(adapter, wait_queue); | ||
986 | if (!cmd_node) | ||
987 | break; | ||
988 | |||
989 | spin_lock_irqsave(&adapter->cmd_pending_q_lock, | ||
990 | cmd_pending_q_flags); | ||
991 | list_del(&cmd_node->list); | ||
992 | spin_unlock_irqrestore(&adapter->cmd_pending_q_lock, | ||
993 | cmd_pending_q_flags); | ||
994 | |||
995 | cmd_node->wq_buf = NULL; | ||
996 | mwifiex_insert_cmd_to_free_q(adapter, cmd_node); | ||
997 | } | ||
998 | spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, cmd_flags); | ||
999 | /* Cancel all pending scan command */ | ||
1000 | spin_lock_irqsave(&adapter->scan_pending_q_lock, | ||
1001 | scan_pending_q_flags); | ||
1002 | list_for_each_entry_safe(cmd_node, tmp_node, | ||
1003 | &adapter->scan_pending_q, list) { | ||
1004 | if (cmd_node->wq_buf == wait_queue) { | ||
1005 | list_del(&cmd_node->list); | ||
1006 | spin_unlock_irqrestore(&adapter->scan_pending_q_lock, | ||
1007 | scan_pending_q_flags); | ||
1008 | cmd_node->wq_buf = NULL; | ||
1009 | mwifiex_insert_cmd_to_free_q(adapter, cmd_node); | ||
1010 | spin_lock_irqsave(&adapter->scan_pending_q_lock, | ||
1011 | scan_pending_q_flags); | ||
1012 | cancel_scan_cmd = true; | ||
1013 | } | ||
1014 | } | ||
1015 | spin_unlock_irqrestore(&adapter->scan_pending_q_lock, | ||
1016 | scan_pending_q_flags); | ||
1017 | |||
1018 | if (cancel_scan_cmd) { | ||
1019 | spin_lock_irqsave(&adapter->mwifiex_cmd_lock, cmd_flags); | ||
1020 | adapter->scan_processing = false; | ||
1021 | spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, cmd_flags); | ||
1022 | } | ||
1023 | wait_queue->status = MWIFIEX_ERROR_CMD_CANCEL; | ||
1024 | mwifiex_ioctl_complete(adapter, wait_queue, -1); | ||
1025 | |||
1026 | return; | ||
1027 | } | ||
1028 | |||
1029 | /* | ||
1030 | * This function sends the sleep confirm command to firmware, if | ||
1031 | * possible. | ||
1032 | * | ||
1033 | * The sleep confirm command cannot be issued if command response, | ||
1034 | * data response or event response is awaiting handling, or if we | ||
1035 | * are in the middle of sending a command, or expecting a command | ||
1036 | * response. | ||
1037 | */ | ||
1038 | void | ||
1039 | mwifiex_check_ps_cond(struct mwifiex_adapter *adapter) | ||
1040 | { | ||
1041 | if (!adapter->cmd_sent && | ||
1042 | !adapter->curr_cmd && !IS_CARD_RX_RCVD(adapter)) | ||
1043 | mwifiex_dnld_sleep_confirm_cmd(adapter); | ||
1044 | else | ||
1045 | dev_dbg(adapter->dev, | ||
1046 | "cmd: Delay Sleep Confirm (%s%s%s)\n", | ||
1047 | (adapter->cmd_sent) ? "D" : "", | ||
1048 | (adapter->curr_cmd) ? "C" : "", | ||
1049 | (IS_CARD_RX_RCVD(adapter)) ? "R" : ""); | ||
1050 | } | ||
1051 | |||
1052 | /* | ||
1053 | * This function sends a Host Sleep activated event to applications. | ||
1054 | * | ||
1055 | * This event is generated by the driver, with a blank event body. | ||
1056 | */ | ||
1057 | void | ||
1058 | mwifiex_hs_activated_event(struct mwifiex_private *priv, u8 activated) | ||
1059 | { | ||
1060 | if (activated) { | ||
1061 | if (priv->adapter->is_hs_configured) { | ||
1062 | priv->adapter->hs_activated = true; | ||
1063 | dev_dbg(priv->adapter->dev, "event: hs_activated\n"); | ||
1064 | priv->adapter->hs_activate_wait_q_woken = true; | ||
1065 | wake_up_interruptible( | ||
1066 | &priv->adapter->hs_activate_wait_q); | ||
1067 | } else { | ||
1068 | dev_dbg(priv->adapter->dev, "event: HS not configured\n"); | ||
1069 | } | ||
1070 | } else { | ||
1071 | dev_dbg(priv->adapter->dev, "event: hs_deactivated\n"); | ||
1072 | priv->adapter->hs_activated = false; | ||
1073 | } | ||
1074 | } | ||
1075 | |||
1076 | /* | ||
1077 | * This function handles the command response of a Host Sleep configuration | ||
1078 | * command. | ||
1079 | * | ||
1080 | * Handling includes changing the header fields into CPU format | ||
1081 | * and setting the current host sleep activation status in driver. | ||
1082 | * | ||
1083 | * In case host sleep status change, the function generates an event to | ||
1084 | * notify the applications. | ||
1085 | */ | ||
1086 | int mwifiex_ret_802_11_hs_cfg(struct mwifiex_private *priv, | ||
1087 | struct host_cmd_ds_command *resp) | ||
1088 | { | ||
1089 | struct mwifiex_adapter *adapter = priv->adapter; | ||
1090 | struct host_cmd_ds_802_11_hs_cfg_enh *phs_cfg = | ||
1091 | &resp->params.opt_hs_cfg; | ||
1092 | uint32_t conditions = le32_to_cpu(phs_cfg->params.hs_config.conditions); | ||
1093 | |||
1094 | if (phs_cfg->action == cpu_to_le16(HS_ACTIVATE)) { | ||
1095 | mwifiex_hs_activated_event(priv, true); | ||
1096 | return 0; | ||
1097 | } else { | ||
1098 | dev_dbg(adapter->dev, "cmd: CMD_RESP: HS_CFG cmd reply" | ||
1099 | " result=%#x, conditions=0x%x gpio=0x%x gap=0x%x\n", | ||
1100 | resp->result, conditions, | ||
1101 | phs_cfg->params.hs_config.gpio, | ||
1102 | phs_cfg->params.hs_config.gap); | ||
1103 | } | ||
1104 | if (conditions != HOST_SLEEP_CFG_CANCEL) { | ||
1105 | adapter->is_hs_configured = true; | ||
1106 | } else { | ||
1107 | adapter->is_hs_configured = false; | ||
1108 | if (adapter->hs_activated) | ||
1109 | mwifiex_hs_activated_event(priv, false); | ||
1110 | } | ||
1111 | |||
1112 | return 0; | ||
1113 | } | ||
1114 | |||
1115 | /* | ||
1116 | * This function wakes up the adapter and generates a Host Sleep | ||
1117 | * cancel event on receiving the power up interrupt. | ||
1118 | */ | ||
1119 | void | ||
1120 | mwifiex_process_hs_config(struct mwifiex_adapter *adapter) | ||
1121 | { | ||
1122 | dev_dbg(adapter->dev, "info: %s: auto cancelling host sleep" | ||
1123 | " since there is interrupt from the firmware\n", __func__); | ||
1124 | |||
1125 | adapter->if_ops.wakeup(adapter); | ||
1126 | adapter->hs_activated = false; | ||
1127 | adapter->is_hs_configured = false; | ||
1128 | mwifiex_hs_activated_event(mwifiex_get_priv(adapter, | ||
1129 | MWIFIEX_BSS_ROLE_ANY), false); | ||
1130 | return; | ||
1131 | } | ||
1132 | |||
1133 | /* | ||
1134 | * This function handles the command response of a sleep confirm command. | ||
1135 | * | ||
1136 | * The function sets the card state to SLEEP if the response indicates success. | ||
1137 | */ | ||
1138 | void | ||
1139 | mwifiex_process_sleep_confirm_resp(struct mwifiex_adapter *adapter, | ||
1140 | u8 *pbuf, u32 upld_len) | ||
1141 | { | ||
1142 | struct host_cmd_ds_command *cmd = (struct host_cmd_ds_command *) pbuf; | ||
1143 | struct mwifiex_private *priv = | ||
1144 | mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY); | ||
1145 | uint16_t result = le16_to_cpu(cmd->result); | ||
1146 | uint16_t command = le16_to_cpu(cmd->command); | ||
1147 | uint16_t seq_num = le16_to_cpu(cmd->seq_num); | ||
1148 | |||
1149 | if (!upld_len) { | ||
1150 | dev_err(adapter->dev, "%s: cmd size is 0\n", __func__); | ||
1151 | return; | ||
1152 | } | ||
1153 | |||
1154 | /* Get BSS number and corresponding priv */ | ||
1155 | priv = mwifiex_get_priv_by_id(adapter, HostCmd_GET_BSS_NO(seq_num), | ||
1156 | HostCmd_GET_BSS_TYPE(seq_num)); | ||
1157 | if (!priv) | ||
1158 | priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY); | ||
1159 | |||
1160 | /* Update sequence number */ | ||
1161 | seq_num = HostCmd_GET_SEQ_NO(seq_num); | ||
1162 | /* Clear RET_BIT from HostCmd */ | ||
1163 | command &= HostCmd_CMD_ID_MASK; | ||
1164 | |||
1165 | if (command != HostCmd_CMD_802_11_PS_MODE_ENH) { | ||
1166 | dev_err(adapter->dev, "%s: received unexpected response for" | ||
1167 | " cmd %x, result = %x\n", __func__, command, result); | ||
1168 | return; | ||
1169 | } | ||
1170 | |||
1171 | if (result) { | ||
1172 | dev_err(adapter->dev, "%s: sleep confirm cmd failed\n", | ||
1173 | __func__); | ||
1174 | adapter->pm_wakeup_card_req = false; | ||
1175 | adapter->ps_state = PS_STATE_AWAKE; | ||
1176 | return; | ||
1177 | } | ||
1178 | adapter->pm_wakeup_card_req = true; | ||
1179 | if (adapter->is_hs_configured) | ||
1180 | mwifiex_hs_activated_event(mwifiex_get_priv(adapter, | ||
1181 | MWIFIEX_BSS_ROLE_ANY), true); | ||
1182 | adapter->ps_state = PS_STATE_SLEEP; | ||
1183 | cmd->command = cpu_to_le16(command); | ||
1184 | cmd->seq_num = cpu_to_le16(seq_num); | ||
1185 | } | ||
1186 | EXPORT_SYMBOL_GPL(mwifiex_process_sleep_confirm_resp); | ||
1187 | |||
1188 | /* | ||
1189 | * This function prepares an enhanced power mode command. | ||
1190 | * | ||
1191 | * This function can be used to disable power save or to configure | ||
1192 | * power save with auto PS or STA PS or auto deep sleep. | ||
1193 | * | ||
1194 | * Preparation includes - | ||
1195 | * - Setting command ID, action and proper size | ||
1196 | * - Setting Power Save bitmap, PS parameters TLV, PS mode TLV, | ||
1197 | * auto deep sleep TLV (as required) | ||
1198 | * - Ensuring correct endian-ness | ||
1199 | */ | ||
1200 | int mwifiex_cmd_enh_power_mode(struct mwifiex_private *priv, | ||
1201 | struct host_cmd_ds_command *cmd, | ||
1202 | u16 cmd_action, uint16_t ps_bitmap, | ||
1203 | void *data_buf) | ||
1204 | { | ||
1205 | struct host_cmd_ds_802_11_ps_mode_enh *psmode_enh = | ||
1206 | &cmd->params.psmode_enh; | ||
1207 | u8 *tlv = NULL; | ||
1208 | u16 cmd_size = 0; | ||
1209 | |||
1210 | cmd->command = cpu_to_le16(HostCmd_CMD_802_11_PS_MODE_ENH); | ||
1211 | if (cmd_action == DIS_AUTO_PS) { | ||
1212 | psmode_enh->action = cpu_to_le16(DIS_AUTO_PS); | ||
1213 | psmode_enh->params.ps_bitmap = cpu_to_le16(ps_bitmap); | ||
1214 | cmd->size = cpu_to_le16(S_DS_GEN + AUTO_PS_FIX_SIZE); | ||
1215 | } else if (cmd_action == GET_PS) { | ||
1216 | psmode_enh->action = cpu_to_le16(GET_PS); | ||
1217 | psmode_enh->params.ps_bitmap = cpu_to_le16(ps_bitmap); | ||
1218 | cmd->size = cpu_to_le16(S_DS_GEN + AUTO_PS_FIX_SIZE); | ||
1219 | } else if (cmd_action == EN_AUTO_PS) { | ||
1220 | psmode_enh->action = cpu_to_le16(EN_AUTO_PS); | ||
1221 | psmode_enh->params.auto_ps.ps_bitmap = cpu_to_le16(ps_bitmap); | ||
1222 | cmd_size = S_DS_GEN + AUTO_PS_FIX_SIZE; | ||
1223 | tlv = (u8 *) cmd + cmd_size; | ||
1224 | if (ps_bitmap & BITMAP_STA_PS) { | ||
1225 | struct mwifiex_adapter *adapter = priv->adapter; | ||
1226 | struct mwifiex_ie_types_ps_param *ps_tlv = | ||
1227 | (struct mwifiex_ie_types_ps_param *) tlv; | ||
1228 | struct mwifiex_ps_param *ps_mode = &ps_tlv->param; | ||
1229 | ps_tlv->header.type = cpu_to_le16(TLV_TYPE_PS_PARAM); | ||
1230 | ps_tlv->header.len = cpu_to_le16(sizeof(*ps_tlv) - | ||
1231 | sizeof(struct mwifiex_ie_types_header)); | ||
1232 | cmd_size += sizeof(*ps_tlv); | ||
1233 | tlv += sizeof(*ps_tlv); | ||
1234 | dev_dbg(adapter->dev, "cmd: PS Command: Enter PS\n"); | ||
1235 | ps_mode->null_pkt_interval = | ||
1236 | cpu_to_le16(adapter->null_pkt_interval); | ||
1237 | ps_mode->multiple_dtims = | ||
1238 | cpu_to_le16(adapter->multiple_dtim); | ||
1239 | ps_mode->bcn_miss_timeout = | ||
1240 | cpu_to_le16(adapter->bcn_miss_time_out); | ||
1241 | ps_mode->local_listen_interval = | ||
1242 | cpu_to_le16(adapter->local_listen_interval); | ||
1243 | ps_mode->adhoc_wake_period = | ||
1244 | cpu_to_le16(adapter->adhoc_awake_period); | ||
1245 | ps_mode->delay_to_ps = | ||
1246 | cpu_to_le16(adapter->delay_to_ps); | ||
1247 | ps_mode->mode = | ||
1248 | cpu_to_le16(adapter->enhanced_ps_mode); | ||
1249 | |||
1250 | } | ||
1251 | if (ps_bitmap & BITMAP_AUTO_DS) { | ||
1252 | struct mwifiex_ie_types_auto_ds_param *auto_ps_tlv = | ||
1253 | (struct mwifiex_ie_types_auto_ds_param *) tlv; | ||
1254 | struct mwifiex_auto_ds_param *auto_ds = | ||
1255 | &auto_ps_tlv->param; | ||
1256 | u16 idletime = 0; | ||
1257 | auto_ps_tlv->header.type = | ||
1258 | cpu_to_le16(TLV_TYPE_AUTO_DS_PARAM); | ||
1259 | auto_ps_tlv->header.len = | ||
1260 | cpu_to_le16(sizeof(*auto_ps_tlv) - | ||
1261 | sizeof(struct mwifiex_ie_types_header)); | ||
1262 | cmd_size += sizeof(*auto_ps_tlv); | ||
1263 | tlv += sizeof(*auto_ps_tlv); | ||
1264 | if (data_buf) | ||
1265 | idletime = ((struct mwifiex_ds_auto_ds *) | ||
1266 | data_buf)->idle_time; | ||
1267 | dev_dbg(priv->adapter->dev, | ||
1268 | "cmd: PS Command: Enter Auto Deep Sleep\n"); | ||
1269 | auto_ds->deep_sleep_timeout = cpu_to_le16(idletime); | ||
1270 | } | ||
1271 | cmd->size = cpu_to_le16(cmd_size); | ||
1272 | } | ||
1273 | return 0; | ||
1274 | } | ||
1275 | |||
1276 | /* | ||
1277 | * This function handles the command response of an enhanced power mode | ||
1278 | * command. | ||
1279 | * | ||
1280 | * Handling includes changing the header fields into CPU format | ||
1281 | * and setting the current enhanced power mode in driver. | ||
1282 | */ | ||
1283 | int mwifiex_ret_enh_power_mode(struct mwifiex_private *priv, | ||
1284 | struct host_cmd_ds_command *resp, | ||
1285 | void *data_buf) | ||
1286 | { | ||
1287 | struct mwifiex_adapter *adapter = priv->adapter; | ||
1288 | struct host_cmd_ds_802_11_ps_mode_enh *ps_mode = | ||
1289 | &resp->params.psmode_enh; | ||
1290 | uint16_t action = le16_to_cpu(ps_mode->action); | ||
1291 | uint16_t ps_bitmap = le16_to_cpu(ps_mode->params.ps_bitmap); | ||
1292 | uint16_t auto_ps_bitmap = | ||
1293 | le16_to_cpu(ps_mode->params.auto_ps.ps_bitmap); | ||
1294 | |||
1295 | dev_dbg(adapter->dev, "info: %s: PS_MODE cmd reply result=%#x action=%#X\n", | ||
1296 | __func__, resp->result, action); | ||
1297 | if (action == EN_AUTO_PS) { | ||
1298 | if (auto_ps_bitmap & BITMAP_AUTO_DS) { | ||
1299 | dev_dbg(adapter->dev, "cmd: Enabled auto deep sleep\n"); | ||
1300 | priv->adapter->is_deep_sleep = true; | ||
1301 | } | ||
1302 | if (auto_ps_bitmap & BITMAP_STA_PS) { | ||
1303 | dev_dbg(adapter->dev, "cmd: Enabled STA power save\n"); | ||
1304 | if (adapter->sleep_period.period) | ||
1305 | dev_dbg(adapter->dev, "cmd: set to uapsd/pps mode\n"); | ||
1306 | } | ||
1307 | } else if (action == DIS_AUTO_PS) { | ||
1308 | if (ps_bitmap & BITMAP_AUTO_DS) { | ||
1309 | priv->adapter->is_deep_sleep = false; | ||
1310 | dev_dbg(adapter->dev, "cmd: Disabled auto deep sleep\n"); | ||
1311 | } | ||
1312 | if (ps_bitmap & BITMAP_STA_PS) { | ||
1313 | dev_dbg(adapter->dev, "cmd: Disabled STA power save\n"); | ||
1314 | if (adapter->sleep_period.period) { | ||
1315 | adapter->delay_null_pkt = false; | ||
1316 | adapter->tx_lock_flag = false; | ||
1317 | adapter->pps_uapsd_mode = false; | ||
1318 | } | ||
1319 | } | ||
1320 | } else if (action == GET_PS) { | ||
1321 | if (ps_bitmap & (BITMAP_STA_PS | BITMAP_UAP_INACT_PS | ||
1322 | | BITMAP_UAP_DTIM_PS)) | ||
1323 | adapter->ps_mode = MWIFIEX_802_11_POWER_MODE_PSP; | ||
1324 | else | ||
1325 | adapter->ps_mode = MWIFIEX_802_11_POWER_MODE_CAM; | ||
1326 | |||
1327 | dev_dbg(adapter->dev, "cmd: ps_bitmap=%#x\n", ps_bitmap); | ||
1328 | |||
1329 | if (data_buf) { | ||
1330 | /* This section is for get power save mode */ | ||
1331 | struct mwifiex_ds_pm_cfg *pm_cfg = | ||
1332 | (struct mwifiex_ds_pm_cfg *)data_buf; | ||
1333 | if (ps_bitmap & BITMAP_STA_PS) | ||
1334 | pm_cfg->param.ps_mode = 1; | ||
1335 | else | ||
1336 | pm_cfg->param.ps_mode = 0; | ||
1337 | } | ||
1338 | } | ||
1339 | return 0; | ||
1340 | } | ||
1341 | |||
1342 | /* | ||
1343 | * This function prepares command to get hardware specifications. | ||
1344 | * | ||
1345 | * Preparation includes - | ||
1346 | * - Setting command ID, action and proper size | ||
1347 | * - Setting permanent address parameter | ||
1348 | * - Ensuring correct endian-ness | ||
1349 | */ | ||
1350 | int mwifiex_cmd_get_hw_spec(struct mwifiex_private *priv, | ||
1351 | struct host_cmd_ds_command *cmd) | ||
1352 | { | ||
1353 | struct host_cmd_ds_get_hw_spec *hw_spec = &cmd->params.hw_spec; | ||
1354 | |||
1355 | cmd->command = cpu_to_le16(HostCmd_CMD_GET_HW_SPEC); | ||
1356 | cmd->size = | ||
1357 | cpu_to_le16(sizeof(struct host_cmd_ds_get_hw_spec) + S_DS_GEN); | ||
1358 | memcpy(hw_spec->permanent_addr, priv->curr_addr, ETH_ALEN); | ||
1359 | |||
1360 | return 0; | ||
1361 | } | ||
1362 | |||
1363 | /* | ||
1364 | * This function handles the command response of get hardware | ||
1365 | * specifications. | ||
1366 | * | ||
1367 | * Handling includes changing the header fields into CPU format | ||
1368 | * and saving/updating the following parameters in driver - | ||
1369 | * - Firmware capability information | ||
1370 | * - Firmware band settings | ||
1371 | * - Ad-hoc start band and channel | ||
1372 | * - Ad-hoc 11n activation status | ||
1373 | * - Firmware release number | ||
1374 | * - Number of antennas | ||
1375 | * - Hardware address | ||
1376 | * - Hardware interface version | ||
1377 | * - Firmware version | ||
1378 | * - Region code | ||
1379 | * - 11n capabilities | ||
1380 | * - MCS support fields | ||
1381 | * - MP end port | ||
1382 | */ | ||
1383 | int mwifiex_ret_get_hw_spec(struct mwifiex_private *priv, | ||
1384 | struct host_cmd_ds_command *resp) | ||
1385 | { | ||
1386 | struct host_cmd_ds_get_hw_spec *hw_spec = &resp->params.hw_spec; | ||
1387 | struct mwifiex_adapter *adapter = priv->adapter; | ||
1388 | int i; | ||
1389 | |||
1390 | adapter->fw_cap_info = le32_to_cpu(hw_spec->fw_cap_info); | ||
1391 | |||
1392 | if (IS_SUPPORT_MULTI_BANDS(adapter)) | ||
1393 | adapter->fw_bands = (u8) GET_FW_DEFAULT_BANDS(adapter); | ||
1394 | else | ||
1395 | adapter->fw_bands = BAND_B; | ||
1396 | |||
1397 | adapter->config_bands = adapter->fw_bands; | ||
1398 | |||
1399 | if (adapter->fw_bands & BAND_A) { | ||
1400 | if (adapter->fw_bands & BAND_GN) { | ||
1401 | adapter->config_bands |= BAND_AN; | ||
1402 | adapter->fw_bands |= BAND_AN; | ||
1403 | } | ||
1404 | if (adapter->fw_bands & BAND_AN) { | ||
1405 | adapter->adhoc_start_band = BAND_A | BAND_AN; | ||
1406 | adapter->adhoc_11n_enabled = true; | ||
1407 | } else { | ||
1408 | adapter->adhoc_start_band = BAND_A; | ||
1409 | } | ||
1410 | priv->adhoc_channel = DEFAULT_AD_HOC_CHANNEL_A; | ||
1411 | } else if (adapter->fw_bands & BAND_GN) { | ||
1412 | adapter->adhoc_start_band = BAND_G | BAND_B | BAND_GN; | ||
1413 | priv->adhoc_channel = DEFAULT_AD_HOC_CHANNEL; | ||
1414 | adapter->adhoc_11n_enabled = true; | ||
1415 | } else if (adapter->fw_bands & BAND_G) { | ||
1416 | adapter->adhoc_start_band = BAND_G | BAND_B; | ||
1417 | priv->adhoc_channel = DEFAULT_AD_HOC_CHANNEL; | ||
1418 | } else if (adapter->fw_bands & BAND_B) { | ||
1419 | adapter->adhoc_start_band = BAND_B; | ||
1420 | priv->adhoc_channel = DEFAULT_AD_HOC_CHANNEL; | ||
1421 | } | ||
1422 | |||
1423 | adapter->fw_release_number = le32_to_cpu(hw_spec->fw_release_number); | ||
1424 | adapter->number_of_antenna = le16_to_cpu(hw_spec->number_of_antenna); | ||
1425 | |||
1426 | dev_dbg(adapter->dev, "info: GET_HW_SPEC: fw_release_number- %#x\n", | ||
1427 | adapter->fw_release_number); | ||
1428 | dev_dbg(adapter->dev, "info: GET_HW_SPEC: permanent addr: %pM\n", | ||
1429 | hw_spec->permanent_addr); | ||
1430 | dev_dbg(adapter->dev, "info: GET_HW_SPEC: hw_if_version=%#x version=%#x\n", | ||
1431 | le16_to_cpu(hw_spec->hw_if_version), | ||
1432 | le16_to_cpu(hw_spec->version)); | ||
1433 | |||
1434 | if (priv->curr_addr[0] == 0xff) | ||
1435 | memmove(priv->curr_addr, hw_spec->permanent_addr, ETH_ALEN); | ||
1436 | |||
1437 | adapter->region_code = le16_to_cpu(hw_spec->region_code); | ||
1438 | |||
1439 | for (i = 0; i < MWIFIEX_MAX_REGION_CODE; i++) | ||
1440 | /* Use the region code to search for the index */ | ||
1441 | if (adapter->region_code == region_code_index[i]) | ||
1442 | break; | ||
1443 | |||
1444 | /* If it's unidentified region code, use the default (USA) */ | ||
1445 | if (i >= MWIFIEX_MAX_REGION_CODE) { | ||
1446 | adapter->region_code = 0x10; | ||
1447 | dev_dbg(adapter->dev, "cmd: unknown region code, use default (USA)\n"); | ||
1448 | } | ||
1449 | |||
1450 | adapter->hw_dot_11n_dev_cap = le32_to_cpu(hw_spec->dot_11n_dev_cap); | ||
1451 | adapter->usr_dot_11n_dev_cap = adapter->hw_dot_11n_dev_cap & | ||
1452 | DEFAULT_11N_CAP_MASK; | ||
1453 | adapter->hw_dev_mcs_support = hw_spec->dev_mcs_support; | ||
1454 | adapter->usr_dev_mcs_support = adapter->hw_dev_mcs_support; | ||
1455 | mwifiex_show_dot_11n_dev_cap(adapter, adapter->hw_dot_11n_dev_cap); | ||
1456 | mwifiex_show_dev_mcs_support(adapter, adapter->hw_dev_mcs_support); | ||
1457 | |||
1458 | if (adapter->if_ops.update_mp_end_port) | ||
1459 | adapter->if_ops.update_mp_end_port(adapter, | ||
1460 | le16_to_cpu(hw_spec->mp_end_port)); | ||
1461 | |||
1462 | return 0; | ||
1463 | } | ||
diff --git a/drivers/net/wireless/mwifiex/debugfs.c b/drivers/net/wireless/mwifiex/debugfs.c new file mode 100644 index 000000000000..63b09692f27d --- /dev/null +++ b/drivers/net/wireless/mwifiex/debugfs.c | |||
@@ -0,0 +1,773 @@ | |||
1 | /* | ||
2 | * Marvell Wireless LAN device driver: debugfs | ||
3 | * | ||
4 | * Copyright (C) 2011, Marvell International Ltd. | ||
5 | * | ||
6 | * This software file (the "File") is distributed by Marvell International | ||
7 | * Ltd. under the terms of the GNU General Public License Version 2, June 1991 | ||
8 | * (the "License"). You may use, redistribute and/or modify this File in | ||
9 | * accordance with the terms and conditions of the License, a copy of which | ||
10 | * is available by writing to the Free Software Foundation, Inc., | ||
11 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the | ||
12 | * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. | ||
13 | * | ||
14 | * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE | ||
15 | * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE | ||
16 | * ARE EXPRESSLY DISCLAIMED. The License provides additional details about | ||
17 | * this warranty disclaimer. | ||
18 | */ | ||
19 | |||
20 | #include <linux/debugfs.h> | ||
21 | |||
22 | #include "main.h" | ||
23 | #include "11n.h" | ||
24 | |||
25 | |||
26 | static struct dentry *mwifiex_dfs_dir; | ||
27 | |||
28 | static char *bss_modes[] = { | ||
29 | "Unknown", | ||
30 | "Managed", | ||
31 | "Ad-hoc", | ||
32 | "Auto" | ||
33 | }; | ||
34 | |||
35 | /* size/addr for mwifiex_debug_info */ | ||
36 | #define item_size(n) (FIELD_SIZEOF(struct mwifiex_debug_info, n)) | ||
37 | #define item_addr(n) (offsetof(struct mwifiex_debug_info, n)) | ||
38 | |||
39 | /* size/addr for struct mwifiex_adapter */ | ||
40 | #define adapter_item_size(n) (FIELD_SIZEOF(struct mwifiex_adapter, n)) | ||
41 | #define adapter_item_addr(n) (offsetof(struct mwifiex_adapter, n)) | ||
42 | |||
43 | struct mwifiex_debug_data { | ||
44 | char name[32]; /* variable/array name */ | ||
45 | u32 size; /* size of the variable/array */ | ||
46 | size_t addr; /* address of the variable/array */ | ||
47 | int num; /* number of variables in an array */ | ||
48 | }; | ||
49 | |||
50 | static struct mwifiex_debug_data items[] = { | ||
51 | {"int_counter", item_size(int_counter), | ||
52 | item_addr(int_counter), 1}, | ||
53 | {"wmm_ac_vo", item_size(packets_out[WMM_AC_VO]), | ||
54 | item_addr(packets_out[WMM_AC_VO]), 1}, | ||
55 | {"wmm_ac_vi", item_size(packets_out[WMM_AC_VI]), | ||
56 | item_addr(packets_out[WMM_AC_VI]), 1}, | ||
57 | {"wmm_ac_be", item_size(packets_out[WMM_AC_BE]), | ||
58 | item_addr(packets_out[WMM_AC_BE]), 1}, | ||
59 | {"wmm_ac_bk", item_size(packets_out[WMM_AC_BK]), | ||
60 | item_addr(packets_out[WMM_AC_BK]), 1}, | ||
61 | {"max_tx_buf_size", item_size(max_tx_buf_size), | ||
62 | item_addr(max_tx_buf_size), 1}, | ||
63 | {"tx_buf_size", item_size(tx_buf_size), | ||
64 | item_addr(tx_buf_size), 1}, | ||
65 | {"curr_tx_buf_size", item_size(curr_tx_buf_size), | ||
66 | item_addr(curr_tx_buf_size), 1}, | ||
67 | {"ps_mode", item_size(ps_mode), | ||
68 | item_addr(ps_mode), 1}, | ||
69 | {"ps_state", item_size(ps_state), | ||
70 | item_addr(ps_state), 1}, | ||
71 | {"is_deep_sleep", item_size(is_deep_sleep), | ||
72 | item_addr(is_deep_sleep), 1}, | ||
73 | {"wakeup_dev_req", item_size(pm_wakeup_card_req), | ||
74 | item_addr(pm_wakeup_card_req), 1}, | ||
75 | {"wakeup_tries", item_size(pm_wakeup_fw_try), | ||
76 | item_addr(pm_wakeup_fw_try), 1}, | ||
77 | {"hs_configured", item_size(is_hs_configured), | ||
78 | item_addr(is_hs_configured), 1}, | ||
79 | {"hs_activated", item_size(hs_activated), | ||
80 | item_addr(hs_activated), 1}, | ||
81 | {"num_tx_timeout", item_size(num_tx_timeout), | ||
82 | item_addr(num_tx_timeout), 1}, | ||
83 | {"num_cmd_timeout", item_size(num_cmd_timeout), | ||
84 | item_addr(num_cmd_timeout), 1}, | ||
85 | {"timeout_cmd_id", item_size(timeout_cmd_id), | ||
86 | item_addr(timeout_cmd_id), 1}, | ||
87 | {"timeout_cmd_act", item_size(timeout_cmd_act), | ||
88 | item_addr(timeout_cmd_act), 1}, | ||
89 | {"last_cmd_id", item_size(last_cmd_id), | ||
90 | item_addr(last_cmd_id), DBG_CMD_NUM}, | ||
91 | {"last_cmd_act", item_size(last_cmd_act), | ||
92 | item_addr(last_cmd_act), DBG_CMD_NUM}, | ||
93 | {"last_cmd_index", item_size(last_cmd_index), | ||
94 | item_addr(last_cmd_index), 1}, | ||
95 | {"last_cmd_resp_id", item_size(last_cmd_resp_id), | ||
96 | item_addr(last_cmd_resp_id), DBG_CMD_NUM}, | ||
97 | {"last_cmd_resp_index", item_size(last_cmd_resp_index), | ||
98 | item_addr(last_cmd_resp_index), 1}, | ||
99 | {"last_event", item_size(last_event), | ||
100 | item_addr(last_event), DBG_CMD_NUM}, | ||
101 | {"last_event_index", item_size(last_event_index), | ||
102 | item_addr(last_event_index), 1}, | ||
103 | {"num_cmd_h2c_fail", item_size(num_cmd_host_to_card_failure), | ||
104 | item_addr(num_cmd_host_to_card_failure), 1}, | ||
105 | {"num_cmd_sleep_cfm_fail", | ||
106 | item_size(num_cmd_sleep_cfm_host_to_card_failure), | ||
107 | item_addr(num_cmd_sleep_cfm_host_to_card_failure), 1}, | ||
108 | {"num_tx_h2c_fail", item_size(num_tx_host_to_card_failure), | ||
109 | item_addr(num_tx_host_to_card_failure), 1}, | ||
110 | {"num_evt_deauth", item_size(num_event_deauth), | ||
111 | item_addr(num_event_deauth), 1}, | ||
112 | {"num_evt_disassoc", item_size(num_event_disassoc), | ||
113 | item_addr(num_event_disassoc), 1}, | ||
114 | {"num_evt_link_lost", item_size(num_event_link_lost), | ||
115 | item_addr(num_event_link_lost), 1}, | ||
116 | {"num_cmd_deauth", item_size(num_cmd_deauth), | ||
117 | item_addr(num_cmd_deauth), 1}, | ||
118 | {"num_cmd_assoc_ok", item_size(num_cmd_assoc_success), | ||
119 | item_addr(num_cmd_assoc_success), 1}, | ||
120 | {"num_cmd_assoc_fail", item_size(num_cmd_assoc_failure), | ||
121 | item_addr(num_cmd_assoc_failure), 1}, | ||
122 | {"cmd_sent", item_size(cmd_sent), | ||
123 | item_addr(cmd_sent), 1}, | ||
124 | {"data_sent", item_size(data_sent), | ||
125 | item_addr(data_sent), 1}, | ||
126 | {"cmd_resp_received", item_size(cmd_resp_received), | ||
127 | item_addr(cmd_resp_received), 1}, | ||
128 | {"event_received", item_size(event_received), | ||
129 | item_addr(event_received), 1}, | ||
130 | |||
131 | /* variables defined in struct mwifiex_adapter */ | ||
132 | {"ioctl_pending", adapter_item_size(ioctl_pending), | ||
133 | adapter_item_addr(ioctl_pending), 1}, | ||
134 | {"tx_pending", adapter_item_size(tx_pending), | ||
135 | adapter_item_addr(tx_pending), 1}, | ||
136 | {"rx_pending", adapter_item_size(rx_pending), | ||
137 | adapter_item_addr(rx_pending), 1}, | ||
138 | }; | ||
139 | |||
140 | static int num_of_items = ARRAY_SIZE(items); | ||
141 | |||
142 | /* | ||
143 | * Generic proc file open handler. | ||
144 | * | ||
145 | * This function is called every time a file is accessed for read or write. | ||
146 | */ | ||
147 | static int | ||
148 | mwifiex_open_generic(struct inode *inode, struct file *file) | ||
149 | { | ||
150 | file->private_data = inode->i_private; | ||
151 | return 0; | ||
152 | } | ||
153 | |||
154 | /* | ||
155 | * Proc info file read handler. | ||
156 | * | ||
157 | * This function is called when the 'info' file is opened for reading. | ||
158 | * It prints the following driver related information - | ||
159 | * - Driver name | ||
160 | * - Driver version | ||
161 | * - Driver extended version | ||
162 | * - Interface name | ||
163 | * - BSS mode | ||
164 | * - Media state (connected or disconnected) | ||
165 | * - MAC address | ||
166 | * - Total number of Tx bytes | ||
167 | * - Total number of Rx bytes | ||
168 | * - Total number of Tx packets | ||
169 | * - Total number of Rx packets | ||
170 | * - Total number of dropped Tx packets | ||
171 | * - Total number of dropped Rx packets | ||
172 | * - Total number of corrupted Tx packets | ||
173 | * - Total number of corrupted Rx packets | ||
174 | * - Carrier status (on or off) | ||
175 | * - Tx queue status (started or stopped) | ||
176 | * | ||
177 | * For STA mode drivers, it also prints the following extra - | ||
178 | * - ESSID | ||
179 | * - BSSID | ||
180 | * - Channel | ||
181 | * - Region code | ||
182 | * - Multicast count | ||
183 | * - Multicast addresses | ||
184 | */ | ||
185 | static ssize_t | ||
186 | mwifiex_info_read(struct file *file, char __user *ubuf, | ||
187 | size_t count, loff_t *ppos) | ||
188 | { | ||
189 | struct mwifiex_private *priv = | ||
190 | (struct mwifiex_private *) file->private_data; | ||
191 | struct net_device *netdev = priv->netdev; | ||
192 | struct netdev_hw_addr *ha; | ||
193 | unsigned long page = get_zeroed_page(GFP_KERNEL); | ||
194 | char *p = (char *) page, fmt[64]; | ||
195 | struct mwifiex_bss_info info; | ||
196 | ssize_t ret = 0; | ||
197 | int i = 0; | ||
198 | |||
199 | if (!p) | ||
200 | return -ENOMEM; | ||
201 | |||
202 | memset(&info, 0, sizeof(info)); | ||
203 | ret = mwifiex_get_bss_info(priv, &info); | ||
204 | if (ret) | ||
205 | goto free_and_exit; | ||
206 | |||
207 | mwifiex_drv_get_driver_version(priv->adapter, fmt, sizeof(fmt) - 1); | ||
208 | |||
209 | if (!priv->version_str[0]) | ||
210 | mwifiex_get_ver_ext(priv); | ||
211 | |||
212 | p += sprintf(p, "driver_name = " "\"mwifiex\"\n"); | ||
213 | p += sprintf(p, "driver_version = %s", fmt); | ||
214 | p += sprintf(p, "\nverext = %s", priv->version_str); | ||
215 | p += sprintf(p, "\ninterface_name=\"%s\"\n", netdev->name); | ||
216 | p += sprintf(p, "bss_mode=\"%s\"\n", bss_modes[info.bss_mode]); | ||
217 | p += sprintf(p, "media_state=\"%s\"\n", | ||
218 | (!priv->media_connected ? "Disconnected" : "Connected")); | ||
219 | p += sprintf(p, "mac_address=\"%02x:%02x:%02x:%02x:%02x:%02x\"\n", | ||
220 | netdev->dev_addr[0], netdev->dev_addr[1], | ||
221 | netdev->dev_addr[2], netdev->dev_addr[3], | ||
222 | netdev->dev_addr[4], netdev->dev_addr[5]); | ||
223 | |||
224 | if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA) { | ||
225 | p += sprintf(p, "multicast_count=\"%d\"\n", | ||
226 | netdev_mc_count(netdev)); | ||
227 | p += sprintf(p, "essid=\"%s\"\n", info.ssid.ssid); | ||
228 | p += sprintf(p, "bssid=\"%02x:%02x:%02x:%02x:%02x:%02x\"\n", | ||
229 | info.bssid[0], info.bssid[1], | ||
230 | info.bssid[2], info.bssid[3], | ||
231 | info.bssid[4], info.bssid[5]); | ||
232 | p += sprintf(p, "channel=\"%d\"\n", (int) info.bss_chan); | ||
233 | p += sprintf(p, "region_code = \"%02x\"\n", info.region_code); | ||
234 | |||
235 | netdev_for_each_mc_addr(ha, netdev) | ||
236 | p += sprintf(p, "multicast_address[%d]=" | ||
237 | "\"%02x:%02x:%02x:%02x:%02x:%02x\"\n", i++, | ||
238 | ha->addr[0], ha->addr[1], | ||
239 | ha->addr[2], ha->addr[3], | ||
240 | ha->addr[4], ha->addr[5]); | ||
241 | } | ||
242 | |||
243 | p += sprintf(p, "num_tx_bytes = %lu\n", priv->stats.tx_bytes); | ||
244 | p += sprintf(p, "num_rx_bytes = %lu\n", priv->stats.rx_bytes); | ||
245 | p += sprintf(p, "num_tx_pkts = %lu\n", priv->stats.tx_packets); | ||
246 | p += sprintf(p, "num_rx_pkts = %lu\n", priv->stats.rx_packets); | ||
247 | p += sprintf(p, "num_tx_pkts_dropped = %lu\n", priv->stats.tx_dropped); | ||
248 | p += sprintf(p, "num_rx_pkts_dropped = %lu\n", priv->stats.rx_dropped); | ||
249 | p += sprintf(p, "num_tx_pkts_err = %lu\n", priv->stats.tx_errors); | ||
250 | p += sprintf(p, "num_rx_pkts_err = %lu\n", priv->stats.rx_errors); | ||
251 | p += sprintf(p, "carrier %s\n", ((netif_carrier_ok(priv->netdev)) | ||
252 | ? "on" : "off")); | ||
253 | p += sprintf(p, "tx queue %s\n", ((netif_queue_stopped(priv->netdev)) | ||
254 | ? "stopped" : "started")); | ||
255 | |||
256 | ret = simple_read_from_buffer(ubuf, count, ppos, (char *) page, | ||
257 | (unsigned long) p - page); | ||
258 | |||
259 | free_and_exit: | ||
260 | free_page(page); | ||
261 | return ret; | ||
262 | } | ||
263 | |||
264 | /* | ||
265 | * Proc getlog file read handler. | ||
266 | * | ||
267 | * This function is called when the 'getlog' file is opened for reading | ||
268 | * It prints the following log information - | ||
269 | * - Number of multicast Tx frames | ||
270 | * - Number of failed packets | ||
271 | * - Number of Tx retries | ||
272 | * - Number of multicast Tx retries | ||
273 | * - Number of duplicate frames | ||
274 | * - Number of RTS successes | ||
275 | * - Number of RTS failures | ||
276 | * - Number of ACK failures | ||
277 | * - Number of fragmented Rx frames | ||
278 | * - Number of multicast Rx frames | ||
279 | * - Number of FCS errors | ||
280 | * - Number of Tx frames | ||
281 | * - WEP ICV error counts | ||
282 | */ | ||
283 | static ssize_t | ||
284 | mwifiex_getlog_read(struct file *file, char __user *ubuf, | ||
285 | size_t count, loff_t *ppos) | ||
286 | { | ||
287 | struct mwifiex_private *priv = | ||
288 | (struct mwifiex_private *) file->private_data; | ||
289 | unsigned long page = get_zeroed_page(GFP_KERNEL); | ||
290 | char *p = (char *) page; | ||
291 | ssize_t ret = 0; | ||
292 | struct mwifiex_ds_get_stats stats; | ||
293 | |||
294 | if (!p) | ||
295 | return -ENOMEM; | ||
296 | |||
297 | memset(&stats, 0, sizeof(stats)); | ||
298 | ret = mwifiex_get_stats_info(priv, &stats); | ||
299 | if (ret) | ||
300 | goto free_and_exit; | ||
301 | |||
302 | p += sprintf(p, "\n" | ||
303 | "mcasttxframe %u\n" | ||
304 | "failed %u\n" | ||
305 | "retry %u\n" | ||
306 | "multiretry %u\n" | ||
307 | "framedup %u\n" | ||
308 | "rtssuccess %u\n" | ||
309 | "rtsfailure %u\n" | ||
310 | "ackfailure %u\n" | ||
311 | "rxfrag %u\n" | ||
312 | "mcastrxframe %u\n" | ||
313 | "fcserror %u\n" | ||
314 | "txframe %u\n" | ||
315 | "wepicverrcnt-1 %u\n" | ||
316 | "wepicverrcnt-2 %u\n" | ||
317 | "wepicverrcnt-3 %u\n" | ||
318 | "wepicverrcnt-4 %u\n", | ||
319 | stats.mcast_tx_frame, | ||
320 | stats.failed, | ||
321 | stats.retry, | ||
322 | stats.multi_retry, | ||
323 | stats.frame_dup, | ||
324 | stats.rts_success, | ||
325 | stats.rts_failure, | ||
326 | stats.ack_failure, | ||
327 | stats.rx_frag, | ||
328 | stats.mcast_rx_frame, | ||
329 | stats.fcs_error, | ||
330 | stats.tx_frame, | ||
331 | stats.wep_icv_error[0], | ||
332 | stats.wep_icv_error[1], | ||
333 | stats.wep_icv_error[2], | ||
334 | stats.wep_icv_error[3]); | ||
335 | |||
336 | |||
337 | ret = simple_read_from_buffer(ubuf, count, ppos, (char *) page, | ||
338 | (unsigned long) p - page); | ||
339 | |||
340 | free_and_exit: | ||
341 | free_page(page); | ||
342 | return ret; | ||
343 | } | ||
344 | |||
345 | static struct mwifiex_debug_info info; | ||
346 | |||
347 | /* | ||
348 | * Proc debug file read handler. | ||
349 | * | ||
350 | * This function is called when the 'debug' file is opened for reading | ||
351 | * It prints the following log information - | ||
352 | * - Interrupt count | ||
353 | * - WMM AC VO packets count | ||
354 | * - WMM AC VI packets count | ||
355 | * - WMM AC BE packets count | ||
356 | * - WMM AC BK packets count | ||
357 | * - Maximum Tx buffer size | ||
358 | * - Tx buffer size | ||
359 | * - Current Tx buffer size | ||
360 | * - Power Save mode | ||
361 | * - Power Save state | ||
362 | * - Deep Sleep status | ||
363 | * - Device wakeup required status | ||
364 | * - Number of wakeup tries | ||
365 | * - Host Sleep configured status | ||
366 | * - Host Sleep activated status | ||
367 | * - Number of Tx timeouts | ||
368 | * - Number of command timeouts | ||
369 | * - Last timed out command ID | ||
370 | * - Last timed out command action | ||
371 | * - Last command ID | ||
372 | * - Last command action | ||
373 | * - Last command index | ||
374 | * - Last command response ID | ||
375 | * - Last command response index | ||
376 | * - Last event | ||
377 | * - Last event index | ||
378 | * - Number of host to card command failures | ||
379 | * - Number of sleep confirm command failures | ||
380 | * - Number of host to card data failure | ||
381 | * - Number of deauthentication events | ||
382 | * - Number of disassociation events | ||
383 | * - Number of link lost events | ||
384 | * - Number of deauthentication commands | ||
385 | * - Number of association success commands | ||
386 | * - Number of association failure commands | ||
387 | * - Number of commands sent | ||
388 | * - Number of data packets sent | ||
389 | * - Number of command responses received | ||
390 | * - Number of events received | ||
391 | * - Tx BA stream table (TID, RA) | ||
392 | * - Rx reorder table (TID, TA, Start window, Window size, Buffer) | ||
393 | */ | ||
394 | static ssize_t | ||
395 | mwifiex_debug_read(struct file *file, char __user *ubuf, | ||
396 | size_t count, loff_t *ppos) | ||
397 | { | ||
398 | struct mwifiex_private *priv = | ||
399 | (struct mwifiex_private *) file->private_data; | ||
400 | struct mwifiex_debug_data *d = &items[0]; | ||
401 | unsigned long page = get_zeroed_page(GFP_KERNEL); | ||
402 | char *p = (char *) page; | ||
403 | ssize_t ret = 0; | ||
404 | size_t size, addr; | ||
405 | long val; | ||
406 | int i, j; | ||
407 | |||
408 | if (!p) | ||
409 | return -ENOMEM; | ||
410 | |||
411 | ret = mwifiex_get_debug_info(priv, &info); | ||
412 | if (ret) | ||
413 | goto free_and_exit; | ||
414 | |||
415 | for (i = 0; i < num_of_items; i++) { | ||
416 | p += sprintf(p, "%s=", d[i].name); | ||
417 | |||
418 | size = d[i].size / d[i].num; | ||
419 | |||
420 | if (i < (num_of_items - 3)) | ||
421 | addr = d[i].addr + (size_t) &info; | ||
422 | else /* The last 3 items are struct mwifiex_adapter variables */ | ||
423 | addr = d[i].addr + (size_t) priv->adapter; | ||
424 | |||
425 | for (j = 0; j < d[i].num; j++) { | ||
426 | switch (size) { | ||
427 | case 1: | ||
428 | val = *((u8 *) addr); | ||
429 | break; | ||
430 | case 2: | ||
431 | val = *((u16 *) addr); | ||
432 | break; | ||
433 | case 4: | ||
434 | val = *((u32 *) addr); | ||
435 | break; | ||
436 | case 8: | ||
437 | val = *((long long *) addr); | ||
438 | break; | ||
439 | default: | ||
440 | val = -1; | ||
441 | break; | ||
442 | } | ||
443 | |||
444 | p += sprintf(p, "%#lx ", val); | ||
445 | addr += size; | ||
446 | } | ||
447 | |||
448 | p += sprintf(p, "\n"); | ||
449 | } | ||
450 | |||
451 | if (info.tx_tbl_num) { | ||
452 | p += sprintf(p, "Tx BA stream table:\n"); | ||
453 | for (i = 0; i < info.tx_tbl_num; i++) | ||
454 | p += sprintf(p, "tid = %d, " | ||
455 | "ra = %02x:%02x:%02x:%02x:%02x:%02x\n", | ||
456 | info.tx_tbl[i].tid, info.tx_tbl[i].ra[0], | ||
457 | info.tx_tbl[i].ra[1], info.tx_tbl[i].ra[2], | ||
458 | info.tx_tbl[i].ra[3], info.tx_tbl[i].ra[4], | ||
459 | info.tx_tbl[i].ra[5]); | ||
460 | } | ||
461 | |||
462 | if (info.rx_tbl_num) { | ||
463 | p += sprintf(p, "Rx reorder table:\n"); | ||
464 | for (i = 0; i < info.rx_tbl_num; i++) { | ||
465 | |||
466 | p += sprintf(p, "tid = %d, " | ||
467 | "ta = %02x:%02x:%02x:%02x:%02x:%02x, " | ||
468 | "start_win = %d, " | ||
469 | "win_size = %d, buffer: ", | ||
470 | info.rx_tbl[i].tid, | ||
471 | info.rx_tbl[i].ta[0], info.rx_tbl[i].ta[1], | ||
472 | info.rx_tbl[i].ta[2], info.rx_tbl[i].ta[3], | ||
473 | info.rx_tbl[i].ta[4], info.rx_tbl[i].ta[5], | ||
474 | info.rx_tbl[i].start_win, | ||
475 | info.rx_tbl[i].win_size); | ||
476 | |||
477 | for (j = 0; j < info.rx_tbl[i].win_size; j++) | ||
478 | p += sprintf(p, "%c ", | ||
479 | info.rx_tbl[i].buffer[j] ? | ||
480 | '1' : '0'); | ||
481 | |||
482 | p += sprintf(p, "\n"); | ||
483 | } | ||
484 | } | ||
485 | |||
486 | ret = simple_read_from_buffer(ubuf, count, ppos, (char *) page, | ||
487 | (unsigned long) p - page); | ||
488 | |||
489 | free_and_exit: | ||
490 | free_page(page); | ||
491 | return ret; | ||
492 | } | ||
493 | |||
494 | static u32 saved_reg_type, saved_reg_offset, saved_reg_value; | ||
495 | |||
496 | /* | ||
497 | * Proc regrdwr file write handler. | ||
498 | * | ||
499 | * This function is called when the 'regrdwr' file is opened for writing | ||
500 | * | ||
501 | * This function can be used to write to a register. | ||
502 | */ | ||
503 | static ssize_t | ||
504 | mwifiex_regrdwr_write(struct file *file, | ||
505 | const char __user *ubuf, size_t count, loff_t *ppos) | ||
506 | { | ||
507 | unsigned long addr = get_zeroed_page(GFP_KERNEL); | ||
508 | char *buf = (char *) addr; | ||
509 | size_t buf_size = min(count, (size_t) (PAGE_SIZE - 1)); | ||
510 | int ret = 0; | ||
511 | u32 reg_type = 0, reg_offset = 0, reg_value = UINT_MAX; | ||
512 | |||
513 | if (!buf) | ||
514 | return -ENOMEM; | ||
515 | |||
516 | |||
517 | if (copy_from_user(buf, ubuf, buf_size)) { | ||
518 | ret = -EFAULT; | ||
519 | goto done; | ||
520 | } | ||
521 | |||
522 | sscanf(buf, "%u %x %x", ®_type, ®_offset, ®_value); | ||
523 | |||
524 | if (reg_type == 0 || reg_offset == 0) { | ||
525 | ret = -EINVAL; | ||
526 | goto done; | ||
527 | } else { | ||
528 | saved_reg_type = reg_type; | ||
529 | saved_reg_offset = reg_offset; | ||
530 | saved_reg_value = reg_value; | ||
531 | ret = count; | ||
532 | } | ||
533 | done: | ||
534 | free_page(addr); | ||
535 | return ret; | ||
536 | } | ||
537 | |||
538 | /* | ||
539 | * Proc regrdwr file read handler. | ||
540 | * | ||
541 | * This function is called when the 'regrdwr' file is opened for reading | ||
542 | * | ||
543 | * This function can be used to read from a register. | ||
544 | */ | ||
545 | static ssize_t | ||
546 | mwifiex_regrdwr_read(struct file *file, char __user *ubuf, | ||
547 | size_t count, loff_t *ppos) | ||
548 | { | ||
549 | struct mwifiex_private *priv = | ||
550 | (struct mwifiex_private *) file->private_data; | ||
551 | unsigned long addr = get_zeroed_page(GFP_KERNEL); | ||
552 | char *buf = (char *) addr; | ||
553 | int pos = 0, ret = 0; | ||
554 | u32 reg_value; | ||
555 | |||
556 | if (!buf) | ||
557 | return -ENOMEM; | ||
558 | |||
559 | if (!saved_reg_type) { | ||
560 | /* No command has been given */ | ||
561 | pos += snprintf(buf, PAGE_SIZE, "0"); | ||
562 | goto done; | ||
563 | } | ||
564 | /* Set command has been given */ | ||
565 | if (saved_reg_value != UINT_MAX) { | ||
566 | ret = mwifiex_reg_write(priv, saved_reg_type, saved_reg_offset, | ||
567 | saved_reg_value); | ||
568 | |||
569 | pos += snprintf(buf, PAGE_SIZE, "%u 0x%x 0x%x\n", | ||
570 | saved_reg_type, saved_reg_offset, | ||
571 | saved_reg_value); | ||
572 | |||
573 | ret = simple_read_from_buffer(ubuf, count, ppos, buf, pos); | ||
574 | |||
575 | goto done; | ||
576 | } | ||
577 | /* Get command has been given */ | ||
578 | ret = mwifiex_reg_read(priv, saved_reg_type, | ||
579 | saved_reg_offset, ®_value); | ||
580 | if (ret) { | ||
581 | ret = -EINVAL; | ||
582 | goto done; | ||
583 | } | ||
584 | |||
585 | pos += snprintf(buf, PAGE_SIZE, "%u 0x%x 0x%x\n", saved_reg_type, | ||
586 | saved_reg_offset, reg_value); | ||
587 | |||
588 | ret = simple_read_from_buffer(ubuf, count, ppos, buf, pos); | ||
589 | |||
590 | done: | ||
591 | free_page(addr); | ||
592 | return ret; | ||
593 | } | ||
594 | |||
595 | static u32 saved_offset = -1, saved_bytes = -1; | ||
596 | |||
597 | /* | ||
598 | * Proc rdeeprom file write handler. | ||
599 | * | ||
600 | * This function is called when the 'rdeeprom' file is opened for writing | ||
601 | * | ||
602 | * This function can be used to write to a RDEEPROM location. | ||
603 | */ | ||
604 | static ssize_t | ||
605 | mwifiex_rdeeprom_write(struct file *file, | ||
606 | const char __user *ubuf, size_t count, loff_t *ppos) | ||
607 | { | ||
608 | unsigned long addr = get_zeroed_page(GFP_KERNEL); | ||
609 | char *buf = (char *) addr; | ||
610 | size_t buf_size = min(count, (size_t) (PAGE_SIZE - 1)); | ||
611 | int ret = 0; | ||
612 | int offset = -1, bytes = -1; | ||
613 | |||
614 | if (!buf) | ||
615 | return -ENOMEM; | ||
616 | |||
617 | |||
618 | if (copy_from_user(buf, ubuf, buf_size)) { | ||
619 | ret = -EFAULT; | ||
620 | goto done; | ||
621 | } | ||
622 | |||
623 | sscanf(buf, "%d %d", &offset, &bytes); | ||
624 | |||
625 | if (offset == -1 || bytes == -1) { | ||
626 | ret = -EINVAL; | ||
627 | goto done; | ||
628 | } else { | ||
629 | saved_offset = offset; | ||
630 | saved_bytes = bytes; | ||
631 | ret = count; | ||
632 | } | ||
633 | done: | ||
634 | free_page(addr); | ||
635 | return ret; | ||
636 | } | ||
637 | |||
638 | /* | ||
639 | * Proc rdeeprom read write handler. | ||
640 | * | ||
641 | * This function is called when the 'rdeeprom' file is opened for reading | ||
642 | * | ||
643 | * This function can be used to read from a RDEEPROM location. | ||
644 | */ | ||
645 | static ssize_t | ||
646 | mwifiex_rdeeprom_read(struct file *file, char __user *ubuf, | ||
647 | size_t count, loff_t *ppos) | ||
648 | { | ||
649 | struct mwifiex_private *priv = | ||
650 | (struct mwifiex_private *) file->private_data; | ||
651 | unsigned long addr = get_zeroed_page(GFP_KERNEL); | ||
652 | char *buf = (char *) addr; | ||
653 | int pos = 0, ret = 0, i = 0; | ||
654 | u8 value[MAX_EEPROM_DATA]; | ||
655 | |||
656 | if (!buf) | ||
657 | return -ENOMEM; | ||
658 | |||
659 | if (saved_offset == -1) { | ||
660 | /* No command has been given */ | ||
661 | pos += snprintf(buf, PAGE_SIZE, "0"); | ||
662 | goto done; | ||
663 | } | ||
664 | |||
665 | /* Get command has been given */ | ||
666 | ret = mwifiex_eeprom_read(priv, (u16) saved_offset, | ||
667 | (u16) saved_bytes, value); | ||
668 | if (ret) { | ||
669 | ret = -EINVAL; | ||
670 | goto done; | ||
671 | } | ||
672 | |||
673 | pos += snprintf(buf, PAGE_SIZE, "%d %d ", saved_offset, saved_bytes); | ||
674 | |||
675 | for (i = 0; i < saved_bytes; i++) | ||
676 | pos += snprintf(buf + strlen(buf), PAGE_SIZE, "%d ", value[i]); | ||
677 | |||
678 | ret = simple_read_from_buffer(ubuf, count, ppos, buf, pos); | ||
679 | |||
680 | done: | ||
681 | free_page(addr); | ||
682 | return ret; | ||
683 | } | ||
684 | |||
685 | |||
686 | #define MWIFIEX_DFS_ADD_FILE(name) do { \ | ||
687 | if (!debugfs_create_file(#name, 0644, priv->dfs_dev_dir, \ | ||
688 | priv, &mwifiex_dfs_##name##_fops)) \ | ||
689 | return; \ | ||
690 | } while (0); | ||
691 | |||
692 | #define MWIFIEX_DFS_FILE_OPS(name) \ | ||
693 | static const struct file_operations mwifiex_dfs_##name##_fops = { \ | ||
694 | .read = mwifiex_##name##_read, \ | ||
695 | .write = mwifiex_##name##_write, \ | ||
696 | .open = mwifiex_open_generic, \ | ||
697 | }; | ||
698 | |||
699 | #define MWIFIEX_DFS_FILE_READ_OPS(name) \ | ||
700 | static const struct file_operations mwifiex_dfs_##name##_fops = { \ | ||
701 | .read = mwifiex_##name##_read, \ | ||
702 | .open = mwifiex_open_generic, \ | ||
703 | }; | ||
704 | |||
705 | #define MWIFIEX_DFS_FILE_WRITE_OPS(name) \ | ||
706 | static const struct file_operations mwifiex_dfs_##name##_fops = { \ | ||
707 | .write = mwifiex_##name##_write, \ | ||
708 | .open = mwifiex_open_generic, \ | ||
709 | }; | ||
710 | |||
711 | |||
712 | MWIFIEX_DFS_FILE_READ_OPS(info); | ||
713 | MWIFIEX_DFS_FILE_READ_OPS(debug); | ||
714 | MWIFIEX_DFS_FILE_READ_OPS(getlog); | ||
715 | MWIFIEX_DFS_FILE_OPS(regrdwr); | ||
716 | MWIFIEX_DFS_FILE_OPS(rdeeprom); | ||
717 | |||
718 | /* | ||
719 | * This function creates the debug FS directory structure and the files. | ||
720 | */ | ||
721 | void | ||
722 | mwifiex_dev_debugfs_init(struct mwifiex_private *priv) | ||
723 | { | ||
724 | if (!mwifiex_dfs_dir || !priv) | ||
725 | return; | ||
726 | |||
727 | priv->dfs_dev_dir = debugfs_create_dir(priv->netdev->name, | ||
728 | mwifiex_dfs_dir); | ||
729 | |||
730 | if (!priv->dfs_dev_dir) | ||
731 | return; | ||
732 | |||
733 | MWIFIEX_DFS_ADD_FILE(info); | ||
734 | MWIFIEX_DFS_ADD_FILE(debug); | ||
735 | MWIFIEX_DFS_ADD_FILE(getlog); | ||
736 | MWIFIEX_DFS_ADD_FILE(regrdwr); | ||
737 | MWIFIEX_DFS_ADD_FILE(rdeeprom); | ||
738 | |||
739 | return; | ||
740 | } | ||
741 | |||
742 | /* | ||
743 | * This function removes the debug FS directory structure and the files. | ||
744 | */ | ||
745 | void | ||
746 | mwifiex_dev_debugfs_remove(struct mwifiex_private *priv) | ||
747 | { | ||
748 | if (!priv) | ||
749 | return; | ||
750 | |||
751 | debugfs_remove_recursive(priv->dfs_dev_dir); | ||
752 | return; | ||
753 | } | ||
754 | |||
755 | /* | ||
756 | * This function creates the top level proc directory. | ||
757 | */ | ||
758 | void | ||
759 | mwifiex_debugfs_init(void) | ||
760 | { | ||
761 | if (!mwifiex_dfs_dir) | ||
762 | mwifiex_dfs_dir = debugfs_create_dir("mwifiex", NULL); | ||
763 | } | ||
764 | |||
765 | /* | ||
766 | * This function removes the top level proc directory. | ||
767 | */ | ||
768 | void | ||
769 | mwifiex_debugfs_remove(void) | ||
770 | { | ||
771 | if (mwifiex_dfs_dir) | ||
772 | debugfs_remove(mwifiex_dfs_dir); | ||
773 | } | ||
diff --git a/drivers/net/wireless/mwifiex/decl.h b/drivers/net/wireless/mwifiex/decl.h new file mode 100644 index 000000000000..4e1f115d3ec3 --- /dev/null +++ b/drivers/net/wireless/mwifiex/decl.h | |||
@@ -0,0 +1,177 @@ | |||
1 | /* | ||
2 | * Marvell Wireless LAN device driver: generic data structures and APIs | ||
3 | * | ||
4 | * Copyright (C) 2011, Marvell International Ltd. | ||
5 | * | ||
6 | * This software file (the "File") is distributed by Marvell International | ||
7 | * Ltd. under the terms of the GNU General Public License Version 2, June 1991 | ||
8 | * (the "License"). You may use, redistribute and/or modify this File in | ||
9 | * accordance with the terms and conditions of the License, a copy of which | ||
10 | * is available by writing to the Free Software Foundation, Inc., | ||
11 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the | ||
12 | * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. | ||
13 | * | ||
14 | * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE | ||
15 | * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE | ||
16 | * ARE EXPRESSLY DISCLAIMED. The License provides additional details about | ||
17 | * this warranty disclaimer. | ||
18 | */ | ||
19 | |||
20 | #ifndef _MWIFIEX_DECL_H_ | ||
21 | #define _MWIFIEX_DECL_H_ | ||
22 | |||
23 | #undef pr_fmt | ||
24 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | ||
25 | |||
26 | #include <linux/wait.h> | ||
27 | #include <linux/timer.h> | ||
28 | #include <linux/ieee80211.h> | ||
29 | |||
30 | |||
31 | #define MWIFIEX_MAX_BSS_NUM (1) | ||
32 | |||
33 | #define MWIFIEX_MIN_DATA_HEADER_LEN 32 /* (sizeof(mwifiex_txpd)) */ | ||
34 | |||
35 | #define MWIFIEX_MAX_TX_BASTREAM_SUPPORTED 2 | ||
36 | #define MWIFIEX_MAX_RX_BASTREAM_SUPPORTED 16 | ||
37 | |||
38 | #define MWIFIEX_AMPDU_DEF_TXWINSIZE 32 | ||
39 | #define MWIFIEX_AMPDU_DEF_RXWINSIZE 16 | ||
40 | #define MWIFIEX_DEFAULT_BLOCK_ACK_TIMEOUT 0xffff | ||
41 | |||
42 | #define MWIFIEX_RATE_INDEX_HRDSSS0 0 | ||
43 | #define MWIFIEX_RATE_INDEX_HRDSSS3 3 | ||
44 | #define MWIFIEX_RATE_INDEX_OFDM0 4 | ||
45 | #define MWIFIEX_RATE_INDEX_OFDM7 11 | ||
46 | #define MWIFIEX_RATE_INDEX_MCS0 12 | ||
47 | |||
48 | #define MWIFIEX_RATE_BITMAP_OFDM0 16 | ||
49 | #define MWIFIEX_RATE_BITMAP_OFDM7 23 | ||
50 | #define MWIFIEX_RATE_BITMAP_MCS0 32 | ||
51 | #define MWIFIEX_RATE_BITMAP_MCS127 159 | ||
52 | |||
53 | #define MWIFIEX_RX_DATA_BUF_SIZE (4 * 1024) | ||
54 | #define MWIFIEX_RX_CMD_BUF_SIZE (2 * 1024) | ||
55 | |||
56 | #define MWIFIEX_RTS_MIN_VALUE (0) | ||
57 | #define MWIFIEX_RTS_MAX_VALUE (2347) | ||
58 | #define MWIFIEX_FRAG_MIN_VALUE (256) | ||
59 | #define MWIFIEX_FRAG_MAX_VALUE (2346) | ||
60 | |||
61 | #define MWIFIEX_SDIO_BLOCK_SIZE 256 | ||
62 | |||
63 | #define MWIFIEX_BUF_FLAG_REQUEUED_PKT BIT(0) | ||
64 | |||
65 | enum mwifiex_error_code { | ||
66 | MWIFIEX_ERROR_NO_ERROR = 0, | ||
67 | MWIFIEX_ERROR_FW_NOT_READY = 0x00000001, | ||
68 | MWIFIEX_ERROR_FW_BUSY, | ||
69 | MWIFIEX_ERROR_FW_CMDRESP, | ||
70 | MWIFIEX_ERROR_PKT_SIZE_INVALID = 0x80000001, | ||
71 | MWIFIEX_ERROR_PKT_TIMEOUT, | ||
72 | MWIFIEX_ERROR_CMD_INVALID, | ||
73 | MWIFIEX_ERROR_CMD_TIMEOUT, | ||
74 | MWIFIEX_ERROR_CMD_DNLD_FAIL, | ||
75 | MWIFIEX_ERROR_CMD_CANCEL, | ||
76 | MWIFIEX_ERROR_CMD_RESP_FAIL, | ||
77 | MWIFIEX_ERROR_ASSOC_FAIL, | ||
78 | MWIFIEX_ERROR_EVENT_UNKNOWN, | ||
79 | MWIFIEX_ERROR_INVALID_PARAMETER, | ||
80 | }; | ||
81 | |||
82 | enum mwifiex_bss_type { | ||
83 | MWIFIEX_BSS_TYPE_STA = 0, | ||
84 | MWIFIEX_BSS_TYPE_UAP = 1, | ||
85 | MWIFIEX_BSS_TYPE_ANY = 0xff, | ||
86 | }; | ||
87 | |||
88 | enum mwifiex_bss_role { | ||
89 | MWIFIEX_BSS_ROLE_STA = 0, | ||
90 | MWIFIEX_BSS_ROLE_UAP = 1, | ||
91 | MWIFIEX_BSS_ROLE_ANY = 0xff, | ||
92 | }; | ||
93 | |||
94 | #define BSS_ROLE_BIT_MASK BIT(0) | ||
95 | |||
96 | #define GET_BSS_ROLE(priv) ((priv)->bss_role & BSS_ROLE_BIT_MASK) | ||
97 | |||
98 | enum mwifiex_data_frame_type { | ||
99 | MWIFIEX_DATA_FRAME_TYPE_ETH_II = 0, | ||
100 | MWIFIEX_DATA_FRAME_TYPE_802_11, | ||
101 | }; | ||
102 | |||
103 | struct mwifiex_fw_image { | ||
104 | u8 *helper_buf; | ||
105 | u32 helper_len; | ||
106 | u8 *fw_buf; | ||
107 | u32 fw_len; | ||
108 | }; | ||
109 | |||
110 | struct mwifiex_802_11_ssid { | ||
111 | u32 ssid_len; | ||
112 | u8 ssid[IEEE80211_MAX_SSID_LEN]; | ||
113 | }; | ||
114 | |||
115 | struct mwifiex_wait_queue { | ||
116 | u32 bss_index; | ||
117 | wait_queue_head_t *wait; | ||
118 | u16 *condition; | ||
119 | u32 start_time; | ||
120 | int status; | ||
121 | u32 enabled; | ||
122 | }; | ||
123 | |||
124 | struct mwifiex_rxinfo { | ||
125 | u8 bss_index; | ||
126 | struct sk_buff *parent; | ||
127 | u8 use_count; | ||
128 | }; | ||
129 | |||
130 | struct mwifiex_txinfo { | ||
131 | u32 status_code; | ||
132 | u8 flags; | ||
133 | u8 bss_index; | ||
134 | }; | ||
135 | |||
136 | struct mwifiex_bss_attr { | ||
137 | u32 bss_type; | ||
138 | u32 frame_type; | ||
139 | u32 active; | ||
140 | u32 bss_priority; | ||
141 | u32 bss_num; | ||
142 | }; | ||
143 | |||
144 | enum mwifiex_cmd_result_e { | ||
145 | MWIFIEX_CMD_RESULT_SUCCESS = 0, | ||
146 | MWIFIEX_CMD_RESULT_FAILURE = 1, | ||
147 | MWIFIEX_CMD_RESULT_TIMEOUT = 2, | ||
148 | MWIFIEX_CMD_RESULT_INVALID_DATA = 3 | ||
149 | } __packed; | ||
150 | |||
151 | enum mwifiex_wmm_ac_e { | ||
152 | WMM_AC_BK, | ||
153 | WMM_AC_BE, | ||
154 | WMM_AC_VI, | ||
155 | WMM_AC_VO | ||
156 | } __packed; | ||
157 | |||
158 | enum mwifiex_wmm_queue_config_action_e { | ||
159 | MWIFIEX_WMM_QUEUE_CONFIG_ACTION_GET = 0, | ||
160 | MWIFIEX_WMM_QUEUE_CONFIG_ACTION_SET = 1, | ||
161 | MWIFIEX_WMM_QUEUE_CONFIG_ACTION_DEFAULT = 2, | ||
162 | MWIFIEX_WMM_QUEUE_CONFIG_ACTION_MAX | ||
163 | } __packed; | ||
164 | |||
165 | enum mwifiex_wmm_queue_stats_action_e { | ||
166 | MWIFIEX_WMM_STATS_ACTION_START = 0, | ||
167 | MWIFIEX_WMM_STATS_ACTION_STOP = 1, | ||
168 | MWIFIEX_WMM_STATS_ACTION_GET_CLR = 2, | ||
169 | MWIFIEX_WMM_STATS_ACTION_SET_CFG = 3, /* Not currently used */ | ||
170 | MWIFIEX_WMM_STATS_ACTION_GET_CFG = 4, /* Not currently used */ | ||
171 | MWIFIEX_WMM_STATS_ACTION_MAX | ||
172 | } __packed; | ||
173 | |||
174 | struct mwifiex_device { | ||
175 | struct mwifiex_bss_attr bss_attr[MWIFIEX_MAX_BSS_NUM]; | ||
176 | }; | ||
177 | #endif /* !_MWIFIEX_DECL_H_ */ | ||
diff --git a/drivers/net/wireless/mwifiex/fw.h b/drivers/net/wireless/mwifiex/fw.h new file mode 100644 index 000000000000..e5dae45b11d2 --- /dev/null +++ b/drivers/net/wireless/mwifiex/fw.h | |||
@@ -0,0 +1,1376 @@ | |||
1 | /* | ||
2 | * Marvell Wireless LAN device driver: Firmware specific macros & structures | ||
3 | * | ||
4 | * Copyright (C) 2011, Marvell International Ltd. | ||
5 | * | ||
6 | * This software file (the "File") is distributed by Marvell International | ||
7 | * Ltd. under the terms of the GNU General Public License Version 2, June 1991 | ||
8 | * (the "License"). You may use, redistribute and/or modify this File in | ||
9 | * accordance with the terms and conditions of the License, a copy of which | ||
10 | * is available by writing to the Free Software Foundation, Inc., | ||
11 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the | ||
12 | * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. | ||
13 | * | ||
14 | * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE | ||
15 | * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE | ||
16 | * ARE EXPRESSLY DISCLAIMED. The License provides additional details about | ||
17 | * this warranty disclaimer. | ||
18 | */ | ||
19 | |||
20 | #ifndef _MWIFIEX_FW_H_ | ||
21 | #define _MWIFIEX_FW_H_ | ||
22 | |||
23 | #include <linux/if_ether.h> | ||
24 | |||
25 | |||
26 | #define INTF_HEADER_LEN 4 | ||
27 | |||
28 | struct rfc_1042_hdr { | ||
29 | u8 llc_dsap; | ||
30 | u8 llc_ssap; | ||
31 | u8 llc_ctrl; | ||
32 | u8 snap_oui[3]; | ||
33 | u16 snap_type; | ||
34 | }; | ||
35 | |||
36 | struct rx_packet_hdr { | ||
37 | struct ethhdr eth803_hdr; | ||
38 | struct rfc_1042_hdr rfc1042_hdr; | ||
39 | }; | ||
40 | |||
41 | struct tx_packet_hdr { | ||
42 | struct ethhdr eth803_hdr; | ||
43 | struct rfc_1042_hdr rfc1042_hdr; | ||
44 | }; | ||
45 | |||
46 | #define B_SUPPORTED_RATES 5 | ||
47 | #define G_SUPPORTED_RATES 9 | ||
48 | #define BG_SUPPORTED_RATES 13 | ||
49 | #define A_SUPPORTED_RATES 9 | ||
50 | #define HOSTCMD_SUPPORTED_RATES 14 | ||
51 | #define N_SUPPORTED_RATES 3 | ||
52 | #define ALL_802_11_BANDS (BAND_A | BAND_B | BAND_G | BAND_GN) | ||
53 | |||
54 | #define FW_MULTI_BANDS_SUPPORT (BIT(8) | BIT(9) | BIT(10) | BIT(11)) | ||
55 | #define IS_SUPPORT_MULTI_BANDS(adapter) \ | ||
56 | (adapter->fw_cap_info & FW_MULTI_BANDS_SUPPORT) | ||
57 | #define GET_FW_DEFAULT_BANDS(adapter) \ | ||
58 | ((adapter->fw_cap_info >> 8) & ALL_802_11_BANDS) | ||
59 | |||
60 | #define SHORT_SLOT_TIME_DISABLED(CapInfo) (CapInfo &= ~BIT(10)) | ||
61 | #define SHORT_SLOT_TIME_ENABLED(CapInfo) (CapInfo |= BIT(10)) | ||
62 | |||
63 | extern u8 supported_rates_b[B_SUPPORTED_RATES]; | ||
64 | extern u8 supported_rates_g[G_SUPPORTED_RATES]; | ||
65 | extern u8 supported_rates_bg[BG_SUPPORTED_RATES]; | ||
66 | extern u8 supported_rates_a[A_SUPPORTED_RATES]; | ||
67 | extern u8 supported_rates_n[N_SUPPORTED_RATES]; | ||
68 | |||
69 | #define HostCmd_WEP_KEY_INDEX_MASK 0x3fff | ||
70 | |||
71 | #define KEY_INFO_ENABLED 0x01 | ||
72 | enum KEY_TYPE_ID { | ||
73 | KEY_TYPE_ID_WEP = 0, | ||
74 | KEY_TYPE_ID_TKIP, | ||
75 | KEY_TYPE_ID_AES, | ||
76 | KEY_TYPE_ID_WAPI, | ||
77 | }; | ||
78 | |||
79 | enum KEY_INFO_WEP { | ||
80 | KEY_INFO_WEP_MCAST = 0x01, | ||
81 | KEY_INFO_WEP_UNICAST = 0x02, | ||
82 | KEY_INFO_WEP_ENABLED = 0x04 | ||
83 | }; | ||
84 | |||
85 | enum KEY_INFO_TKIP { | ||
86 | KEY_INFO_TKIP_MCAST = 0x01, | ||
87 | KEY_INFO_TKIP_UNICAST = 0x02, | ||
88 | KEY_INFO_TKIP_ENABLED = 0x04 | ||
89 | }; | ||
90 | |||
91 | enum KEY_INFO_AES { | ||
92 | KEY_INFO_AES_MCAST = 0x01, | ||
93 | KEY_INFO_AES_UNICAST = 0x02, | ||
94 | KEY_INFO_AES_ENABLED = 0x04 | ||
95 | }; | ||
96 | |||
97 | #define WAPI_KEY_LEN 50 | ||
98 | |||
99 | enum KEY_INFO_WAPI { | ||
100 | KEY_INFO_WAPI_MCAST = 0x01, | ||
101 | KEY_INFO_WAPI_UNICAST = 0x02, | ||
102 | KEY_INFO_WAPI_ENABLED = 0x04 | ||
103 | }; | ||
104 | |||
105 | #define MAX_POLL_TRIES 100 | ||
106 | |||
107 | #define MAX_MULTI_INTERFACE_POLL_TRIES 1000 | ||
108 | |||
109 | #define MAX_FIRMWARE_POLL_TRIES 100 | ||
110 | |||
111 | #define FIRMWARE_READY 0xfedc | ||
112 | |||
113 | #define FIRMWARE_TRANSFER_NBLOCK 2 | ||
114 | |||
115 | enum MWIFIEX_802_11_PRIVACY_FILTER { | ||
116 | MWIFIEX_802_11_PRIV_FILTER_ACCEPT_ALL, | ||
117 | MWIFIEX_802_11_PRIV_FILTER_8021X_WEP | ||
118 | }; | ||
119 | |||
120 | enum MWIFIEX_802_11_WEP_STATUS { | ||
121 | MWIFIEX_802_11_WEP_ENABLED, | ||
122 | MWIFIEX_802_11_WEP_DISABLED, | ||
123 | }; | ||
124 | |||
125 | #define CAL_SNR(RSSI, NF) ((s16)((s16)(RSSI)-(s16)(NF))) | ||
126 | |||
127 | #define PROPRIETARY_TLV_BASE_ID 0x0100 | ||
128 | #define TLV_TYPE_KEY_MATERIAL (PROPRIETARY_TLV_BASE_ID + 0) | ||
129 | #define TLV_TYPE_CHANLIST (PROPRIETARY_TLV_BASE_ID + 1) | ||
130 | #define TLV_TYPE_NUMPROBES (PROPRIETARY_TLV_BASE_ID + 2) | ||
131 | #define TLV_TYPE_RSSI_LOW (PROPRIETARY_TLV_BASE_ID + 4) | ||
132 | #define TLV_TYPE_SNR_LOW (PROPRIETARY_TLV_BASE_ID + 5) | ||
133 | #define TLV_TYPE_FAILCOUNT (PROPRIETARY_TLV_BASE_ID + 6) | ||
134 | #define TLV_TYPE_BCNMISS (PROPRIETARY_TLV_BASE_ID + 7) | ||
135 | #define TLV_TYPE_LEDBEHAVIOR (PROPRIETARY_TLV_BASE_ID + 9) | ||
136 | #define TLV_TYPE_PASSTHROUGH (PROPRIETARY_TLV_BASE_ID + 10) | ||
137 | #define TLV_TYPE_POWER_TBL_2_4GHZ (PROPRIETARY_TLV_BASE_ID + 12) | ||
138 | #define TLV_TYPE_POWER_TBL_5GHZ (PROPRIETARY_TLV_BASE_ID + 13) | ||
139 | #define TLV_TYPE_WMMQSTATUS (PROPRIETARY_TLV_BASE_ID + 16) | ||
140 | #define TLV_TYPE_WILDCARDSSID (PROPRIETARY_TLV_BASE_ID + 18) | ||
141 | #define TLV_TYPE_TSFTIMESTAMP (PROPRIETARY_TLV_BASE_ID + 19) | ||
142 | #define TLV_TYPE_RSSI_HIGH (PROPRIETARY_TLV_BASE_ID + 22) | ||
143 | #define TLV_TYPE_SNR_HIGH (PROPRIETARY_TLV_BASE_ID + 23) | ||
144 | |||
145 | #define TLV_TYPE_STARTBGSCANLATER (PROPRIETARY_TLV_BASE_ID + 30) | ||
146 | #define TLV_TYPE_AUTH_TYPE (PROPRIETARY_TLV_BASE_ID + 31) | ||
147 | #define TLV_TYPE_LINK_QUALITY (PROPRIETARY_TLV_BASE_ID + 36) | ||
148 | #define TLV_TYPE_RSSI_LOW_DATA (PROPRIETARY_TLV_BASE_ID + 38) | ||
149 | #define TLV_TYPE_SNR_LOW_DATA (PROPRIETARY_TLV_BASE_ID + 39) | ||
150 | #define TLV_TYPE_RSSI_HIGH_DATA (PROPRIETARY_TLV_BASE_ID + 40) | ||
151 | #define TLV_TYPE_SNR_HIGH_DATA (PROPRIETARY_TLV_BASE_ID + 41) | ||
152 | |||
153 | #define TLV_TYPE_CHANNELBANDLIST (PROPRIETARY_TLV_BASE_ID + 42) | ||
154 | #define TLV_TYPE_WAPI_IE (PROPRIETARY_TLV_BASE_ID + 94) | ||
155 | #define TLV_TYPE_BSSID (PROPRIETARY_TLV_BASE_ID + 35) | ||
156 | |||
157 | #define MWIFIEX_TX_DATA_BUF_SIZE_2K 2048 | ||
158 | |||
159 | #define TLV_TYPE_HT_CAP (PROPRIETARY_TLV_BASE_ID + 74) | ||
160 | #define TLV_TYPE_HT_INFO (PROPRIETARY_TLV_BASE_ID + 75) | ||
161 | #define TLV_SECONDARY_CHANNEL_OFFSET (PROPRIETARY_TLV_BASE_ID + 76) | ||
162 | #define TLV_TYPE_2040BSS_COEXISTENCE (PROPRIETARY_TLV_BASE_ID + 77) | ||
163 | #define TLV_TYPE_OVERLAP_BSS_SCAN_PARAM (PROPRIETARY_TLV_BASE_ID + 78) | ||
164 | #define TLV_TYPE_EXTCAP (PROPRIETARY_TLV_BASE_ID + 79) | ||
165 | #define TLV_TYPE_HT_OPERATIONAL_MCS_SET (PROPRIETARY_TLV_BASE_ID + 80) | ||
166 | |||
167 | #define ADDBA_TID_MASK (BIT(2) | BIT(3) | BIT(4) | BIT(5)) | ||
168 | #define DELBA_TID_MASK (BIT(12) | BIT(13) | BIT(14) | BIT(15)) | ||
169 | #define SSN_MASK 0xfff0 | ||
170 | |||
171 | #define BA_RESULT_SUCCESS 0x0 | ||
172 | #define BA_RESULT_FAILURE 0x1 | ||
173 | #define BA_RESULT_TIMEOUT 0x2 | ||
174 | #define BA_RESULT_DATA_INVALID 0x3 | ||
175 | |||
176 | #define IS_BASTREAM_SETUP(ptr) (ptr->ba_status) | ||
177 | |||
178 | #define BA_STREAM_NOT_ALLOWED 0xff | ||
179 | |||
180 | #define IS_11N_ENABLED(priv) ((priv->adapter->config_bands & BAND_GN || \ | ||
181 | priv->adapter->config_bands & BAND_AN) \ | ||
182 | && priv->curr_bss_params.bss_descriptor.bcn_ht_cap) | ||
183 | #define INITIATOR_BIT(DelBAParamSet) (((DelBAParamSet) &\ | ||
184 | BIT(DELBA_INITIATOR_POS)) >> DELBA_INITIATOR_POS) | ||
185 | |||
186 | #define MWIFIEX_TX_DATA_BUF_SIZE_4K 4096 | ||
187 | #define MWIFIEX_TX_DATA_BUF_SIZE_8K 8192 | ||
188 | #define MAX_RX_AMPDU_SIZE_64K 0x03 | ||
189 | #define NON_GREENFIELD_STAS 0x04 | ||
190 | |||
191 | #define HWSPEC_GREENFIELD_SUPP BIT(29) | ||
192 | #define HWSPEC_RXSTBC_SUPP BIT(26) | ||
193 | #define HWSPEC_SHORTGI40_SUPP BIT(24) | ||
194 | #define HWSPEC_SHORTGI20_SUPP BIT(23) | ||
195 | #define HWSPEC_CHANBW40_SUPP BIT(17) | ||
196 | |||
197 | #define DEFAULT_11N_CAP_MASK (HWSPEC_SHORTGI20_SUPP | HWSPEC_RXSTBC_SUPP) | ||
198 | #define ISSUPP_11NENABLED(FwCapInfo) (FwCapInfo & BIT(11)) | ||
199 | #define ISSUPP_MAXAMSDU(Dot11nDevCap) (Dot11nDevCap & BIT(31)) | ||
200 | #define ISSUPP_BEAMFORMING(Dot11nDevCap) (Dot11nDevCap & BIT(30)) | ||
201 | #define ISSUPP_GREENFIELD(Dot11nDevCap) (Dot11nDevCap & BIT(29)) | ||
202 | #define ISSUPP_AMPDU(Dot11nDevCap) (Dot11nDevCap & BIT(28)) | ||
203 | #define ISSUPP_MIMOPS(Dot11nDevCap) (Dot11nDevCap & BIT(27)) | ||
204 | #define ISSUPP_RXSTBC(Dot11nDevCap) (Dot11nDevCap & BIT(26)) | ||
205 | #define ISSUPP_TXSTBC(Dot11nDevCap) (Dot11nDevCap & BIT(25)) | ||
206 | #define ISSUPP_SHORTGI40(Dot11nDevCap) (Dot11nDevCap & BIT(24)) | ||
207 | #define ISSUPP_SHORTGI20(Dot11nDevCap) (Dot11nDevCap & BIT(23)) | ||
208 | #define ISSUPP_RXLDPC(Dot11nDevCap) (Dot11nDevCap & BIT(22)) | ||
209 | #define GET_DELAYEDBACK(Dot11nDevCap) (((Dot11nDevCap >> 20) & 0x03)) | ||
210 | #define GET_IMMEDIATEBACK(Dot11nDevCap) (((Dot11nDevCap >> 18) & 0x03)) | ||
211 | #define ISSUPP_CHANWIDTH40(Dot11nDevCap) (Dot11nDevCap & BIT(17)) | ||
212 | #define ISSUPP_CHANWIDTH20(Dot11nDevCap) (Dot11nDevCap & BIT(16)) | ||
213 | #define ISSUPP_CHANWIDTH10(Dot11nDevCap) (Dot11nDevCap & BIT(15)) | ||
214 | #define ISENABLED_40MHZ_INTOLARENT(Dot11nDevCap) (Dot11nDevCap & BIT(8)) | ||
215 | #define ISSUPP_RXANTENNAD(Dot11nDevCap) (Dot11nDevCap & BIT(7)) | ||
216 | #define ISSUPP_RXANTENNAC(Dot11nDevCap) (Dot11nDevCap & BIT(6)) | ||
217 | #define ISSUPP_RXANTENNAB(Dot11nDevCap) (Dot11nDevCap & BIT(5)) | ||
218 | #define ISSUPP_RXANTENNAA(Dot11nDevCap) (Dot11nDevCap & BIT(4)) | ||
219 | #define ISSUPP_TXANTENNAD(Dot11nDevCap) (Dot11nDevCap & BIT(3)) | ||
220 | #define ISSUPP_TXANTENNAC(Dot11nDevCap) (Dot11nDevCap & BIT(2)) | ||
221 | #define ISSUPP_TXANTENNAB(Dot11nDevCap) (Dot11nDevCap & BIT(1)) | ||
222 | #define ISSUPP_TXANTENNAA(Dot11nDevCap) (Dot11nDevCap & BIT(0)) | ||
223 | #define SETSUPP_CHANWIDTH40(Dot11nDevCap) (Dot11nDevCap |= BIT(17)) | ||
224 | #define RESETSUPP_CHANWIDTH40(Dot11nDevCap) (Dot11nDevCap &= ~BIT(17)) | ||
225 | #define GET_TXMCSSUPP(DevMCSSupported) (DevMCSSupported >> 4) | ||
226 | #define GET_RXMCSSUPP(DevMCSSupported) (DevMCSSupported & 0x0f) | ||
227 | #define GETHT_SUPPCHANWIDTH(HTCapInfo) (HTCapInfo & BIT(1)) | ||
228 | #define GETHT_GREENFIELD(HTCapInfo) (HTCapInfo & BIT(4)) | ||
229 | #define GETHT_SHORTGI20(HTCapInfo) (HTCapInfo & BIT(5)) | ||
230 | #define GETHT_SHORTGI40(HTCapInfo) (HTCapInfo & BIT(6)) | ||
231 | #define GETHT_TXSTBC(HTCapInfo) (HTCapInfo & BIT(7)) | ||
232 | #define GETHT_RXSTBC(HTCapInfo) ((HTCapInfo >> 8) & 0x03) | ||
233 | #define GETHT_DELAYEDBACK(HTCapInfo) (HTCapInfo & BIT(10)) | ||
234 | #define GETHT_MAXAMSDU(HTCapInfo) (HTCapInfo & BIT(11)) | ||
235 | #define SETHT_SUPPCHANWIDTH(HTCapInfo) (HTCapInfo |= BIT(1)) | ||
236 | #define SETHT_GREENFIELD(HTCapInfo) (HTCapInfo |= BIT(4)) | ||
237 | #define SETHT_SHORTGI20(HTCapInfo) (HTCapInfo |= BIT(5)) | ||
238 | #define SETHT_SHORTGI40(HTCapInfo) (HTCapInfo |= BIT(6)) | ||
239 | #define SETHT_TXSTBC(HTCapInfo) (HTCapInfo |= BIT(7)) | ||
240 | #define SETHT_RXSTBC(HTCapInfo, value) (HTCapInfo |= (value << 8)) | ||
241 | #define SETHT_DELAYEDBACK(HTCapInfo) (HTCapInfo |= BIT(10)) | ||
242 | #define SETHT_MAXAMSDU(HTCapInfo) (HTCapInfo |= BIT(11)) | ||
243 | #define SETHT_DSSSCCK40(HTCapInfo) (HTCapInfo |= BIT(12)) | ||
244 | #define SETHT_40MHZ_INTOLARANT(HTCapInfo) (HTCapInfo |= BIT(14)) | ||
245 | #define RESETHT_SUPPCHANWIDTH(HTCapInfo) (HTCapInfo &= ~BIT(1)) | ||
246 | #define RESETHT_GREENFIELD(HTCapInfo) (HTCapInfo &= ~BIT(4)) | ||
247 | #define RESETHT_SHORTGI20(HTCapInfo) (HTCapInfo &= ~BIT(5)) | ||
248 | #define RESETHT_SHORTGI40(HTCapInfo) (HTCapInfo &= ~BIT(6)) | ||
249 | #define RESETHT_TXSTBC(HTCapInfo) (HTCapInfo &= ~BIT(7)) | ||
250 | #define RESETHT_RXSTBC(HTCapInfo) (HTCapInfo &= ~(0x03 << 8)) | ||
251 | #define RESETHT_DELAYEDBACK(HTCapInfo) (HTCapInfo &= ~BIT(10)) | ||
252 | #define RESETHT_MAXAMSDU(HTCapInfo) (HTCapInfo &= ~BIT(11)) | ||
253 | #define RESETHT_40MHZ_INTOLARANT(HTCapInfo) (HTCapInfo &= ~BIT(14)) | ||
254 | #define RESETHT_EXTCAP_RDG(HTExtCap) (HTExtCap &= ~BIT(11)) | ||
255 | #define SETHT_MCS32(x) (x[4] |= 1) | ||
256 | #define SETHT_MCS_SET_DEFINED(x) (x[12] |= 1) | ||
257 | #define SETHT_RX_HIGHEST_DT_SUPP(x, y) ((*(u16 *) (x + 10)) = y) | ||
258 | #define AMPDU_FACTOR_64K 0x03 | ||
259 | #define SETAMPDU_SIZE(x, y) do { \ | ||
260 | x = x & ~0x03; \ | ||
261 | x |= y & 0x03; \ | ||
262 | } while (0) \ | ||
263 | |||
264 | #define SETAMPDU_SPACING(x, y) do { \ | ||
265 | x = x & ~0x1c; \ | ||
266 | x |= (y & 0x07) << 2; \ | ||
267 | } while (0) \ | ||
268 | |||
269 | #define ISSUPP_BANDA(FwCapInfo) (FwCapInfo & BIT(10)) | ||
270 | #define ISALLOWED_CHANWIDTH40(Field2) (Field2 & BIT(2)) | ||
271 | #define SET_CHANWIDTH40(Field2) (Field2 |= BIT(2)) | ||
272 | #define RESET_CHANWIDTH40(Field2) (Field2 &= ~(BIT(0) | BIT(1) | BIT(2))) | ||
273 | #define GET_SECONDARYCHAN(Field2) (Field2 & (BIT(0) | BIT(1))) | ||
274 | #define SET_SECONDARYCHAN(RadioType, SECCHAN) (RadioType |= (SECCHAN << 4)) | ||
275 | |||
276 | #define LLC_SNAP_LEN 8 | ||
277 | |||
278 | #define TLV_TYPE_RATE_DROP_PATTERN (PROPRIETARY_TLV_BASE_ID + 81) | ||
279 | #define TLV_TYPE_RATE_DROP_CONTROL (PROPRIETARY_TLV_BASE_ID + 82) | ||
280 | #define TLV_TYPE_RATE_SCOPE (PROPRIETARY_TLV_BASE_ID + 83) | ||
281 | |||
282 | #define TLV_TYPE_POWER_GROUP (PROPRIETARY_TLV_BASE_ID + 84) | ||
283 | |||
284 | #define MOD_CLASS_HR_DSSS 0x03 | ||
285 | #define MOD_CLASS_OFDM 0x07 | ||
286 | #define MOD_CLASS_HT 0x08 | ||
287 | #define HT_BW_20 0 | ||
288 | #define HT_BW_40 1 | ||
289 | |||
290 | #define HostCmd_CMD_GET_HW_SPEC 0x0003 | ||
291 | #define HostCmd_CMD_802_11_SCAN 0x0006 | ||
292 | #define HostCmd_CMD_802_11_GET_LOG 0x000b | ||
293 | #define HostCmd_CMD_MAC_MULTICAST_ADR 0x0010 | ||
294 | #define HostCmd_CMD_802_11_EEPROM_ACCESS 0x0059 | ||
295 | #define HostCmd_CMD_802_11_ASSOCIATE 0x0012 | ||
296 | #define HostCmd_CMD_802_11_SNMP_MIB 0x0016 | ||
297 | #define HostCmd_CMD_MAC_REG_ACCESS 0x0019 | ||
298 | #define HostCmd_CMD_BBP_REG_ACCESS 0x001a | ||
299 | #define HostCmd_CMD_RF_REG_ACCESS 0x001b | ||
300 | #define HostCmd_CMD_PMIC_REG_ACCESS 0x00ad | ||
301 | #define HostCmd_CMD_802_11_RF_CHANNEL 0x001d | ||
302 | #define HostCmd_CMD_802_11_DEAUTHENTICATE 0x0024 | ||
303 | #define HostCmd_CMD_MAC_CONTROL 0x0028 | ||
304 | #define HostCmd_CMD_802_11_AD_HOC_START 0x002b | ||
305 | #define HostCmd_CMD_802_11_AD_HOC_JOIN 0x002c | ||
306 | #define HostCmd_CMD_802_11_AD_HOC_STOP 0x0040 | ||
307 | #define HostCmd_CMD_802_11_MAC_ADDRESS 0x004D | ||
308 | #define HostCmd_CMD_802_11D_DOMAIN_INFO 0x005b | ||
309 | #define HostCmd_CMD_802_11_KEY_MATERIAL 0x005e | ||
310 | #define HostCmd_CMD_802_11_BG_SCAN_QUERY 0x006c | ||
311 | #define HostCmd_CMD_WMM_GET_STATUS 0x0071 | ||
312 | #define HostCmd_CMD_802_11_TX_RATE_QUERY 0x007f | ||
313 | #define HostCmd_CMD_802_11_IBSS_COALESCING_STATUS 0x0083 | ||
314 | #define HostCmd_CMD_VERSION_EXT 0x0097 | ||
315 | #define HostCmd_CMD_RSSI_INFO 0x00a4 | ||
316 | #define HostCmd_CMD_FUNC_INIT 0x00a9 | ||
317 | #define HostCmd_CMD_FUNC_SHUTDOWN 0x00aa | ||
318 | #define HostCmd_CMD_11N_CFG 0x00cd | ||
319 | #define HostCmd_CMD_11N_ADDBA_REQ 0x00ce | ||
320 | #define HostCmd_CMD_11N_ADDBA_RSP 0x00cf | ||
321 | #define HostCmd_CMD_11N_DELBA 0x00d0 | ||
322 | #define HostCmd_CMD_RECONFIGURE_TX_BUFF 0x00d9 | ||
323 | #define HostCmd_CMD_AMSDU_AGGR_CTRL 0x00df | ||
324 | #define HostCmd_CMD_TXPWR_CFG 0x00d1 | ||
325 | #define HostCmd_CMD_TX_RATE_CFG 0x00d6 | ||
326 | #define HostCmd_CMD_802_11_PS_MODE_ENH 0x00e4 | ||
327 | #define HostCmd_CMD_802_11_HS_CFG_ENH 0x00e5 | ||
328 | #define HostCmd_CMD_CAU_REG_ACCESS 0x00ed | ||
329 | #define HostCmd_CMD_SET_BSS_MODE 0x00f7 | ||
330 | |||
331 | |||
332 | enum ENH_PS_MODES { | ||
333 | EN_PS = 1, | ||
334 | DIS_PS = 2, | ||
335 | EN_AUTO_DS = 3, | ||
336 | DIS_AUTO_DS = 4, | ||
337 | SLEEP_CONFIRM = 5, | ||
338 | GET_PS = 0, | ||
339 | EN_AUTO_PS = 0xff, | ||
340 | DIS_AUTO_PS = 0xfe, | ||
341 | }; | ||
342 | |||
343 | #define HostCmd_RET_BIT 0x8000 | ||
344 | #define HostCmd_ACT_GEN_GET 0x0000 | ||
345 | #define HostCmd_ACT_GEN_SET 0x0001 | ||
346 | #define HostCmd_ACT_GEN_REMOVE 0x0004 | ||
347 | #define HostCmd_ACT_SET_BOTH 0x0003 | ||
348 | #define HostCmd_ACT_GET_BOTH 0x000c | ||
349 | #define HostCmd_RESULT_OK 0x0000 | ||
350 | #define HostCmd_RESULT_ERROR 0x0001 | ||
351 | #define HostCmd_RESULT_NOT_SUPPORT 0x0002 | ||
352 | #define HostCmd_RESULT_PENDING 0x0003 | ||
353 | #define HostCmd_RESULT_BUSY 0x0004 | ||
354 | #define HostCmd_RESULT_PARTIAL_DATA 0x0005 | ||
355 | |||
356 | #define HostCmd_ACT_MAC_RX_ON 0x0001 | ||
357 | #define HostCmd_ACT_MAC_TX_ON 0x0002 | ||
358 | #define HostCmd_ACT_MAC_WEP_ENABLE 0x0008 | ||
359 | #define HostCmd_ACT_MAC_ETHERNETII_ENABLE 0x0010 | ||
360 | #define HostCmd_ACT_MAC_PROMISCUOUS_ENABLE 0x0080 | ||
361 | #define HostCmd_ACT_MAC_ALL_MULTICAST_ENABLE 0x0100 | ||
362 | #define HostCmd_ACT_MAC_RTS_CTS_ENABLE 0x0200 | ||
363 | #define HostCmd_ACT_MAC_STRICT_PROTECTION_ENABLE 0x0400 | ||
364 | #define HostCmd_ACT_MAC_ADHOC_G_PROTECTION_ON 0x2000 | ||
365 | |||
366 | #define HostCmd_BSS_MODE_BSS 0x0001 | ||
367 | #define HostCmd_BSS_MODE_IBSS 0x0002 | ||
368 | #define HostCmd_BSS_MODE_ANY 0x0003 | ||
369 | |||
370 | #define HostCmd_SCAN_RADIO_TYPE_BG 0 | ||
371 | #define HostCmd_SCAN_RADIO_TYPE_A 1 | ||
372 | |||
373 | #define HOST_SLEEP_CFG_CANCEL 0xffffffff | ||
374 | #define HOST_SLEEP_CFG_COND_DEF 0x0000000f | ||
375 | #define HOST_SLEEP_CFG_GPIO_DEF 0xff | ||
376 | #define HOST_SLEEP_CFG_GAP_DEF 0 | ||
377 | |||
378 | #define CMD_F_HOSTCMD (1 << 0) | ||
379 | #define CMD_F_CANCELED (1 << 1) | ||
380 | |||
381 | #define HostCmd_CMD_ID_MASK 0x0fff | ||
382 | |||
383 | #define HostCmd_SEQ_NUM_MASK 0x00ff | ||
384 | |||
385 | #define HostCmd_BSS_NUM_MASK 0x0f00 | ||
386 | |||
387 | #define HostCmd_BSS_TYPE_MASK 0xf000 | ||
388 | |||
389 | #define HostCmd_SET_SEQ_NO_BSS_INFO(seq, num, type) { \ | ||
390 | (((seq) & 0x00ff) | \ | ||
391 | (((num) & 0x000f) << 8)) | \ | ||
392 | (((type) & 0x000f) << 12); } | ||
393 | |||
394 | #define HostCmd_GET_SEQ_NO(seq) \ | ||
395 | ((seq) & HostCmd_SEQ_NUM_MASK) | ||
396 | |||
397 | #define HostCmd_GET_BSS_NO(seq) \ | ||
398 | (((seq) & HostCmd_BSS_NUM_MASK) >> 8) | ||
399 | |||
400 | #define HostCmd_GET_BSS_TYPE(seq) \ | ||
401 | (((seq) & HostCmd_BSS_TYPE_MASK) >> 12) | ||
402 | |||
403 | #define EVENT_DUMMY_HOST_WAKEUP_SIGNAL 0x00000001 | ||
404 | #define EVENT_LINK_LOST 0x00000003 | ||
405 | #define EVENT_LINK_SENSED 0x00000004 | ||
406 | #define EVENT_MIB_CHANGED 0x00000006 | ||
407 | #define EVENT_INIT_DONE 0x00000007 | ||
408 | #define EVENT_DEAUTHENTICATED 0x00000008 | ||
409 | #define EVENT_DISASSOCIATED 0x00000009 | ||
410 | #define EVENT_PS_AWAKE 0x0000000a | ||
411 | #define EVENT_PS_SLEEP 0x0000000b | ||
412 | #define EVENT_MIC_ERR_MULTICAST 0x0000000d | ||
413 | #define EVENT_MIC_ERR_UNICAST 0x0000000e | ||
414 | #define EVENT_DEEP_SLEEP_AWAKE 0x00000010 | ||
415 | #define EVENT_ADHOC_BCN_LOST 0x00000011 | ||
416 | |||
417 | #define EVENT_WMM_STATUS_CHANGE 0x00000017 | ||
418 | #define EVENT_BG_SCAN_REPORT 0x00000018 | ||
419 | #define EVENT_RSSI_LOW 0x00000019 | ||
420 | #define EVENT_SNR_LOW 0x0000001a | ||
421 | #define EVENT_MAX_FAIL 0x0000001b | ||
422 | #define EVENT_RSSI_HIGH 0x0000001c | ||
423 | #define EVENT_SNR_HIGH 0x0000001d | ||
424 | #define EVENT_IBSS_COALESCED 0x0000001e | ||
425 | #define EVENT_DATA_RSSI_LOW 0x00000024 | ||
426 | #define EVENT_DATA_SNR_LOW 0x00000025 | ||
427 | #define EVENT_DATA_RSSI_HIGH 0x00000026 | ||
428 | #define EVENT_DATA_SNR_HIGH 0x00000027 | ||
429 | #define EVENT_LINK_QUALITY 0x00000028 | ||
430 | #define EVENT_PORT_RELEASE 0x0000002b | ||
431 | #define EVENT_PRE_BEACON_LOST 0x00000031 | ||
432 | #define EVENT_ADDBA 0x00000033 | ||
433 | #define EVENT_DELBA 0x00000034 | ||
434 | #define EVENT_BA_STREAM_TIEMOUT 0x00000037 | ||
435 | #define EVENT_AMSDU_AGGR_CTRL 0x00000042 | ||
436 | #define EVENT_WEP_ICV_ERR 0x00000046 | ||
437 | #define EVENT_HS_ACT_REQ 0x00000047 | ||
438 | #define EVENT_BW_CHANGE 0x00000048 | ||
439 | |||
440 | #define EVENT_HOSTWAKE_STAIE 0x0000004d | ||
441 | |||
442 | #define EVENT_ID_MASK 0xffff | ||
443 | #define BSS_NUM_MASK 0xf | ||
444 | |||
445 | #define EVENT_GET_BSS_NUM(event_cause) \ | ||
446 | (((event_cause) >> 16) & BSS_NUM_MASK) | ||
447 | |||
448 | #define EVENT_GET_BSS_TYPE(event_cause) \ | ||
449 | (((event_cause) >> 24) & 0x00ff) | ||
450 | |||
451 | struct mwifiex_event_wep_icv_err { | ||
452 | u16 reason_code; | ||
453 | u8 src_mac_addr[ETH_ALEN]; | ||
454 | u8 wep_key_index; | ||
455 | u8 wep_key_length; | ||
456 | u8 key[WLAN_KEY_LEN_WEP104]; | ||
457 | }; | ||
458 | |||
459 | struct mwifiex_802_11_fixed_ies { | ||
460 | u8 time_stamp[8]; | ||
461 | __le16 beacon_interval; | ||
462 | __le16 capabilities; | ||
463 | }; | ||
464 | |||
465 | struct mwifiex_ie_types_header { | ||
466 | __le16 type; | ||
467 | __le16 len; | ||
468 | } __packed; | ||
469 | |||
470 | struct mwifiex_ie_types_data { | ||
471 | struct mwifiex_ie_types_header header; | ||
472 | u8 data[1]; | ||
473 | } __packed; | ||
474 | |||
475 | #define MWIFIEX_TxPD_POWER_MGMT_NULL_PACKET 0x01 | ||
476 | #define MWIFIEX_TxPD_POWER_MGMT_LAST_PACKET 0x08 | ||
477 | |||
478 | struct txpd { | ||
479 | u8 bss_type; | ||
480 | u8 bss_num; | ||
481 | __le16 tx_pkt_length; | ||
482 | __le16 tx_pkt_offset; | ||
483 | __le16 tx_pkt_type; | ||
484 | __le32 tx_control; | ||
485 | u8 priority; | ||
486 | u8 flags; | ||
487 | u8 pkt_delay_2ms; | ||
488 | u8 reserved1; | ||
489 | } __packed; | ||
490 | |||
491 | struct rxpd { | ||
492 | u8 bss_type; | ||
493 | u8 bss_num; | ||
494 | u16 rx_pkt_length; | ||
495 | u16 rx_pkt_offset; | ||
496 | u16 rx_pkt_type; | ||
497 | u16 seq_num; | ||
498 | u8 priority; | ||
499 | u8 rx_rate; | ||
500 | s8 snr; | ||
501 | s8 nf; | ||
502 | /* Ht Info [Bit 0] RxRate format: LG=0, HT=1 | ||
503 | * [Bit 1] HT Bandwidth: BW20 = 0, BW40 = 1 | ||
504 | * [Bit 2] HT Guard Interval: LGI = 0, SGI = 1 */ | ||
505 | u8 ht_info; | ||
506 | u8 reserved; | ||
507 | } __packed; | ||
508 | |||
509 | enum mwifiex_chan_scan_mode_bitmasks { | ||
510 | MWIFIEX_PASSIVE_SCAN = BIT(0), | ||
511 | MWIFIEX_DISABLE_CHAN_FILT = BIT(1), | ||
512 | }; | ||
513 | |||
514 | #define SECOND_CHANNEL_BELOW 0x30 | ||
515 | #define SECOND_CHANNEL_ABOVE 0x10 | ||
516 | struct mwifiex_chan_scan_param_set { | ||
517 | u8 radio_type; | ||
518 | u8 chan_number; | ||
519 | u8 chan_scan_mode_bitmap; | ||
520 | __le16 min_scan_time; | ||
521 | __le16 max_scan_time; | ||
522 | } __packed; | ||
523 | |||
524 | struct mwifiex_ie_types_chan_list_param_set { | ||
525 | struct mwifiex_ie_types_header header; | ||
526 | struct mwifiex_chan_scan_param_set chan_scan_param[1]; | ||
527 | } __packed; | ||
528 | |||
529 | struct chan_band_param_set { | ||
530 | u8 radio_type; | ||
531 | u8 chan_number; | ||
532 | }; | ||
533 | |||
534 | struct mwifiex_ie_types_chan_band_list_param_set { | ||
535 | struct mwifiex_ie_types_header header; | ||
536 | struct chan_band_param_set chan_band_param[1]; | ||
537 | } __packed; | ||
538 | |||
539 | struct mwifiex_ie_types_rates_param_set { | ||
540 | struct mwifiex_ie_types_header header; | ||
541 | u8 rates[1]; | ||
542 | } __packed; | ||
543 | |||
544 | struct mwifiex_ie_types_ssid_param_set { | ||
545 | struct mwifiex_ie_types_header header; | ||
546 | u8 ssid[1]; | ||
547 | } __packed; | ||
548 | |||
549 | struct mwifiex_ie_types_num_probes { | ||
550 | struct mwifiex_ie_types_header header; | ||
551 | __le16 num_probes; | ||
552 | } __packed; | ||
553 | |||
554 | struct mwifiex_ie_types_wildcard_ssid_params { | ||
555 | struct mwifiex_ie_types_header header; | ||
556 | u8 max_ssid_length; | ||
557 | u8 ssid[1]; | ||
558 | } __packed; | ||
559 | |||
560 | #define TSF_DATA_SIZE 8 | ||
561 | struct mwifiex_ie_types_tsf_timestamp { | ||
562 | struct mwifiex_ie_types_header header; | ||
563 | u8 tsf_data[1]; | ||
564 | } __packed; | ||
565 | |||
566 | struct mwifiex_cf_param_set { | ||
567 | u8 cfp_cnt; | ||
568 | u8 cfp_period; | ||
569 | u16 cfp_max_duration; | ||
570 | u16 cfp_duration_remaining; | ||
571 | } __packed; | ||
572 | |||
573 | struct mwifiex_ibss_param_set { | ||
574 | u16 atim_window; | ||
575 | } __packed; | ||
576 | |||
577 | struct mwifiex_ie_types_ss_param_set { | ||
578 | struct mwifiex_ie_types_header header; | ||
579 | union { | ||
580 | struct mwifiex_cf_param_set cf_param_set[1]; | ||
581 | struct mwifiex_ibss_param_set ibss_param_set[1]; | ||
582 | } cf_ibss; | ||
583 | } __packed; | ||
584 | |||
585 | struct mwifiex_fh_param_set { | ||
586 | u16 dwell_time; | ||
587 | u8 hop_set; | ||
588 | u8 hop_pattern; | ||
589 | u8 hop_index; | ||
590 | } __packed; | ||
591 | |||
592 | struct mwifiex_ds_param_set { | ||
593 | u8 current_chan; | ||
594 | } __packed; | ||
595 | |||
596 | struct mwifiex_ie_types_phy_param_set { | ||
597 | struct mwifiex_ie_types_header header; | ||
598 | union { | ||
599 | struct mwifiex_fh_param_set fh_param_set[1]; | ||
600 | struct mwifiex_ds_param_set ds_param_set[1]; | ||
601 | } fh_ds; | ||
602 | } __packed; | ||
603 | |||
604 | struct mwifiex_ie_types_auth_type { | ||
605 | struct mwifiex_ie_types_header header; | ||
606 | __le16 auth_type; | ||
607 | } __packed; | ||
608 | |||
609 | struct mwifiex_ie_types_vendor_param_set { | ||
610 | struct mwifiex_ie_types_header header; | ||
611 | u8 ie[MWIFIEX_MAX_VSIE_LEN]; | ||
612 | }; | ||
613 | |||
614 | struct mwifiex_ie_types_rsn_param_set { | ||
615 | struct mwifiex_ie_types_header header; | ||
616 | u8 rsn_ie[1]; | ||
617 | } __packed; | ||
618 | |||
619 | #define KEYPARAMSET_FIXED_LEN 6 | ||
620 | |||
621 | struct mwifiex_ie_type_key_param_set { | ||
622 | __le16 type; | ||
623 | __le16 length; | ||
624 | __le16 key_type_id; | ||
625 | __le16 key_info; | ||
626 | __le16 key_len; | ||
627 | u8 key[50]; | ||
628 | } __packed; | ||
629 | |||
630 | struct host_cmd_ds_802_11_key_material { | ||
631 | __le16 action; | ||
632 | struct mwifiex_ie_type_key_param_set key_param_set; | ||
633 | } __packed; | ||
634 | |||
635 | struct host_cmd_ds_gen { | ||
636 | u16 command; | ||
637 | u16 size; | ||
638 | u16 seq_num; | ||
639 | u16 result; | ||
640 | }; | ||
641 | |||
642 | #define S_DS_GEN sizeof(struct host_cmd_ds_gen) | ||
643 | |||
644 | enum sleep_resp_ctrl { | ||
645 | RESP_NOT_NEEDED = 0, | ||
646 | RESP_NEEDED, | ||
647 | }; | ||
648 | |||
649 | struct mwifiex_ps_param { | ||
650 | __le16 null_pkt_interval; | ||
651 | __le16 multiple_dtims; | ||
652 | __le16 bcn_miss_timeout; | ||
653 | __le16 local_listen_interval; | ||
654 | __le16 adhoc_wake_period; | ||
655 | __le16 mode; | ||
656 | __le16 delay_to_ps; | ||
657 | }; | ||
658 | |||
659 | struct mwifiex_auto_ds_param { | ||
660 | __le16 deep_sleep_timeout; | ||
661 | }; | ||
662 | |||
663 | struct sleep_confirm_param { | ||
664 | __le16 resp_ctrl; | ||
665 | }; | ||
666 | |||
667 | #define BITMAP_AUTO_DS 0x01 | ||
668 | #define BITMAP_STA_PS 0x10 | ||
669 | #define BITMAP_UAP_INACT_PS 0x100 | ||
670 | #define BITMAP_UAP_DTIM_PS 0x200 | ||
671 | struct auto_ps_param { | ||
672 | __le16 ps_bitmap; | ||
673 | /* auto deep sleep parameter, | ||
674 | * sta power save parameter | ||
675 | * uap inactivity parameter | ||
676 | * uap DTIM parameter */ | ||
677 | }; | ||
678 | |||
679 | #define AUTO_PS_FIX_SIZE 4 | ||
680 | |||
681 | #define TLV_TYPE_AUTO_DS_PARAM (PROPRIETARY_TLV_BASE_ID + 113) | ||
682 | #define TLV_TYPE_PS_PARAM (PROPRIETARY_TLV_BASE_ID + 114) | ||
683 | |||
684 | struct mwifiex_ie_types_auto_ds_param { | ||
685 | struct mwifiex_ie_types_header header; | ||
686 | struct mwifiex_auto_ds_param param; | ||
687 | } __packed; | ||
688 | |||
689 | struct mwifiex_ie_types_ps_param { | ||
690 | struct mwifiex_ie_types_header header; | ||
691 | struct mwifiex_ps_param param; | ||
692 | } __packed; | ||
693 | |||
694 | struct host_cmd_ds_802_11_ps_mode_enh { | ||
695 | __le16 action; | ||
696 | |||
697 | union { | ||
698 | struct mwifiex_ps_param opt_ps; | ||
699 | struct mwifiex_auto_ds_param auto_ds; | ||
700 | struct sleep_confirm_param sleep_cfm; | ||
701 | __le16 ps_bitmap; | ||
702 | struct auto_ps_param auto_ps; | ||
703 | } params; | ||
704 | } __packed; | ||
705 | |||
706 | struct host_cmd_ds_get_hw_spec { | ||
707 | __le16 hw_if_version; | ||
708 | __le16 version; | ||
709 | __le16 reserved; | ||
710 | __le16 num_of_mcast_adr; | ||
711 | u8 permanent_addr[ETH_ALEN]; | ||
712 | __le16 region_code; | ||
713 | __le16 number_of_antenna; | ||
714 | __le32 fw_release_number; | ||
715 | __le32 reserved_1; | ||
716 | __le32 reserved_2; | ||
717 | __le32 reserved_3; | ||
718 | __le32 fw_cap_info; | ||
719 | __le32 dot_11n_dev_cap; | ||
720 | u8 dev_mcs_support; | ||
721 | __le16 mp_end_port; /* SDIO only, reserved for other interfacces */ | ||
722 | __le16 reserved_4; | ||
723 | } __packed; | ||
724 | |||
725 | struct host_cmd_ds_802_11_rssi_info { | ||
726 | __le16 action; | ||
727 | __le16 ndata; | ||
728 | __le16 nbcn; | ||
729 | __le16 reserved[9]; | ||
730 | long long reserved_1; | ||
731 | }; | ||
732 | |||
733 | struct host_cmd_ds_802_11_rssi_info_rsp { | ||
734 | __le16 action; | ||
735 | __le16 ndata; | ||
736 | __le16 nbcn; | ||
737 | __le16 data_rssi_last; | ||
738 | __le16 data_nf_last; | ||
739 | __le16 data_rssi_avg; | ||
740 | __le16 data_nf_avg; | ||
741 | __le16 bcn_rssi_last; | ||
742 | __le16 bcn_nf_last; | ||
743 | __le16 bcn_rssi_avg; | ||
744 | __le16 bcn_nf_avg; | ||
745 | long long tsf_bcn; | ||
746 | }; | ||
747 | |||
748 | struct host_cmd_ds_802_11_mac_address { | ||
749 | __le16 action; | ||
750 | u8 mac_addr[ETH_ALEN]; | ||
751 | }; | ||
752 | |||
753 | struct host_cmd_ds_mac_control { | ||
754 | __le16 action; | ||
755 | __le16 reserved; | ||
756 | }; | ||
757 | |||
758 | struct host_cmd_ds_mac_multicast_adr { | ||
759 | __le16 action; | ||
760 | __le16 num_of_adrs; | ||
761 | u8 mac_list[MWIFIEX_MAX_MULTICAST_LIST_SIZE][ETH_ALEN]; | ||
762 | } __packed; | ||
763 | |||
764 | struct host_cmd_ds_802_11_deauthenticate { | ||
765 | u8 mac_addr[ETH_ALEN]; | ||
766 | __le16 reason_code; | ||
767 | } __packed; | ||
768 | |||
769 | struct host_cmd_ds_802_11_associate { | ||
770 | u8 peer_sta_addr[ETH_ALEN]; | ||
771 | __le16 cap_info_bitmap; | ||
772 | __le16 listen_interval; | ||
773 | __le16 beacon_period; | ||
774 | u8 dtim_period; | ||
775 | } __packed; | ||
776 | |||
777 | struct ieee_types_assoc_rsp { | ||
778 | __le16 cap_info_bitmap; | ||
779 | __le16 status_code; | ||
780 | __le16 a_id; | ||
781 | u8 ie_buffer[1]; | ||
782 | } __packed; | ||
783 | |||
784 | struct host_cmd_ds_802_11_associate_rsp { | ||
785 | struct ieee_types_assoc_rsp assoc_rsp; | ||
786 | } __packed; | ||
787 | |||
788 | struct ieee_types_cf_param_set { | ||
789 | u8 element_id; | ||
790 | u8 len; | ||
791 | u8 cfp_cnt; | ||
792 | u8 cfp_period; | ||
793 | u16 cfp_max_duration; | ||
794 | u16 cfp_duration_remaining; | ||
795 | } __packed; | ||
796 | |||
797 | struct ieee_types_ibss_param_set { | ||
798 | u8 element_id; | ||
799 | u8 len; | ||
800 | __le16 atim_window; | ||
801 | } __packed; | ||
802 | |||
803 | union ieee_types_ss_param_set { | ||
804 | struct ieee_types_cf_param_set cf_param_set; | ||
805 | struct ieee_types_ibss_param_set ibss_param_set; | ||
806 | } __packed; | ||
807 | |||
808 | struct ieee_types_fh_param_set { | ||
809 | u8 element_id; | ||
810 | u8 len; | ||
811 | __le16 dwell_time; | ||
812 | u8 hop_set; | ||
813 | u8 hop_pattern; | ||
814 | u8 hop_index; | ||
815 | } __packed; | ||
816 | |||
817 | struct ieee_types_ds_param_set { | ||
818 | u8 element_id; | ||
819 | u8 len; | ||
820 | u8 current_chan; | ||
821 | } __packed; | ||
822 | |||
823 | union ieee_types_phy_param_set { | ||
824 | struct ieee_types_fh_param_set fh_param_set; | ||
825 | struct ieee_types_ds_param_set ds_param_set; | ||
826 | } __packed; | ||
827 | |||
828 | struct host_cmd_ds_802_11_ad_hoc_start { | ||
829 | u8 ssid[IEEE80211_MAX_SSID_LEN]; | ||
830 | u8 bss_mode; | ||
831 | __le16 beacon_period; | ||
832 | u8 dtim_period; | ||
833 | union ieee_types_ss_param_set ss_param_set; | ||
834 | union ieee_types_phy_param_set phy_param_set; | ||
835 | u16 reserved1; | ||
836 | __le16 cap_info_bitmap; | ||
837 | u8 DataRate[HOSTCMD_SUPPORTED_RATES]; | ||
838 | } __packed; | ||
839 | |||
840 | struct host_cmd_ds_802_11_ad_hoc_result { | ||
841 | u8 pad[3]; | ||
842 | u8 bssid[ETH_ALEN]; | ||
843 | } __packed; | ||
844 | |||
845 | struct adhoc_bss_desc { | ||
846 | u8 bssid[ETH_ALEN]; | ||
847 | u8 ssid[IEEE80211_MAX_SSID_LEN]; | ||
848 | u8 bss_mode; | ||
849 | __le16 beacon_period; | ||
850 | u8 dtim_period; | ||
851 | u8 time_stamp[8]; | ||
852 | u8 local_time[8]; | ||
853 | union ieee_types_phy_param_set phy_param_set; | ||
854 | union ieee_types_ss_param_set ss_param_set; | ||
855 | __le16 cap_info_bitmap; | ||
856 | u8 data_rates[HOSTCMD_SUPPORTED_RATES]; | ||
857 | |||
858 | /* | ||
859 | * DO NOT ADD ANY FIELDS TO THIS STRUCTURE. | ||
860 | * It is used in the Adhoc join command and will cause a | ||
861 | * binary layout mismatch with the firmware | ||
862 | */ | ||
863 | } __packed; | ||
864 | |||
865 | struct host_cmd_ds_802_11_ad_hoc_join { | ||
866 | struct adhoc_bss_desc bss_descriptor; | ||
867 | u16 reserved1; | ||
868 | u16 reserved2; | ||
869 | } __packed; | ||
870 | |||
871 | struct host_cmd_ds_802_11_get_log { | ||
872 | __le32 mcast_tx_frame; | ||
873 | __le32 failed; | ||
874 | __le32 retry; | ||
875 | __le32 multi_retry; | ||
876 | __le32 frame_dup; | ||
877 | __le32 rts_success; | ||
878 | __le32 rts_failure; | ||
879 | __le32 ack_failure; | ||
880 | __le32 rx_frag; | ||
881 | __le32 mcast_rx_frame; | ||
882 | __le32 fcs_error; | ||
883 | __le32 tx_frame; | ||
884 | __le32 reserved; | ||
885 | __le32 wep_icv_err_cnt[4]; | ||
886 | }; | ||
887 | |||
888 | struct host_cmd_ds_tx_rate_query { | ||
889 | u8 tx_rate; | ||
890 | /* Ht Info [Bit 0] RxRate format: LG=0, HT=1 | ||
891 | * [Bit 1] HT Bandwidth: BW20 = 0, BW40 = 1 | ||
892 | * [Bit 2] HT Guard Interval: LGI = 0, SGI = 1 */ | ||
893 | u8 ht_info; | ||
894 | } __packed; | ||
895 | |||
896 | enum Host_Sleep_Action { | ||
897 | HS_CONFIGURE = 0x0001, | ||
898 | HS_ACTIVATE = 0x0002, | ||
899 | }; | ||
900 | |||
901 | struct mwifiex_hs_config_param { | ||
902 | __le32 conditions; | ||
903 | u8 gpio; | ||
904 | u8 gap; | ||
905 | } __packed; | ||
906 | |||
907 | struct hs_activate_param { | ||
908 | u16 resp_ctrl; | ||
909 | } __packed; | ||
910 | |||
911 | struct host_cmd_ds_802_11_hs_cfg_enh { | ||
912 | __le16 action; | ||
913 | |||
914 | union { | ||
915 | struct mwifiex_hs_config_param hs_config; | ||
916 | struct hs_activate_param hs_activate; | ||
917 | } params; | ||
918 | } __packed; | ||
919 | |||
920 | enum SNMP_MIB_INDEX { | ||
921 | OP_RATE_SET_I = 1, | ||
922 | DTIM_PERIOD_I = 3, | ||
923 | RTS_THRESH_I = 5, | ||
924 | SHORT_RETRY_LIM_I = 6, | ||
925 | LONG_RETRY_LIM_I = 7, | ||
926 | FRAG_THRESH_I = 8, | ||
927 | DOT11D_I = 9, | ||
928 | }; | ||
929 | |||
930 | #define MAX_SNMP_BUF_SIZE 128 | ||
931 | |||
932 | struct host_cmd_ds_802_11_snmp_mib { | ||
933 | __le16 query_type; | ||
934 | __le16 oid; | ||
935 | __le16 buf_size; | ||
936 | u8 value[1]; | ||
937 | } __packed; | ||
938 | |||
939 | #define RADIO_ON 0x01 | ||
940 | #define RADIO_OFF 0x00 | ||
941 | |||
942 | struct mwifiex_rate_scope { | ||
943 | __le16 type; | ||
944 | __le16 length; | ||
945 | __le16 hr_dsss_rate_bitmap; | ||
946 | __le16 ofdm_rate_bitmap; | ||
947 | __le16 ht_mcs_rate_bitmap[8]; | ||
948 | } __packed; | ||
949 | |||
950 | struct mwifiex_rate_drop_pattern { | ||
951 | __le16 type; | ||
952 | __le16 length; | ||
953 | __le32 rate_drop_mode; | ||
954 | } __packed; | ||
955 | |||
956 | struct host_cmd_ds_tx_rate_cfg { | ||
957 | __le16 action; | ||
958 | __le16 cfg_index; | ||
959 | } __packed; | ||
960 | |||
961 | struct mwifiex_power_group { | ||
962 | u8 modulation_class; | ||
963 | u8 first_rate_code; | ||
964 | u8 last_rate_code; | ||
965 | s8 power_step; | ||
966 | s8 power_min; | ||
967 | s8 power_max; | ||
968 | u8 ht_bandwidth; | ||
969 | u8 reserved; | ||
970 | } __packed; | ||
971 | |||
972 | struct mwifiex_types_power_group { | ||
973 | u16 type; | ||
974 | u16 length; | ||
975 | } __packed; | ||
976 | |||
977 | struct host_cmd_ds_txpwr_cfg { | ||
978 | __le16 action; | ||
979 | __le16 cfg_index; | ||
980 | __le32 mode; | ||
981 | } __packed; | ||
982 | |||
983 | #define MWIFIEX_USER_SCAN_CHAN_MAX 50 | ||
984 | |||
985 | #define MWIFIEX_MAX_SSID_LIST_LENGTH 10 | ||
986 | |||
987 | struct mwifiex_scan_cmd_config { | ||
988 | /* | ||
989 | * BSS Type to be sent in the firmware command | ||
990 | * | ||
991 | * Field can be used to restrict the types of networks returned in the | ||
992 | * scan. Valid settings are: | ||
993 | * | ||
994 | * - MWIFIEX_SCAN_MODE_BSS (infrastructure) | ||
995 | * - MWIFIEX_SCAN_MODE_IBSS (adhoc) | ||
996 | * - MWIFIEX_SCAN_MODE_ANY (unrestricted, adhoc and infrastructure) | ||
997 | */ | ||
998 | u8 bss_mode; | ||
999 | |||
1000 | /* Specific BSSID used to filter scan results in the firmware */ | ||
1001 | u8 specific_bssid[ETH_ALEN]; | ||
1002 | |||
1003 | /* Length of TLVs sent in command starting at tlvBuffer */ | ||
1004 | u32 tlv_buf_len; | ||
1005 | |||
1006 | /* | ||
1007 | * SSID TLV(s) and ChanList TLVs to be sent in the firmware command | ||
1008 | * | ||
1009 | * TLV_TYPE_CHANLIST, mwifiex_ie_types_chan_list_param_set | ||
1010 | * WLAN_EID_SSID, mwifiex_ie_types_ssid_param_set | ||
1011 | */ | ||
1012 | u8 tlv_buf[1]; /* SSID TLV(s) and ChanList TLVs are stored | ||
1013 | here */ | ||
1014 | } __packed; | ||
1015 | |||
1016 | struct mwifiex_user_scan_chan { | ||
1017 | u8 chan_number; | ||
1018 | u8 radio_type; | ||
1019 | u8 scan_type; | ||
1020 | u8 reserved; | ||
1021 | u32 scan_time; | ||
1022 | } __packed; | ||
1023 | |||
1024 | struct mwifiex_user_scan_ssid { | ||
1025 | u8 ssid[IEEE80211_MAX_SSID_LEN + 1]; | ||
1026 | u8 max_len; | ||
1027 | } __packed; | ||
1028 | |||
1029 | struct mwifiex_user_scan_cfg { | ||
1030 | /* | ||
1031 | * Flag set to keep the previous scan table intact | ||
1032 | * | ||
1033 | * If set, the scan results will accumulate, replacing any previous | ||
1034 | * matched entries for a BSS with the new scan data | ||
1035 | */ | ||
1036 | u8 keep_previous_scan; | ||
1037 | /* | ||
1038 | * BSS mode to be sent in the firmware command | ||
1039 | * | ||
1040 | * Field can be used to restrict the types of networks returned in the | ||
1041 | * scan. Valid settings are: | ||
1042 | * | ||
1043 | * - MWIFIEX_SCAN_MODE_BSS (infrastructure) | ||
1044 | * - MWIFIEX_SCAN_MODE_IBSS (adhoc) | ||
1045 | * - MWIFIEX_SCAN_MODE_ANY (unrestricted, adhoc and infrastructure) | ||
1046 | */ | ||
1047 | u8 bss_mode; | ||
1048 | /* Configure the number of probe requests for active chan scans */ | ||
1049 | u8 num_probes; | ||
1050 | u8 reserved; | ||
1051 | /* BSSID filter sent in the firmware command to limit the results */ | ||
1052 | u8 specific_bssid[ETH_ALEN]; | ||
1053 | /* SSID filter list used in the to limit the scan results */ | ||
1054 | struct mwifiex_user_scan_ssid ssid_list[MWIFIEX_MAX_SSID_LIST_LENGTH]; | ||
1055 | /* Variable number (fixed maximum) of channels to scan up */ | ||
1056 | struct mwifiex_user_scan_chan chan_list[MWIFIEX_USER_SCAN_CHAN_MAX]; | ||
1057 | } __packed; | ||
1058 | |||
1059 | struct ie_body { | ||
1060 | u8 grp_key_oui[4]; | ||
1061 | u8 ptk_cnt[2]; | ||
1062 | u8 ptk_body[4]; | ||
1063 | } __packed; | ||
1064 | |||
1065 | struct host_cmd_ds_802_11_scan { | ||
1066 | u8 bss_mode; | ||
1067 | u8 bssid[ETH_ALEN]; | ||
1068 | u8 tlv_buffer[1]; | ||
1069 | } __packed; | ||
1070 | |||
1071 | struct host_cmd_ds_802_11_scan_rsp { | ||
1072 | __le16 bss_descript_size; | ||
1073 | u8 number_of_sets; | ||
1074 | u8 bss_desc_and_tlv_buffer[1]; | ||
1075 | } __packed; | ||
1076 | |||
1077 | struct host_cmd_ds_802_11_bg_scan_query { | ||
1078 | u8 flush; | ||
1079 | } __packed; | ||
1080 | |||
1081 | struct host_cmd_ds_802_11_bg_scan_query_rsp { | ||
1082 | u32 report_condition; | ||
1083 | struct host_cmd_ds_802_11_scan_rsp scan_resp; | ||
1084 | } __packed; | ||
1085 | |||
1086 | struct mwifiex_ietypes_domain_param_set { | ||
1087 | struct mwifiex_ie_types_header header; | ||
1088 | u8 country_code[IEEE80211_COUNTRY_STRING_LEN]; | ||
1089 | struct ieee80211_country_ie_triplet triplet[1]; | ||
1090 | } __packed; | ||
1091 | |||
1092 | struct host_cmd_ds_802_11d_domain_info { | ||
1093 | __le16 action; | ||
1094 | struct mwifiex_ietypes_domain_param_set domain; | ||
1095 | } __packed; | ||
1096 | |||
1097 | struct host_cmd_ds_802_11d_domain_info_rsp { | ||
1098 | __le16 action; | ||
1099 | struct mwifiex_ietypes_domain_param_set domain; | ||
1100 | } __packed; | ||
1101 | |||
1102 | struct host_cmd_ds_11n_addba_req { | ||
1103 | u8 add_req_result; | ||
1104 | u8 peer_mac_addr[ETH_ALEN]; | ||
1105 | u8 dialog_token; | ||
1106 | __le16 block_ack_param_set; | ||
1107 | __le16 block_ack_tmo; | ||
1108 | __le16 ssn; | ||
1109 | } __packed; | ||
1110 | |||
1111 | struct host_cmd_ds_11n_addba_rsp { | ||
1112 | u8 add_rsp_result; | ||
1113 | u8 peer_mac_addr[ETH_ALEN]; | ||
1114 | u8 dialog_token; | ||
1115 | __le16 status_code; | ||
1116 | __le16 block_ack_param_set; | ||
1117 | __le16 block_ack_tmo; | ||
1118 | __le16 ssn; | ||
1119 | } __packed; | ||
1120 | |||
1121 | struct host_cmd_ds_11n_delba { | ||
1122 | u8 del_result; | ||
1123 | u8 peer_mac_addr[ETH_ALEN]; | ||
1124 | __le16 del_ba_param_set; | ||
1125 | __le16 reason_code; | ||
1126 | u8 reserved; | ||
1127 | } __packed; | ||
1128 | |||
1129 | struct host_cmd_ds_11n_batimeout { | ||
1130 | u8 tid; | ||
1131 | u8 peer_mac_addr[ETH_ALEN]; | ||
1132 | u8 origninator; | ||
1133 | } __packed; | ||
1134 | |||
1135 | struct host_cmd_ds_11n_cfg { | ||
1136 | __le16 action; | ||
1137 | __le16 ht_tx_cap; | ||
1138 | __le16 ht_tx_info; | ||
1139 | } __packed; | ||
1140 | |||
1141 | struct host_cmd_ds_txbuf_cfg { | ||
1142 | __le16 action; | ||
1143 | __le16 buff_size; | ||
1144 | __le16 mp_end_port; /* SDIO only, reserved for other interfacces */ | ||
1145 | __le16 reserved3; | ||
1146 | } __packed; | ||
1147 | |||
1148 | struct host_cmd_ds_amsdu_aggr_ctrl { | ||
1149 | __le16 action; | ||
1150 | __le16 enable; | ||
1151 | __le16 curr_buf_size; | ||
1152 | } __packed; | ||
1153 | |||
1154 | struct mwifiex_ie_types_wmm_param_set { | ||
1155 | struct mwifiex_ie_types_header header; | ||
1156 | u8 wmm_ie[1]; | ||
1157 | }; | ||
1158 | |||
1159 | struct mwifiex_ie_types_wmm_queue_status { | ||
1160 | struct mwifiex_ie_types_header header; | ||
1161 | u8 queue_index; | ||
1162 | u8 disabled; | ||
1163 | u16 medium_time; | ||
1164 | u8 flow_required; | ||
1165 | u8 flow_created; | ||
1166 | u32 reserved; | ||
1167 | }; | ||
1168 | |||
1169 | struct ieee_types_vendor_header { | ||
1170 | u8 element_id; | ||
1171 | u8 len; | ||
1172 | u8 oui[3]; | ||
1173 | u8 oui_type; | ||
1174 | u8 oui_subtype; | ||
1175 | u8 version; | ||
1176 | } __packed; | ||
1177 | |||
1178 | struct ieee_types_wmm_ac_parameters { | ||
1179 | u8 aci_aifsn_bitmap; | ||
1180 | u8 ecw_bitmap; | ||
1181 | __le16 tx_op_limit; | ||
1182 | } __packed; | ||
1183 | |||
1184 | struct ieee_types_wmm_parameter { | ||
1185 | /* | ||
1186 | * WMM Parameter IE - Vendor Specific Header: | ||
1187 | * element_id [221/0xdd] | ||
1188 | * Len [24] | ||
1189 | * Oui [00:50:f2] | ||
1190 | * OuiType [2] | ||
1191 | * OuiSubType [1] | ||
1192 | * Version [1] | ||
1193 | */ | ||
1194 | struct ieee_types_vendor_header vend_hdr; | ||
1195 | u8 qos_info_bitmap; | ||
1196 | u8 reserved; | ||
1197 | struct ieee_types_wmm_ac_parameters ac_params[IEEE80211_MAX_QUEUES]; | ||
1198 | } __packed; | ||
1199 | |||
1200 | struct ieee_types_wmm_info { | ||
1201 | |||
1202 | /* | ||
1203 | * WMM Info IE - Vendor Specific Header: | ||
1204 | * element_id [221/0xdd] | ||
1205 | * Len [7] | ||
1206 | * Oui [00:50:f2] | ||
1207 | * OuiType [2] | ||
1208 | * OuiSubType [0] | ||
1209 | * Version [1] | ||
1210 | */ | ||
1211 | struct ieee_types_vendor_header vend_hdr; | ||
1212 | |||
1213 | u8 qos_info_bitmap; | ||
1214 | } __packed; | ||
1215 | |||
1216 | struct host_cmd_ds_wmm_get_status { | ||
1217 | u8 queue_status_tlv[sizeof(struct mwifiex_ie_types_wmm_queue_status) * | ||
1218 | IEEE80211_MAX_QUEUES]; | ||
1219 | u8 wmm_param_tlv[sizeof(struct ieee_types_wmm_parameter) + 2]; | ||
1220 | } __packed; | ||
1221 | |||
1222 | struct mwifiex_wmm_ac_status { | ||
1223 | u8 disabled; | ||
1224 | u8 flow_required; | ||
1225 | u8 flow_created; | ||
1226 | }; | ||
1227 | |||
1228 | struct mwifiex_ie_types_htcap { | ||
1229 | struct mwifiex_ie_types_header header; | ||
1230 | struct ieee80211_ht_cap ht_cap; | ||
1231 | } __packed; | ||
1232 | |||
1233 | struct mwifiex_ie_types_htinfo { | ||
1234 | struct mwifiex_ie_types_header header; | ||
1235 | struct ieee80211_ht_info ht_info; | ||
1236 | } __packed; | ||
1237 | |||
1238 | struct mwifiex_ie_types_2040bssco { | ||
1239 | struct mwifiex_ie_types_header header; | ||
1240 | u8 bss_co_2040; | ||
1241 | } __packed; | ||
1242 | |||
1243 | struct mwifiex_ie_types_extcap { | ||
1244 | struct mwifiex_ie_types_header header; | ||
1245 | u8 ext_cap; | ||
1246 | } __packed; | ||
1247 | |||
1248 | struct host_cmd_ds_mac_reg_access { | ||
1249 | __le16 action; | ||
1250 | __le16 offset; | ||
1251 | __le32 value; | ||
1252 | } __packed; | ||
1253 | |||
1254 | struct host_cmd_ds_bbp_reg_access { | ||
1255 | __le16 action; | ||
1256 | __le16 offset; | ||
1257 | u8 value; | ||
1258 | u8 reserved[3]; | ||
1259 | } __packed; | ||
1260 | |||
1261 | struct host_cmd_ds_rf_reg_access { | ||
1262 | __le16 action; | ||
1263 | __le16 offset; | ||
1264 | u8 value; | ||
1265 | u8 reserved[3]; | ||
1266 | } __packed; | ||
1267 | |||
1268 | struct host_cmd_ds_pmic_reg_access { | ||
1269 | __le16 action; | ||
1270 | __le16 offset; | ||
1271 | u8 value; | ||
1272 | u8 reserved[3]; | ||
1273 | } __packed; | ||
1274 | |||
1275 | struct host_cmd_ds_802_11_eeprom_access { | ||
1276 | __le16 action; | ||
1277 | |||
1278 | __le16 offset; | ||
1279 | __le16 byte_count; | ||
1280 | u8 value; | ||
1281 | } __packed; | ||
1282 | |||
1283 | struct host_cmd_ds_802_11_rf_channel { | ||
1284 | __le16 action; | ||
1285 | __le16 current_channel; | ||
1286 | __le16 rf_type; | ||
1287 | __le16 reserved; | ||
1288 | u8 reserved_1[32]; | ||
1289 | } __packed; | ||
1290 | |||
1291 | struct host_cmd_ds_version_ext { | ||
1292 | u8 version_str_sel; | ||
1293 | char version_str[128]; | ||
1294 | } __packed; | ||
1295 | |||
1296 | struct host_cmd_ds_802_11_ibss_status { | ||
1297 | __le16 action; | ||
1298 | __le16 enable; | ||
1299 | u8 bssid[ETH_ALEN]; | ||
1300 | __le16 beacon_interval; | ||
1301 | __le16 atim_window; | ||
1302 | __le16 use_g_rate_protect; | ||
1303 | } __packed; | ||
1304 | |||
1305 | #define CONNECTION_TYPE_INFRA 0 | ||
1306 | #define CONNECTION_TYPE_ADHOC 1 | ||
1307 | |||
1308 | struct host_cmd_ds_set_bss_mode { | ||
1309 | u8 con_type; | ||
1310 | } __packed; | ||
1311 | |||
1312 | struct host_cmd_ds_command { | ||
1313 | __le16 command; | ||
1314 | __le16 size; | ||
1315 | __le16 seq_num; | ||
1316 | __le16 result; | ||
1317 | union { | ||
1318 | struct host_cmd_ds_get_hw_spec hw_spec; | ||
1319 | struct host_cmd_ds_mac_control mac_ctrl; | ||
1320 | struct host_cmd_ds_802_11_mac_address mac_addr; | ||
1321 | struct host_cmd_ds_mac_multicast_adr mc_addr; | ||
1322 | struct host_cmd_ds_802_11_get_log get_log; | ||
1323 | struct host_cmd_ds_802_11_rssi_info rssi_info; | ||
1324 | struct host_cmd_ds_802_11_rssi_info_rsp rssi_info_rsp; | ||
1325 | struct host_cmd_ds_802_11_snmp_mib smib; | ||
1326 | struct host_cmd_ds_802_11_rf_channel rf_channel; | ||
1327 | struct host_cmd_ds_tx_rate_query tx_rate; | ||
1328 | struct host_cmd_ds_tx_rate_cfg tx_rate_cfg; | ||
1329 | struct host_cmd_ds_txpwr_cfg txp_cfg; | ||
1330 | struct host_cmd_ds_802_11_ps_mode_enh psmode_enh; | ||
1331 | struct host_cmd_ds_802_11_hs_cfg_enh opt_hs_cfg; | ||
1332 | struct host_cmd_ds_802_11_scan scan; | ||
1333 | struct host_cmd_ds_802_11_scan_rsp scan_resp; | ||
1334 | struct host_cmd_ds_802_11_bg_scan_query bg_scan_query; | ||
1335 | struct host_cmd_ds_802_11_bg_scan_query_rsp bg_scan_query_resp; | ||
1336 | struct host_cmd_ds_802_11_associate associate; | ||
1337 | struct host_cmd_ds_802_11_associate_rsp associate_rsp; | ||
1338 | struct host_cmd_ds_802_11_deauthenticate deauth; | ||
1339 | struct host_cmd_ds_802_11_ad_hoc_start adhoc_start; | ||
1340 | struct host_cmd_ds_802_11_ad_hoc_result adhoc_result; | ||
1341 | struct host_cmd_ds_802_11_ad_hoc_join adhoc_join; | ||
1342 | struct host_cmd_ds_802_11d_domain_info domain_info; | ||
1343 | struct host_cmd_ds_802_11d_domain_info_rsp domain_info_resp; | ||
1344 | struct host_cmd_ds_11n_addba_req add_ba_req; | ||
1345 | struct host_cmd_ds_11n_addba_rsp add_ba_rsp; | ||
1346 | struct host_cmd_ds_11n_delba del_ba; | ||
1347 | struct host_cmd_ds_txbuf_cfg tx_buf; | ||
1348 | struct host_cmd_ds_amsdu_aggr_ctrl amsdu_aggr_ctrl; | ||
1349 | struct host_cmd_ds_11n_cfg htcfg; | ||
1350 | struct host_cmd_ds_wmm_get_status get_wmm_status; | ||
1351 | struct host_cmd_ds_802_11_key_material key_material; | ||
1352 | struct host_cmd_ds_version_ext verext; | ||
1353 | struct host_cmd_ds_802_11_ibss_status ibss_coalescing; | ||
1354 | struct host_cmd_ds_mac_reg_access mac_reg; | ||
1355 | struct host_cmd_ds_bbp_reg_access bbp_reg; | ||
1356 | struct host_cmd_ds_rf_reg_access rf_reg; | ||
1357 | struct host_cmd_ds_pmic_reg_access pmic_reg; | ||
1358 | struct host_cmd_ds_set_bss_mode bss_mode; | ||
1359 | struct host_cmd_ds_802_11_eeprom_access eeprom; | ||
1360 | } params; | ||
1361 | } __packed; | ||
1362 | |||
1363 | struct mwifiex_opt_sleep_confirm { | ||
1364 | __le16 command; | ||
1365 | __le16 size; | ||
1366 | __le16 seq_num; | ||
1367 | __le16 result; | ||
1368 | __le16 action; | ||
1369 | struct sleep_confirm_param sleep_cfm; | ||
1370 | } __packed; | ||
1371 | |||
1372 | struct mwifiex_opt_sleep_confirm_buffer { | ||
1373 | u8 hdr[4]; | ||
1374 | struct mwifiex_opt_sleep_confirm ps_cfm_sleep; | ||
1375 | } __packed; | ||
1376 | #endif /* !_MWIFIEX_FW_H_ */ | ||
diff --git a/drivers/net/wireless/mwifiex/init.c b/drivers/net/wireless/mwifiex/init.c new file mode 100644 index 000000000000..07ebc97e19c0 --- /dev/null +++ b/drivers/net/wireless/mwifiex/init.c | |||
@@ -0,0 +1,665 @@ | |||
1 | /* | ||
2 | * Marvell Wireless LAN device driver: HW/FW Initialization | ||
3 | * | ||
4 | * Copyright (C) 2011, Marvell International Ltd. | ||
5 | * | ||
6 | * This software file (the "File") is distributed by Marvell International | ||
7 | * Ltd. under the terms of the GNU General Public License Version 2, June 1991 | ||
8 | * (the "License"). You may use, redistribute and/or modify this File in | ||
9 | * accordance with the terms and conditions of the License, a copy of which | ||
10 | * is available by writing to the Free Software Foundation, Inc., | ||
11 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the | ||
12 | * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. | ||
13 | * | ||
14 | * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE | ||
15 | * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE | ||
16 | * ARE EXPRESSLY DISCLAIMED. The License provides additional details about | ||
17 | * this warranty disclaimer. | ||
18 | */ | ||
19 | |||
20 | #include "decl.h" | ||
21 | #include "ioctl.h" | ||
22 | #include "util.h" | ||
23 | #include "fw.h" | ||
24 | #include "main.h" | ||
25 | #include "wmm.h" | ||
26 | #include "11n.h" | ||
27 | |||
28 | /* | ||
29 | * This function adds a BSS priority table to the table list. | ||
30 | * | ||
31 | * The function allocates a new BSS priority table node and adds it to | ||
32 | * the end of BSS priority table list, kept in driver memory. | ||
33 | */ | ||
34 | static int mwifiex_add_bss_prio_tbl(struct mwifiex_private *priv) | ||
35 | { | ||
36 | struct mwifiex_adapter *adapter = priv->adapter; | ||
37 | struct mwifiex_bss_prio_node *bss_prio; | ||
38 | int status = 0; | ||
39 | unsigned long flags; | ||
40 | |||
41 | bss_prio = kzalloc(sizeof(struct mwifiex_bss_prio_node), GFP_KERNEL); | ||
42 | if (!bss_prio) { | ||
43 | dev_err(adapter->dev, "%s: failed to alloc bss_prio\n", | ||
44 | __func__); | ||
45 | return -1; | ||
46 | } | ||
47 | |||
48 | bss_prio->priv = priv; | ||
49 | INIT_LIST_HEAD(&bss_prio->list); | ||
50 | if (!adapter->bss_prio_tbl[priv->bss_priority].bss_prio_cur) | ||
51 | adapter->bss_prio_tbl[priv->bss_priority].bss_prio_cur = | ||
52 | bss_prio; | ||
53 | |||
54 | spin_lock_irqsave(&adapter->bss_prio_tbl[priv->bss_priority] | ||
55 | .bss_prio_lock, flags); | ||
56 | list_add_tail(&bss_prio->list, | ||
57 | &adapter->bss_prio_tbl[priv->bss_priority] | ||
58 | .bss_prio_head); | ||
59 | spin_unlock_irqrestore(&adapter->bss_prio_tbl[priv->bss_priority] | ||
60 | .bss_prio_lock, flags); | ||
61 | |||
62 | return status; | ||
63 | } | ||
64 | |||
65 | /* | ||
66 | * This function initializes the private structure and sets default | ||
67 | * values to the members. | ||
68 | * | ||
69 | * Additionally, it also initializes all the locks and sets up all the | ||
70 | * lists. | ||
71 | */ | ||
72 | static int mwifiex_init_priv(struct mwifiex_private *priv) | ||
73 | { | ||
74 | u32 i; | ||
75 | int ret = 0; | ||
76 | |||
77 | priv->media_connected = false; | ||
78 | memset(priv->curr_addr, 0xff, ETH_ALEN); | ||
79 | |||
80 | priv->pkt_tx_ctrl = 0; | ||
81 | priv->bss_mode = MWIFIEX_BSS_MODE_INFRA; | ||
82 | priv->data_rate = 0; /* Initially indicate the rate as auto */ | ||
83 | priv->is_data_rate_auto = true; | ||
84 | priv->bcn_avg_factor = DEFAULT_BCN_AVG_FACTOR; | ||
85 | priv->data_avg_factor = DEFAULT_DATA_AVG_FACTOR; | ||
86 | |||
87 | priv->sec_info.wep_status = MWIFIEX_802_11_WEP_DISABLED; | ||
88 | priv->sec_info.authentication_mode = MWIFIEX_AUTH_MODE_OPEN; | ||
89 | priv->sec_info.encryption_mode = MWIFIEX_ENCRYPTION_MODE_NONE; | ||
90 | for (i = 0; i < ARRAY_SIZE(priv->wep_key); i++) | ||
91 | memset(&priv->wep_key[i], 0, sizeof(struct mwifiex_wep_key)); | ||
92 | priv->wep_key_curr_index = 0; | ||
93 | priv->curr_pkt_filter = HostCmd_ACT_MAC_RX_ON | HostCmd_ACT_MAC_TX_ON | | ||
94 | HostCmd_ACT_MAC_ETHERNETII_ENABLE; | ||
95 | |||
96 | priv->beacon_period = 100; /* beacon interval */ ; | ||
97 | priv->attempted_bss_desc = NULL; | ||
98 | memset(&priv->curr_bss_params, 0, sizeof(priv->curr_bss_params)); | ||
99 | priv->listen_interval = MWIFIEX_DEFAULT_LISTEN_INTERVAL; | ||
100 | |||
101 | memset(&priv->prev_ssid, 0, sizeof(priv->prev_ssid)); | ||
102 | memset(&priv->prev_bssid, 0, sizeof(priv->prev_bssid)); | ||
103 | memset(&priv->assoc_rsp_buf, 0, sizeof(priv->assoc_rsp_buf)); | ||
104 | priv->assoc_rsp_size = 0; | ||
105 | priv->adhoc_channel = DEFAULT_AD_HOC_CHANNEL; | ||
106 | priv->atim_window = 0; | ||
107 | priv->adhoc_state = ADHOC_IDLE; | ||
108 | priv->tx_power_level = 0; | ||
109 | priv->max_tx_power_level = 0; | ||
110 | priv->min_tx_power_level = 0; | ||
111 | priv->tx_rate = 0; | ||
112 | priv->rxpd_htinfo = 0; | ||
113 | priv->rxpd_rate = 0; | ||
114 | priv->rate_bitmap = 0; | ||
115 | priv->data_rssi_last = 0; | ||
116 | priv->data_rssi_avg = 0; | ||
117 | priv->data_nf_avg = 0; | ||
118 | priv->data_nf_last = 0; | ||
119 | priv->bcn_rssi_last = 0; | ||
120 | priv->bcn_rssi_avg = 0; | ||
121 | priv->bcn_nf_avg = 0; | ||
122 | priv->bcn_nf_last = 0; | ||
123 | memset(&priv->wpa_ie, 0, sizeof(priv->wpa_ie)); | ||
124 | memset(&priv->aes_key, 0, sizeof(priv->aes_key)); | ||
125 | priv->wpa_ie_len = 0; | ||
126 | priv->wpa_is_gtk_set = false; | ||
127 | |||
128 | memset(&priv->assoc_tlv_buf, 0, sizeof(priv->assoc_tlv_buf)); | ||
129 | priv->assoc_tlv_buf_len = 0; | ||
130 | memset(&priv->wps, 0, sizeof(priv->wps)); | ||
131 | memset(&priv->gen_ie_buf, 0, sizeof(priv->gen_ie_buf)); | ||
132 | priv->gen_ie_buf_len = 0; | ||
133 | memset(priv->vs_ie, 0, sizeof(priv->vs_ie)); | ||
134 | |||
135 | priv->wmm_required = true; | ||
136 | priv->wmm_enabled = false; | ||
137 | priv->wmm_qosinfo = 0; | ||
138 | priv->curr_bcn_buf = NULL; | ||
139 | priv->curr_bcn_size = 0; | ||
140 | |||
141 | priv->scan_block = false; | ||
142 | |||
143 | ret = mwifiex_add_bss_prio_tbl(priv); | ||
144 | |||
145 | return ret; | ||
146 | } | ||
147 | |||
148 | /* | ||
149 | * This function allocates buffers for members of the adapter | ||
150 | * structure. | ||
151 | * | ||
152 | * The memory allocated includes scan table, command buffers, and | ||
153 | * sleep confirm command buffer. In addition, the queues are | ||
154 | * also initialized. | ||
155 | */ | ||
156 | static int mwifiex_allocate_adapter(struct mwifiex_adapter *adapter) | ||
157 | { | ||
158 | int ret = 0; | ||
159 | u32 buf_size; | ||
160 | struct mwifiex_bssdescriptor *temp_scan_table; | ||
161 | |||
162 | /* Allocate buffer to store the BSSID list */ | ||
163 | buf_size = sizeof(struct mwifiex_bssdescriptor) * IW_MAX_AP; | ||
164 | temp_scan_table = kzalloc(buf_size, GFP_KERNEL); | ||
165 | if (!temp_scan_table) { | ||
166 | dev_err(adapter->dev, "%s: failed to alloc temp_scan_table\n", | ||
167 | __func__); | ||
168 | return -1; | ||
169 | } | ||
170 | |||
171 | adapter->scan_table = temp_scan_table; | ||
172 | |||
173 | /* Allocate command buffer */ | ||
174 | ret = mwifiex_alloc_cmd_buffer(adapter); | ||
175 | if (ret) { | ||
176 | dev_err(adapter->dev, "%s: failed to alloc cmd buffer\n", | ||
177 | __func__); | ||
178 | return -1; | ||
179 | } | ||
180 | |||
181 | adapter->sleep_cfm = | ||
182 | dev_alloc_skb(sizeof(struct mwifiex_opt_sleep_confirm_buffer) | ||
183 | + INTF_HEADER_LEN); | ||
184 | |||
185 | if (!adapter->sleep_cfm) { | ||
186 | dev_err(adapter->dev, "%s: failed to alloc sleep cfm" | ||
187 | " cmd buffer\n", __func__); | ||
188 | return -1; | ||
189 | } | ||
190 | skb_reserve(adapter->sleep_cfm, INTF_HEADER_LEN); | ||
191 | |||
192 | return 0; | ||
193 | } | ||
194 | |||
195 | /* | ||
196 | * This function initializes the adapter structure and sets default | ||
197 | * values to the members of adapter. | ||
198 | * | ||
199 | * This also initializes the WMM related parameters in the driver private | ||
200 | * structures. | ||
201 | */ | ||
202 | static void mwifiex_init_adapter(struct mwifiex_adapter *adapter) | ||
203 | { | ||
204 | struct mwifiex_opt_sleep_confirm_buffer *sleep_cfm_buf = NULL; | ||
205 | |||
206 | skb_put(adapter->sleep_cfm, sizeof(sleep_cfm_buf->ps_cfm_sleep)); | ||
207 | sleep_cfm_buf = (struct mwifiex_opt_sleep_confirm_buffer *) | ||
208 | (adapter->sleep_cfm->data); | ||
209 | |||
210 | adapter->cmd_sent = false; | ||
211 | adapter->data_sent = true; | ||
212 | adapter->cmd_resp_received = false; | ||
213 | adapter->event_received = false; | ||
214 | adapter->data_received = false; | ||
215 | |||
216 | adapter->surprise_removed = false; | ||
217 | |||
218 | adapter->hw_status = MWIFIEX_HW_STATUS_INITIALIZING; | ||
219 | |||
220 | adapter->ps_mode = MWIFIEX_802_11_POWER_MODE_CAM; | ||
221 | adapter->ps_state = PS_STATE_AWAKE; | ||
222 | adapter->need_to_wakeup = false; | ||
223 | |||
224 | adapter->scan_mode = HostCmd_BSS_MODE_ANY; | ||
225 | adapter->specific_scan_time = MWIFIEX_SPECIFIC_SCAN_CHAN_TIME; | ||
226 | adapter->active_scan_time = MWIFIEX_ACTIVE_SCAN_CHAN_TIME; | ||
227 | adapter->passive_scan_time = MWIFIEX_PASSIVE_SCAN_CHAN_TIME; | ||
228 | |||
229 | adapter->num_in_scan_table = 0; | ||
230 | memset(adapter->scan_table, 0, | ||
231 | (sizeof(struct mwifiex_bssdescriptor) * IW_MAX_AP)); | ||
232 | adapter->scan_probes = 1; | ||
233 | |||
234 | memset(adapter->bcn_buf, 0, sizeof(adapter->bcn_buf)); | ||
235 | adapter->bcn_buf_end = adapter->bcn_buf; | ||
236 | |||
237 | adapter->radio_on = RADIO_ON; | ||
238 | adapter->multiple_dtim = 1; | ||
239 | |||
240 | adapter->local_listen_interval = 0; /* default value in firmware | ||
241 | will be used */ | ||
242 | |||
243 | adapter->is_deep_sleep = false; | ||
244 | |||
245 | adapter->delay_null_pkt = false; | ||
246 | adapter->delay_to_ps = 1000; | ||
247 | adapter->enhanced_ps_mode = PS_MODE_AUTO; | ||
248 | |||
249 | adapter->gen_null_pkt = false; /* Disable NULL Pkg generation by | ||
250 | default */ | ||
251 | adapter->pps_uapsd_mode = false; /* Disable pps/uapsd mode by | ||
252 | default */ | ||
253 | adapter->pm_wakeup_card_req = false; | ||
254 | |||
255 | adapter->pm_wakeup_fw_try = false; | ||
256 | |||
257 | adapter->max_tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_2K; | ||
258 | adapter->tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_2K; | ||
259 | adapter->curr_tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_2K; | ||
260 | |||
261 | adapter->is_hs_configured = false; | ||
262 | adapter->hs_cfg.conditions = cpu_to_le32(HOST_SLEEP_CFG_COND_DEF); | ||
263 | adapter->hs_cfg.gpio = HOST_SLEEP_CFG_GPIO_DEF; | ||
264 | adapter->hs_cfg.gap = HOST_SLEEP_CFG_GAP_DEF; | ||
265 | adapter->hs_activated = false; | ||
266 | |||
267 | memset(adapter->event_body, 0, sizeof(adapter->event_body)); | ||
268 | adapter->hw_dot_11n_dev_cap = 0; | ||
269 | adapter->hw_dev_mcs_support = 0; | ||
270 | adapter->usr_dot_11n_dev_cap = 0; | ||
271 | adapter->usr_dev_mcs_support = 0; | ||
272 | adapter->chan_offset = 0; | ||
273 | adapter->adhoc_11n_enabled = false; | ||
274 | |||
275 | mwifiex_wmm_init(adapter); | ||
276 | |||
277 | if (adapter->sleep_cfm) { | ||
278 | memset(&sleep_cfm_buf->ps_cfm_sleep, 0, | ||
279 | adapter->sleep_cfm->len); | ||
280 | sleep_cfm_buf->ps_cfm_sleep.command = | ||
281 | cpu_to_le16(HostCmd_CMD_802_11_PS_MODE_ENH); | ||
282 | sleep_cfm_buf->ps_cfm_sleep.size = | ||
283 | cpu_to_le16(adapter->sleep_cfm->len); | ||
284 | sleep_cfm_buf->ps_cfm_sleep.result = 0; | ||
285 | sleep_cfm_buf->ps_cfm_sleep.action = cpu_to_le16(SLEEP_CONFIRM); | ||
286 | sleep_cfm_buf->ps_cfm_sleep.sleep_cfm.resp_ctrl = | ||
287 | cpu_to_le16(RESP_NEEDED); | ||
288 | } | ||
289 | memset(&adapter->sleep_params, 0, sizeof(adapter->sleep_params)); | ||
290 | memset(&adapter->sleep_period, 0, sizeof(adapter->sleep_period)); | ||
291 | adapter->tx_lock_flag = false; | ||
292 | adapter->null_pkt_interval = 0; | ||
293 | adapter->fw_bands = 0; | ||
294 | adapter->config_bands = 0; | ||
295 | adapter->adhoc_start_band = 0; | ||
296 | adapter->scan_channels = NULL; | ||
297 | adapter->fw_release_number = 0; | ||
298 | adapter->fw_cap_info = 0; | ||
299 | memset(&adapter->upld_buf, 0, sizeof(adapter->upld_buf)); | ||
300 | adapter->event_cause = 0; | ||
301 | adapter->region_code = 0; | ||
302 | adapter->bcn_miss_time_out = DEFAULT_BCN_MISS_TIMEOUT; | ||
303 | adapter->adhoc_awake_period = 0; | ||
304 | memset(&adapter->arp_filter, 0, sizeof(adapter->arp_filter)); | ||
305 | adapter->arp_filter_size = 0; | ||
306 | |||
307 | return; | ||
308 | } | ||
309 | |||
310 | /* | ||
311 | * This function frees the adapter structure. | ||
312 | * | ||
313 | * The freeing operation is done recursively, by canceling all | ||
314 | * pending commands, freeing the member buffers previously | ||
315 | * allocated (command buffers, scan table buffer, sleep confirm | ||
316 | * command buffer), stopping the timers and calling the cleanup | ||
317 | * routines for every interface, before the actual adapter | ||
318 | * structure is freed. | ||
319 | */ | ||
320 | static void | ||
321 | mwifiex_free_adapter(struct mwifiex_adapter *adapter) | ||
322 | { | ||
323 | if (!adapter) { | ||
324 | pr_err("%s: adapter is NULL\n", __func__); | ||
325 | return; | ||
326 | } | ||
327 | |||
328 | mwifiex_cancel_all_pending_cmd(adapter); | ||
329 | |||
330 | /* Free lock variables */ | ||
331 | mwifiex_free_lock_list(adapter); | ||
332 | |||
333 | /* Free command buffer */ | ||
334 | dev_dbg(adapter->dev, "info: free cmd buffer\n"); | ||
335 | mwifiex_free_cmd_buffer(adapter); | ||
336 | |||
337 | del_timer(&adapter->cmd_timer); | ||
338 | |||
339 | dev_dbg(adapter->dev, "info: free scan table\n"); | ||
340 | kfree(adapter->scan_table); | ||
341 | adapter->scan_table = NULL; | ||
342 | |||
343 | adapter->if_ops.cleanup_if(adapter); | ||
344 | |||
345 | dev_kfree_skb_any(adapter->sleep_cfm); | ||
346 | |||
347 | return; | ||
348 | } | ||
349 | |||
350 | /* | ||
351 | * This function intializes the lock variables and | ||
352 | * the list heads. | ||
353 | */ | ||
354 | int mwifiex_init_lock_list(struct mwifiex_adapter *adapter) | ||
355 | { | ||
356 | struct mwifiex_private *priv = NULL; | ||
357 | s32 i = 0; | ||
358 | u32 j = 0; | ||
359 | |||
360 | spin_lock_init(&adapter->mwifiex_lock); | ||
361 | spin_lock_init(&adapter->int_lock); | ||
362 | spin_lock_init(&adapter->main_proc_lock); | ||
363 | spin_lock_init(&adapter->mwifiex_cmd_lock); | ||
364 | for (i = 0; i < adapter->priv_num; i++) { | ||
365 | if (adapter->priv[i]) { | ||
366 | priv = adapter->priv[i]; | ||
367 | spin_lock_init(&priv->rx_pkt_lock); | ||
368 | spin_lock_init(&priv->wmm.ra_list_spinlock); | ||
369 | spin_lock_init(&priv->curr_bcn_buf_lock); | ||
370 | } | ||
371 | } | ||
372 | |||
373 | /* Initialize cmd_free_q */ | ||
374 | INIT_LIST_HEAD(&adapter->cmd_free_q); | ||
375 | /* Initialize cmd_pending_q */ | ||
376 | INIT_LIST_HEAD(&adapter->cmd_pending_q); | ||
377 | /* Initialize scan_pending_q */ | ||
378 | INIT_LIST_HEAD(&adapter->scan_pending_q); | ||
379 | |||
380 | spin_lock_init(&adapter->cmd_free_q_lock); | ||
381 | spin_lock_init(&adapter->cmd_pending_q_lock); | ||
382 | spin_lock_init(&adapter->scan_pending_q_lock); | ||
383 | |||
384 | for (i = 0; i < adapter->priv_num; ++i) { | ||
385 | INIT_LIST_HEAD(&adapter->bss_prio_tbl[i].bss_prio_head); | ||
386 | adapter->bss_prio_tbl[i].bss_prio_cur = NULL; | ||
387 | spin_lock_init(&adapter->bss_prio_tbl[i].bss_prio_lock); | ||
388 | } | ||
389 | |||
390 | for (i = 0; i < adapter->priv_num; i++) { | ||
391 | if (!adapter->priv[i]) | ||
392 | continue; | ||
393 | priv = adapter->priv[i]; | ||
394 | for (j = 0; j < MAX_NUM_TID; ++j) { | ||
395 | INIT_LIST_HEAD(&priv->wmm.tid_tbl_ptr[j].ra_list); | ||
396 | spin_lock_init(&priv->wmm.tid_tbl_ptr[j].tid_tbl_lock); | ||
397 | } | ||
398 | INIT_LIST_HEAD(&priv->tx_ba_stream_tbl_ptr); | ||
399 | INIT_LIST_HEAD(&priv->rx_reorder_tbl_ptr); | ||
400 | |||
401 | spin_lock_init(&priv->tx_ba_stream_tbl_lock); | ||
402 | spin_lock_init(&priv->rx_reorder_tbl_lock); | ||
403 | } | ||
404 | |||
405 | return 0; | ||
406 | } | ||
407 | |||
408 | /* | ||
409 | * This function releases the lock variables and frees the locks and | ||
410 | * associated locks. | ||
411 | */ | ||
412 | void mwifiex_free_lock_list(struct mwifiex_adapter *adapter) | ||
413 | { | ||
414 | struct mwifiex_private *priv = NULL; | ||
415 | s32 i = 0; | ||
416 | s32 j = 0; | ||
417 | |||
418 | /* Free lists */ | ||
419 | list_del(&adapter->cmd_free_q); | ||
420 | list_del(&adapter->cmd_pending_q); | ||
421 | list_del(&adapter->scan_pending_q); | ||
422 | |||
423 | for (i = 0; i < adapter->priv_num; i++) | ||
424 | list_del(&adapter->bss_prio_tbl[i].bss_prio_head); | ||
425 | |||
426 | for (i = 0; i < adapter->priv_num; i++) { | ||
427 | if (adapter->priv[i]) { | ||
428 | priv = adapter->priv[i]; | ||
429 | for (j = 0; j < MAX_NUM_TID; ++j) | ||
430 | list_del(&priv->wmm.tid_tbl_ptr[j].ra_list); | ||
431 | list_del(&priv->tx_ba_stream_tbl_ptr); | ||
432 | list_del(&priv->rx_reorder_tbl_ptr); | ||
433 | } | ||
434 | } | ||
435 | |||
436 | return; | ||
437 | } | ||
438 | |||
439 | /* | ||
440 | * This function initializes the firmware. | ||
441 | * | ||
442 | * The following operations are performed sequentially - | ||
443 | * - Allocate adapter structure | ||
444 | * - Initialize the adapter structure | ||
445 | * - Initialize the private structure | ||
446 | * - Add BSS priority tables to the adapter structure | ||
447 | * - For each interface, send the init commands to firmware | ||
448 | * - Send the first command in command pending queue, if available | ||
449 | */ | ||
450 | int mwifiex_init_fw(struct mwifiex_adapter *adapter) | ||
451 | { | ||
452 | int ret = 0; | ||
453 | struct mwifiex_private *priv = NULL; | ||
454 | u8 i = 0; | ||
455 | u8 first_sta = true; | ||
456 | int is_cmd_pend_q_empty; | ||
457 | unsigned long flags; | ||
458 | |||
459 | adapter->hw_status = MWIFIEX_HW_STATUS_INITIALIZING; | ||
460 | |||
461 | /* Allocate memory for member of adapter structure */ | ||
462 | ret = mwifiex_allocate_adapter(adapter); | ||
463 | if (ret) | ||
464 | return -1; | ||
465 | |||
466 | /* Initialize adapter structure */ | ||
467 | mwifiex_init_adapter(adapter); | ||
468 | |||
469 | for (i = 0; i < adapter->priv_num; i++) { | ||
470 | if (adapter->priv[i]) { | ||
471 | priv = adapter->priv[i]; | ||
472 | |||
473 | /* Initialize private structure */ | ||
474 | ret = mwifiex_init_priv(priv); | ||
475 | if (ret) | ||
476 | return -1; | ||
477 | } | ||
478 | } | ||
479 | for (i = 0; i < adapter->priv_num; i++) { | ||
480 | if (adapter->priv[i]) { | ||
481 | ret = mwifiex_sta_init_cmd(adapter->priv[i], first_sta); | ||
482 | if (ret == -1) | ||
483 | return -1; | ||
484 | |||
485 | first_sta = false; | ||
486 | } | ||
487 | } | ||
488 | |||
489 | spin_lock_irqsave(&adapter->cmd_pending_q_lock, flags); | ||
490 | is_cmd_pend_q_empty = list_empty(&adapter->cmd_pending_q); | ||
491 | spin_unlock_irqrestore(&adapter->cmd_pending_q_lock, flags); | ||
492 | if (!is_cmd_pend_q_empty) { | ||
493 | /* Send the first command in queue and return */ | ||
494 | if (mwifiex_main_process(adapter) != -1) | ||
495 | ret = -EINPROGRESS; | ||
496 | } else { | ||
497 | adapter->hw_status = MWIFIEX_HW_STATUS_READY; | ||
498 | } | ||
499 | |||
500 | return ret; | ||
501 | } | ||
502 | |||
503 | /* | ||
504 | * This function deletes the BSS priority tables. | ||
505 | * | ||
506 | * The function traverses through all the allocated BSS priority nodes | ||
507 | * in every BSS priority table and frees them. | ||
508 | */ | ||
509 | static void mwifiex_delete_bss_prio_tbl(struct mwifiex_private *priv) | ||
510 | { | ||
511 | int i; | ||
512 | struct mwifiex_adapter *adapter = priv->adapter; | ||
513 | struct mwifiex_bss_prio_node *bssprio_node = NULL, *tmp_node = NULL, | ||
514 | **cur = NULL; | ||
515 | struct list_head *head; | ||
516 | spinlock_t *lock; | ||
517 | unsigned long flags; | ||
518 | |||
519 | for (i = 0; i < adapter->priv_num; ++i) { | ||
520 | head = &adapter->bss_prio_tbl[i].bss_prio_head; | ||
521 | cur = &adapter->bss_prio_tbl[i].bss_prio_cur; | ||
522 | lock = &adapter->bss_prio_tbl[i].bss_prio_lock; | ||
523 | dev_dbg(adapter->dev, "info: delete BSS priority table," | ||
524 | " index = %d, i = %d, head = %p, cur = %p\n", | ||
525 | priv->bss_index, i, head, *cur); | ||
526 | if (*cur) { | ||
527 | spin_lock_irqsave(lock, flags); | ||
528 | if (list_empty(head)) { | ||
529 | spin_unlock_irqrestore(lock, flags); | ||
530 | continue; | ||
531 | } | ||
532 | bssprio_node = list_first_entry(head, | ||
533 | struct mwifiex_bss_prio_node, list); | ||
534 | spin_unlock_irqrestore(lock, flags); | ||
535 | |||
536 | list_for_each_entry_safe(bssprio_node, tmp_node, head, | ||
537 | list) { | ||
538 | if (bssprio_node->priv == priv) { | ||
539 | dev_dbg(adapter->dev, "info: Delete " | ||
540 | "node %p, next = %p\n", | ||
541 | bssprio_node, tmp_node); | ||
542 | spin_lock_irqsave(lock, flags); | ||
543 | list_del(&bssprio_node->list); | ||
544 | spin_unlock_irqrestore(lock, flags); | ||
545 | kfree(bssprio_node); | ||
546 | } | ||
547 | } | ||
548 | *cur = (struct mwifiex_bss_prio_node *)head; | ||
549 | } | ||
550 | } | ||
551 | } | ||
552 | |||
553 | /* | ||
554 | * This function is used to shutdown the driver. | ||
555 | * | ||
556 | * The following operations are performed sequentially - | ||
557 | * - Check if already shut down | ||
558 | * - Make sure the main process has stopped | ||
559 | * - Clean up the Tx and Rx queues | ||
560 | * - Delete BSS priority tables | ||
561 | * - Free the adapter | ||
562 | * - Notify completion | ||
563 | */ | ||
564 | int | ||
565 | mwifiex_shutdown_drv(struct mwifiex_adapter *adapter) | ||
566 | { | ||
567 | int ret = -EINPROGRESS; | ||
568 | struct mwifiex_private *priv = NULL; | ||
569 | s32 i = 0; | ||
570 | unsigned long flags; | ||
571 | |||
572 | /* mwifiex already shutdown */ | ||
573 | if (adapter->hw_status == MWIFIEX_HW_STATUS_NOT_READY) | ||
574 | return 0; | ||
575 | |||
576 | adapter->hw_status = MWIFIEX_HW_STATUS_CLOSING; | ||
577 | /* wait for mwifiex_process to complete */ | ||
578 | if (adapter->mwifiex_processing) { | ||
579 | dev_warn(adapter->dev, "main process is still running\n"); | ||
580 | return ret; | ||
581 | } | ||
582 | |||
583 | /* shut down mwifiex */ | ||
584 | dev_dbg(adapter->dev, "info: shutdown mwifiex...\n"); | ||
585 | |||
586 | /* Clean up Tx/Rx queues and delete BSS priority table */ | ||
587 | for (i = 0; i < adapter->priv_num; i++) { | ||
588 | if (adapter->priv[i]) { | ||
589 | priv = adapter->priv[i]; | ||
590 | |||
591 | mwifiex_clean_txrx(priv); | ||
592 | mwifiex_delete_bss_prio_tbl(priv); | ||
593 | } | ||
594 | } | ||
595 | |||
596 | spin_lock_irqsave(&adapter->mwifiex_lock, flags); | ||
597 | |||
598 | /* Free adapter structure */ | ||
599 | mwifiex_free_adapter(adapter); | ||
600 | |||
601 | spin_unlock_irqrestore(&adapter->mwifiex_lock, flags); | ||
602 | |||
603 | /* Notify completion */ | ||
604 | ret = mwifiex_shutdown_fw_complete(adapter); | ||
605 | |||
606 | return ret; | ||
607 | } | ||
608 | |||
609 | /* | ||
610 | * This function downloads the firmware to the card. | ||
611 | * | ||
612 | * The actual download is preceded by two sanity checks - | ||
613 | * - Check if firmware is already running | ||
614 | * - Check if the interface is the winner to download the firmware | ||
615 | * | ||
616 | * ...and followed by another - | ||
617 | * - Check if the firmware is downloaded successfully | ||
618 | * | ||
619 | * After download is successfully completed, the host interrupts are enabled. | ||
620 | */ | ||
621 | int mwifiex_dnld_fw(struct mwifiex_adapter *adapter, | ||
622 | struct mwifiex_fw_image *pmfw) | ||
623 | { | ||
624 | int ret = 0; | ||
625 | u32 poll_num = 1; | ||
626 | int winner; | ||
627 | |||
628 | /* Check if firmware is already running */ | ||
629 | ret = adapter->if_ops.check_fw_status(adapter, poll_num, &winner); | ||
630 | if (!ret) { | ||
631 | dev_notice(adapter->dev, | ||
632 | "WLAN FW already running! Skip FW download\n"); | ||
633 | goto done; | ||
634 | } | ||
635 | poll_num = MAX_FIRMWARE_POLL_TRIES; | ||
636 | |||
637 | /* Check if we are the winner for downloading FW */ | ||
638 | if (!winner) { | ||
639 | dev_notice(adapter->dev, | ||
640 | "Other interface already running!" | ||
641 | " Skip FW download\n"); | ||
642 | poll_num = MAX_MULTI_INTERFACE_POLL_TRIES; | ||
643 | goto poll_fw; | ||
644 | } | ||
645 | if (pmfw) { | ||
646 | /* Download firmware with helper */ | ||
647 | ret = adapter->if_ops.prog_fw(adapter, pmfw); | ||
648 | if (ret) { | ||
649 | dev_err(adapter->dev, "prog_fw failed ret=%#x\n", ret); | ||
650 | return ret; | ||
651 | } | ||
652 | } | ||
653 | |||
654 | poll_fw: | ||
655 | /* Check if the firmware is downloaded successfully or not */ | ||
656 | ret = adapter->if_ops.check_fw_status(adapter, poll_num, NULL); | ||
657 | if (ret) { | ||
658 | dev_err(adapter->dev, "FW failed to be active in time\n"); | ||
659 | return -1; | ||
660 | } | ||
661 | done: | ||
662 | /* re-enable host interrupt for mwifiex after fw dnld is successful */ | ||
663 | adapter->if_ops.enable_int(adapter); | ||
664 | return ret; | ||
665 | } | ||
diff --git a/drivers/net/wireless/mwifiex/ioctl.h b/drivers/net/wireless/mwifiex/ioctl.h new file mode 100644 index 000000000000..d6babfb1495c --- /dev/null +++ b/drivers/net/wireless/mwifiex/ioctl.h | |||
@@ -0,0 +1,433 @@ | |||
1 | /* | ||
2 | * Marvell Wireless LAN device driver: ioctl data structures & APIs | ||
3 | * | ||
4 | * Copyright (C) 2011, Marvell International Ltd. | ||
5 | * | ||
6 | * This software file (the "File") is distributed by Marvell International | ||
7 | * Ltd. under the terms of the GNU General Public License Version 2, June 1991 | ||
8 | * (the "License"). You may use, redistribute and/or modify this File in | ||
9 | * accordance with the terms and conditions of the License, a copy of which | ||
10 | * is available by writing to the Free Software Foundation, Inc., | ||
11 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the | ||
12 | * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. | ||
13 | * | ||
14 | * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE | ||
15 | * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE | ||
16 | * ARE EXPRESSLY DISCLAIMED. The License provides additional details about | ||
17 | * this warranty disclaimer. | ||
18 | */ | ||
19 | |||
20 | #ifndef _MWIFIEX_IOCTL_H_ | ||
21 | #define _MWIFIEX_IOCTL_H_ | ||
22 | |||
23 | #include <net/mac80211.h> | ||
24 | |||
25 | enum { | ||
26 | MWIFIEX_SCAN_MODE_UNCHANGED = 0, | ||
27 | MWIFIEX_SCAN_MODE_BSS, | ||
28 | MWIFIEX_SCAN_MODE_IBSS, | ||
29 | MWIFIEX_SCAN_MODE_ANY | ||
30 | }; | ||
31 | |||
32 | enum { | ||
33 | MWIFIEX_SCAN_TYPE_UNCHANGED = 0, | ||
34 | MWIFIEX_SCAN_TYPE_ACTIVE, | ||
35 | MWIFIEX_SCAN_TYPE_PASSIVE | ||
36 | }; | ||
37 | |||
38 | struct mwifiex_get_scan_table_fixed { | ||
39 | u8 bssid[ETH_ALEN]; | ||
40 | u8 channel; | ||
41 | u8 rssi; | ||
42 | long long network_tsf; | ||
43 | }; | ||
44 | |||
45 | struct mwifiex_scan_time_params { | ||
46 | u32 specific_scan_time; | ||
47 | u32 active_scan_time; | ||
48 | u32 passive_scan_time; | ||
49 | }; | ||
50 | |||
51 | struct mwifiex_user_scan { | ||
52 | u32 scan_cfg_len; | ||
53 | u8 scan_cfg_buf[1]; | ||
54 | }; | ||
55 | |||
56 | struct mwifiex_scan_req { | ||
57 | u32 scan_mode; | ||
58 | u32 scan_type; | ||
59 | struct mwifiex_802_11_ssid scan_ssid; | ||
60 | struct mwifiex_scan_time_params scan_time; | ||
61 | struct mwifiex_user_scan user_scan; | ||
62 | }; | ||
63 | |||
64 | struct mwifiex_scan_resp { | ||
65 | u32 num_in_scan_table; | ||
66 | u8 *scan_table; | ||
67 | }; | ||
68 | |||
69 | enum { | ||
70 | MWIFIEX_BSS_MODE_INFRA = 1, | ||
71 | MWIFIEX_BSS_MODE_IBSS, | ||
72 | MWIFIEX_BSS_MODE_AUTO | ||
73 | }; | ||
74 | |||
75 | #define MWIFIEX_PROMISC_MODE 1 | ||
76 | #define MWIFIEX_MULTICAST_MODE 2 | ||
77 | #define MWIFIEX_ALL_MULTI_MODE 4 | ||
78 | #define MWIFIEX_MAX_MULTICAST_LIST_SIZE 32 | ||
79 | |||
80 | struct mwifiex_multicast_list { | ||
81 | u32 mode; | ||
82 | u32 num_multicast_addr; | ||
83 | u8 mac_list[MWIFIEX_MAX_MULTICAST_LIST_SIZE][ETH_ALEN]; | ||
84 | }; | ||
85 | |||
86 | #define MWIFIEX_MAX_CHANNEL_NUM 128 | ||
87 | |||
88 | struct mwifiex_chan_freq { | ||
89 | u32 channel; | ||
90 | u32 freq; | ||
91 | }; | ||
92 | |||
93 | struct mwifiex_chan_list { | ||
94 | u32 num_of_chan; | ||
95 | struct mwifiex_chan_freq cf[MWIFIEX_MAX_CHANNEL_NUM]; | ||
96 | }; | ||
97 | |||
98 | struct mwifiex_ssid_bssid { | ||
99 | struct mwifiex_802_11_ssid ssid; | ||
100 | u8 bssid[ETH_ALEN]; | ||
101 | }; | ||
102 | |||
103 | enum { | ||
104 | BAND_B = 1, | ||
105 | BAND_G = 2, | ||
106 | BAND_A = 4, | ||
107 | BAND_GN = 8, | ||
108 | BAND_AN = 16, | ||
109 | }; | ||
110 | |||
111 | #define NO_SEC_CHANNEL 0 | ||
112 | #define SEC_CHANNEL_ABOVE 1 | ||
113 | #define SEC_CHANNEL_BELOW 3 | ||
114 | |||
115 | struct mwifiex_ds_band_cfg { | ||
116 | u32 config_bands; | ||
117 | u32 adhoc_start_band; | ||
118 | u32 adhoc_channel; | ||
119 | u32 sec_chan_offset; | ||
120 | }; | ||
121 | |||
122 | enum { | ||
123 | ADHOC_IDLE, | ||
124 | ADHOC_STARTED, | ||
125 | ADHOC_JOINED, | ||
126 | ADHOC_COALESCED | ||
127 | }; | ||
128 | |||
129 | struct mwifiex_ds_get_stats { | ||
130 | u32 mcast_tx_frame; | ||
131 | u32 failed; | ||
132 | u32 retry; | ||
133 | u32 multi_retry; | ||
134 | u32 frame_dup; | ||
135 | u32 rts_success; | ||
136 | u32 rts_failure; | ||
137 | u32 ack_failure; | ||
138 | u32 rx_frag; | ||
139 | u32 mcast_rx_frame; | ||
140 | u32 fcs_error; | ||
141 | u32 tx_frame; | ||
142 | u32 wep_icv_error[4]; | ||
143 | }; | ||
144 | |||
145 | #define BCN_RSSI_LAST_MASK 0x00000001 | ||
146 | #define BCN_RSSI_AVG_MASK 0x00000002 | ||
147 | #define DATA_RSSI_LAST_MASK 0x00000004 | ||
148 | #define DATA_RSSI_AVG_MASK 0x00000008 | ||
149 | #define BCN_SNR_LAST_MASK 0x00000010 | ||
150 | #define BCN_SNR_AVG_MASK 0x00000020 | ||
151 | #define DATA_SNR_LAST_MASK 0x00000040 | ||
152 | #define DATA_SNR_AVG_MASK 0x00000080 | ||
153 | #define BCN_NF_LAST_MASK 0x00000100 | ||
154 | #define BCN_NF_AVG_MASK 0x00000200 | ||
155 | #define DATA_NF_LAST_MASK 0x00000400 | ||
156 | #define DATA_NF_AVG_MASK 0x00000800 | ||
157 | #define ALL_RSSI_INFO_MASK 0x00000fff | ||
158 | |||
159 | struct mwifiex_ds_get_signal { | ||
160 | /* | ||
161 | * Bit0: Last Beacon RSSI, Bit1: Average Beacon RSSI, | ||
162 | * Bit2: Last Data RSSI, Bit3: Average Data RSSI, | ||
163 | * Bit4: Last Beacon SNR, Bit5: Average Beacon SNR, | ||
164 | * Bit6: Last Data SNR, Bit7: Average Data SNR, | ||
165 | * Bit8: Last Beacon NF, Bit9: Average Beacon NF, | ||
166 | * Bit10: Last Data NF, Bit11: Average Data NF | ||
167 | */ | ||
168 | u16 selector; | ||
169 | s16 bcn_rssi_last; | ||
170 | s16 bcn_rssi_avg; | ||
171 | s16 data_rssi_last; | ||
172 | s16 data_rssi_avg; | ||
173 | s16 bcn_snr_last; | ||
174 | s16 bcn_snr_avg; | ||
175 | s16 data_snr_last; | ||
176 | s16 data_snr_avg; | ||
177 | s16 bcn_nf_last; | ||
178 | s16 bcn_nf_avg; | ||
179 | s16 data_nf_last; | ||
180 | s16 data_nf_avg; | ||
181 | }; | ||
182 | |||
183 | struct mwifiex_fw_info { | ||
184 | u32 fw_ver; | ||
185 | u8 mac_addr[ETH_ALEN]; | ||
186 | }; | ||
187 | |||
188 | #define MWIFIEX_MAX_VER_STR_LEN 128 | ||
189 | |||
190 | struct mwifiex_ver_ext { | ||
191 | u32 version_str_sel; | ||
192 | char version_str[MWIFIEX_MAX_VER_STR_LEN]; | ||
193 | }; | ||
194 | |||
195 | struct mwifiex_bss_info { | ||
196 | u32 bss_mode; | ||
197 | struct mwifiex_802_11_ssid ssid; | ||
198 | u32 scan_table_idx; | ||
199 | u32 bss_chan; | ||
200 | u32 region_code; | ||
201 | u32 media_connected; | ||
202 | u32 radio_on; | ||
203 | u32 max_power_level; | ||
204 | u32 min_power_level; | ||
205 | u32 adhoc_state; | ||
206 | signed int bcn_nf_last; | ||
207 | u32 wep_status; | ||
208 | u32 is_hs_configured; | ||
209 | u32 is_deep_sleep; | ||
210 | u8 bssid[ETH_ALEN]; | ||
211 | }; | ||
212 | |||
213 | #define MAX_NUM_TID 8 | ||
214 | |||
215 | #define MAX_RX_WINSIZE 64 | ||
216 | |||
217 | struct mwifiex_ds_rx_reorder_tbl { | ||
218 | u16 tid; | ||
219 | u8 ta[ETH_ALEN]; | ||
220 | u32 start_win; | ||
221 | u32 win_size; | ||
222 | u32 buffer[MAX_RX_WINSIZE]; | ||
223 | }; | ||
224 | |||
225 | struct mwifiex_ds_tx_ba_stream_tbl { | ||
226 | u16 tid; | ||
227 | u8 ra[ETH_ALEN]; | ||
228 | }; | ||
229 | |||
230 | #define DBG_CMD_NUM 5 | ||
231 | |||
232 | struct mwifiex_debug_info { | ||
233 | u32 int_counter; | ||
234 | u32 packets_out[MAX_NUM_TID]; | ||
235 | u32 max_tx_buf_size; | ||
236 | u32 tx_buf_size; | ||
237 | u32 curr_tx_buf_size; | ||
238 | u32 tx_tbl_num; | ||
239 | struct mwifiex_ds_tx_ba_stream_tbl | ||
240 | tx_tbl[MWIFIEX_MAX_TX_BASTREAM_SUPPORTED]; | ||
241 | u32 rx_tbl_num; | ||
242 | struct mwifiex_ds_rx_reorder_tbl rx_tbl | ||
243 | [MWIFIEX_MAX_RX_BASTREAM_SUPPORTED]; | ||
244 | u16 ps_mode; | ||
245 | u32 ps_state; | ||
246 | u8 is_deep_sleep; | ||
247 | u8 pm_wakeup_card_req; | ||
248 | u32 pm_wakeup_fw_try; | ||
249 | u8 is_hs_configured; | ||
250 | u8 hs_activated; | ||
251 | u32 num_cmd_host_to_card_failure; | ||
252 | u32 num_cmd_sleep_cfm_host_to_card_failure; | ||
253 | u32 num_tx_host_to_card_failure; | ||
254 | u32 num_event_deauth; | ||
255 | u32 num_event_disassoc; | ||
256 | u32 num_event_link_lost; | ||
257 | u32 num_cmd_deauth; | ||
258 | u32 num_cmd_assoc_success; | ||
259 | u32 num_cmd_assoc_failure; | ||
260 | u32 num_tx_timeout; | ||
261 | u32 num_cmd_timeout; | ||
262 | u16 timeout_cmd_id; | ||
263 | u16 timeout_cmd_act; | ||
264 | u16 last_cmd_id[DBG_CMD_NUM]; | ||
265 | u16 last_cmd_act[DBG_CMD_NUM]; | ||
266 | u16 last_cmd_index; | ||
267 | u16 last_cmd_resp_id[DBG_CMD_NUM]; | ||
268 | u16 last_cmd_resp_index; | ||
269 | u16 last_event[DBG_CMD_NUM]; | ||
270 | u16 last_event_index; | ||
271 | u8 data_sent; | ||
272 | u8 cmd_sent; | ||
273 | u8 cmd_resp_received; | ||
274 | u8 event_received; | ||
275 | }; | ||
276 | |||
277 | enum { | ||
278 | MWIFIEX_AUTH_MODE_OPEN = 0x00, | ||
279 | MWIFIEX_AUTH_MODE_SHARED = 0x01, | ||
280 | MWIFIEX_AUTH_MODE_NETWORKEAP = 0x80, | ||
281 | MWIFIEX_AUTH_MODE_AUTO = 0xFF, | ||
282 | }; | ||
283 | |||
284 | enum { | ||
285 | MWIFIEX_ENCRYPTION_MODE_NONE = 0, | ||
286 | MWIFIEX_ENCRYPTION_MODE_WEP40 = 1, | ||
287 | MWIFIEX_ENCRYPTION_MODE_TKIP = 2, | ||
288 | MWIFIEX_ENCRYPTION_MODE_CCMP = 3, | ||
289 | MWIFIEX_ENCRYPTION_MODE_WEP104 = 4, | ||
290 | }; | ||
291 | |||
292 | #define MWIFIEX_KEY_INDEX_UNICAST 0x40000000 | ||
293 | #define MWIFIEX_MAX_KEY_LENGTH 32 | ||
294 | #define WAPI_RXPN_LEN 16 | ||
295 | |||
296 | struct mwifiex_ds_encrypt_key { | ||
297 | u32 key_disable; | ||
298 | u32 key_index; | ||
299 | u32 key_len; | ||
300 | u8 key_material[MWIFIEX_MAX_KEY_LENGTH]; | ||
301 | u8 mac_addr[ETH_ALEN]; | ||
302 | u32 is_wapi_key; | ||
303 | u8 wapi_rxpn[WAPI_RXPN_LEN]; | ||
304 | }; | ||
305 | |||
306 | struct mwifiex_rate_cfg { | ||
307 | u32 action; | ||
308 | u32 is_rate_auto; | ||
309 | u32 rate; | ||
310 | }; | ||
311 | |||
312 | struct mwifiex_data_rate { | ||
313 | u32 tx_data_rate; | ||
314 | u32 rx_data_rate; | ||
315 | }; | ||
316 | |||
317 | struct mwifiex_power_cfg { | ||
318 | u32 is_power_auto; | ||
319 | u32 power_level; | ||
320 | }; | ||
321 | |||
322 | struct mwifiex_ds_hs_cfg { | ||
323 | u32 is_invoke_hostcmd; | ||
324 | /* Bit0: non-unicast data | ||
325 | * Bit1: unicast data | ||
326 | * Bit2: mac events | ||
327 | * Bit3: magic packet | ||
328 | */ | ||
329 | u32 conditions; | ||
330 | u32 gpio; | ||
331 | u32 gap; | ||
332 | }; | ||
333 | |||
334 | #define DEEP_SLEEP_ON 1 | ||
335 | #define DEEP_SLEEP_OFF 0 | ||
336 | |||
337 | #define DEEP_SLEEP_IDLE_TIME 100 | ||
338 | |||
339 | struct mwifiex_ds_auto_ds { | ||
340 | u16 auto_ds; | ||
341 | u16 idle_time; | ||
342 | }; | ||
343 | |||
344 | #define PS_MODE_UNCHANGED 0 | ||
345 | #define PS_MODE_AUTO 1 | ||
346 | #define PS_MODE_POLL 2 | ||
347 | #define PS_MODE_NULL 3 | ||
348 | |||
349 | |||
350 | struct mwifiex_ds_pm_cfg { | ||
351 | union { | ||
352 | u32 ps_mode; | ||
353 | struct mwifiex_ds_hs_cfg hs_cfg; | ||
354 | struct mwifiex_ds_auto_ds auto_deep_sleep; | ||
355 | u32 sleep_period; | ||
356 | } param; | ||
357 | }; | ||
358 | |||
359 | struct mwifiex_ioctl_wmm_queue_status_ac { | ||
360 | u8 wmm_acm; | ||
361 | u8 flow_required; | ||
362 | u8 flow_created; | ||
363 | u8 disabled; | ||
364 | }; | ||
365 | |||
366 | struct mwifiex_ds_wmm_queue_status { | ||
367 | struct mwifiex_ioctl_wmm_queue_status_ac | ||
368 | ac_status[IEEE80211_MAX_QUEUES]; | ||
369 | }; | ||
370 | |||
371 | struct mwifiex_ds_11n_tx_cfg { | ||
372 | u16 tx_htcap; | ||
373 | u16 tx_htinfo; | ||
374 | }; | ||
375 | |||
376 | struct mwifiex_ds_11n_amsdu_aggr_ctrl { | ||
377 | u16 enable; | ||
378 | u16 curr_buf_size; | ||
379 | }; | ||
380 | |||
381 | #define MWIFIEX_NUM_OF_CMD_BUFFER 20 | ||
382 | #define MWIFIEX_SIZE_OF_CMD_BUFFER 2048 | ||
383 | |||
384 | enum { | ||
385 | MWIFIEX_IE_TYPE_GEN_IE = 0, | ||
386 | MWIFIEX_IE_TYPE_ARP_FILTER, | ||
387 | }; | ||
388 | |||
389 | enum { | ||
390 | MWIFIEX_REG_MAC = 1, | ||
391 | MWIFIEX_REG_BBP, | ||
392 | MWIFIEX_REG_RF, | ||
393 | MWIFIEX_REG_PMIC, | ||
394 | MWIFIEX_REG_CAU, | ||
395 | }; | ||
396 | |||
397 | struct mwifiex_ds_reg_rw { | ||
398 | __le32 type; | ||
399 | __le32 offset; | ||
400 | __le32 value; | ||
401 | }; | ||
402 | |||
403 | #define MAX_EEPROM_DATA 256 | ||
404 | |||
405 | struct mwifiex_ds_read_eeprom { | ||
406 | __le16 offset; | ||
407 | __le16 byte_count; | ||
408 | u8 value[MAX_EEPROM_DATA]; | ||
409 | }; | ||
410 | |||
411 | struct mwifiex_ds_misc_gen_ie { | ||
412 | u32 type; | ||
413 | u32 len; | ||
414 | u8 ie_data[IW_CUSTOM_MAX]; | ||
415 | }; | ||
416 | |||
417 | struct mwifiex_ds_misc_cmd { | ||
418 | u32 len; | ||
419 | u8 cmd[MWIFIEX_SIZE_OF_CMD_BUFFER]; | ||
420 | }; | ||
421 | |||
422 | #define MWIFIEX_MAX_VSIE_LEN (256) | ||
423 | #define MWIFIEX_MAX_VSIE_NUM (8) | ||
424 | #define MWIFIEX_VSIE_MASK_SCAN 0x01 | ||
425 | #define MWIFIEX_VSIE_MASK_ASSOC 0x02 | ||
426 | #define MWIFIEX_VSIE_MASK_ADHOC 0x04 | ||
427 | |||
428 | enum { | ||
429 | MWIFIEX_FUNC_INIT = 1, | ||
430 | MWIFIEX_FUNC_SHUTDOWN, | ||
431 | }; | ||
432 | |||
433 | #endif /* !_MWIFIEX_IOCTL_H_ */ | ||
diff --git a/drivers/net/wireless/mwifiex/join.c b/drivers/net/wireless/mwifiex/join.c new file mode 100644 index 000000000000..d06f4c2d1d30 --- /dev/null +++ b/drivers/net/wireless/mwifiex/join.c | |||
@@ -0,0 +1,1464 @@ | |||
1 | /* | ||
2 | * Marvell Wireless LAN device driver: association and ad-hoc start/join | ||
3 | * | ||
4 | * Copyright (C) 2011, Marvell International Ltd. | ||
5 | * | ||
6 | * This software file (the "File") is distributed by Marvell International | ||
7 | * Ltd. under the terms of the GNU General Public License Version 2, June 1991 | ||
8 | * (the "License"). You may use, redistribute and/or modify this File in | ||
9 | * accordance with the terms and conditions of the License, a copy of which | ||
10 | * is available by writing to the Free Software Foundation, Inc., | ||
11 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the | ||
12 | * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. | ||
13 | * | ||
14 | * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE | ||
15 | * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE | ||
16 | * ARE EXPRESSLY DISCLAIMED. The License provides additional details about | ||
17 | * this warranty disclaimer. | ||
18 | */ | ||
19 | |||
20 | #include "decl.h" | ||
21 | #include "ioctl.h" | ||
22 | #include "util.h" | ||
23 | #include "fw.h" | ||
24 | #include "main.h" | ||
25 | #include "wmm.h" | ||
26 | #include "11n.h" | ||
27 | |||
28 | #define CAPINFO_MASK (~(BIT(15) | BIT(14) | BIT(12) | BIT(11) | BIT(9))) | ||
29 | |||
30 | /* | ||
31 | * Append a generic IE as a pass through TLV to a TLV buffer. | ||
32 | * | ||
33 | * This function is called from the network join command preparation routine. | ||
34 | * | ||
35 | * If the IE buffer has been setup by the application, this routine appends | ||
36 | * the buffer as a pass through TLV type to the request. | ||
37 | */ | ||
38 | static int | ||
39 | mwifiex_cmd_append_generic_ie(struct mwifiex_private *priv, u8 **buffer) | ||
40 | { | ||
41 | int ret_len = 0; | ||
42 | struct mwifiex_ie_types_header ie_header; | ||
43 | |||
44 | /* Null Checks */ | ||
45 | if (!buffer) | ||
46 | return 0; | ||
47 | if (!(*buffer)) | ||
48 | return 0; | ||
49 | |||
50 | /* | ||
51 | * If there is a generic ie buffer setup, append it to the return | ||
52 | * parameter buffer pointer. | ||
53 | */ | ||
54 | if (priv->gen_ie_buf_len) { | ||
55 | dev_dbg(priv->adapter->dev, "info: %s: append generic %d to %p\n", | ||
56 | __func__, priv->gen_ie_buf_len, *buffer); | ||
57 | |||
58 | /* Wrap the generic IE buffer with a pass through TLV type */ | ||
59 | ie_header.type = cpu_to_le16(TLV_TYPE_PASSTHROUGH); | ||
60 | ie_header.len = cpu_to_le16(priv->gen_ie_buf_len); | ||
61 | memcpy(*buffer, &ie_header, sizeof(ie_header)); | ||
62 | |||
63 | /* Increment the return size and the return buffer pointer | ||
64 | param */ | ||
65 | *buffer += sizeof(ie_header); | ||
66 | ret_len += sizeof(ie_header); | ||
67 | |||
68 | /* Copy the generic IE buffer to the output buffer, advance | ||
69 | pointer */ | ||
70 | memcpy(*buffer, priv->gen_ie_buf, priv->gen_ie_buf_len); | ||
71 | |||
72 | /* Increment the return size and the return buffer pointer | ||
73 | param */ | ||
74 | *buffer += priv->gen_ie_buf_len; | ||
75 | ret_len += priv->gen_ie_buf_len; | ||
76 | |||
77 | /* Reset the generic IE buffer */ | ||
78 | priv->gen_ie_buf_len = 0; | ||
79 | } | ||
80 | |||
81 | /* return the length appended to the buffer */ | ||
82 | return ret_len; | ||
83 | } | ||
84 | |||
85 | /* | ||
86 | * Append TSF tracking info from the scan table for the target AP. | ||
87 | * | ||
88 | * This function is called from the network join command preparation routine. | ||
89 | * | ||
90 | * The TSF table TSF sent to the firmware contains two TSF values: | ||
91 | * - The TSF of the target AP from its previous beacon/probe response | ||
92 | * - The TSF timestamp of our local MAC at the time we observed the | ||
93 | * beacon/probe response. | ||
94 | * | ||
95 | * The firmware uses the timestamp values to set an initial TSF value | ||
96 | * in the MAC for the new association after a reassociation attempt. | ||
97 | */ | ||
98 | static int | ||
99 | mwifiex_cmd_append_tsf_tlv(struct mwifiex_private *priv, u8 **buffer, | ||
100 | struct mwifiex_bssdescriptor *bss_desc) | ||
101 | { | ||
102 | struct mwifiex_ie_types_tsf_timestamp tsf_tlv; | ||
103 | long long tsf_val; | ||
104 | |||
105 | /* Null Checks */ | ||
106 | if (buffer == NULL) | ||
107 | return 0; | ||
108 | if (*buffer == NULL) | ||
109 | return 0; | ||
110 | |||
111 | memset(&tsf_tlv, 0x00, sizeof(struct mwifiex_ie_types_tsf_timestamp)); | ||
112 | |||
113 | tsf_tlv.header.type = cpu_to_le16(TLV_TYPE_TSFTIMESTAMP); | ||
114 | tsf_tlv.header.len = cpu_to_le16(2 * sizeof(tsf_val)); | ||
115 | |||
116 | memcpy(*buffer, &tsf_tlv, sizeof(tsf_tlv.header)); | ||
117 | *buffer += sizeof(tsf_tlv.header); | ||
118 | |||
119 | memcpy(*buffer, &tsf_val, sizeof(tsf_val)); | ||
120 | *buffer += sizeof(tsf_val); | ||
121 | |||
122 | memcpy(&tsf_val, bss_desc->time_stamp, sizeof(tsf_val)); | ||
123 | |||
124 | dev_dbg(priv->adapter->dev, "info: %s: TSF offset calc: %016llx - " | ||
125 | "%016llx\n", __func__, tsf_val, bss_desc->network_tsf); | ||
126 | |||
127 | memcpy(*buffer, &tsf_val, sizeof(tsf_val)); | ||
128 | *buffer += sizeof(tsf_val); | ||
129 | |||
130 | return sizeof(tsf_tlv.header) + (2 * sizeof(tsf_val)); | ||
131 | } | ||
132 | |||
133 | /* | ||
134 | * This function finds out the common rates between rate1 and rate2. | ||
135 | * | ||
136 | * It will fill common rates in rate1 as output if found. | ||
137 | * | ||
138 | * NOTE: Setting the MSB of the basic rates needs to be taken | ||
139 | * care of, either before or after calling this function. | ||
140 | */ | ||
141 | static int mwifiex_get_common_rates(struct mwifiex_private *priv, u8 *rate1, | ||
142 | u32 rate1_size, u8 *rate2, u32 rate2_size) | ||
143 | { | ||
144 | int ret = 0; | ||
145 | u8 *ptr = rate1; | ||
146 | u8 *tmp = NULL; | ||
147 | u32 i, j; | ||
148 | |||
149 | tmp = kmalloc(rate1_size, GFP_KERNEL); | ||
150 | if (!tmp) { | ||
151 | dev_err(priv->adapter->dev, "failed to alloc tmp buf\n"); | ||
152 | return -ENOMEM; | ||
153 | } | ||
154 | |||
155 | memcpy(tmp, rate1, rate1_size); | ||
156 | memset(rate1, 0, rate1_size); | ||
157 | |||
158 | for (i = 0; rate2[i] && i < rate2_size; i++) { | ||
159 | for (j = 0; tmp[j] && j < rate1_size; j++) { | ||
160 | /* Check common rate, excluding the bit for | ||
161 | basic rate */ | ||
162 | if ((rate2[i] & 0x7F) == (tmp[j] & 0x7F)) { | ||
163 | *rate1++ = tmp[j]; | ||
164 | break; | ||
165 | } | ||
166 | } | ||
167 | } | ||
168 | |||
169 | dev_dbg(priv->adapter->dev, "info: Tx data rate set to %#x\n", | ||
170 | priv->data_rate); | ||
171 | |||
172 | if (!priv->is_data_rate_auto) { | ||
173 | while (*ptr) { | ||
174 | if ((*ptr & 0x7f) == priv->data_rate) { | ||
175 | ret = 0; | ||
176 | goto done; | ||
177 | } | ||
178 | ptr++; | ||
179 | } | ||
180 | dev_err(priv->adapter->dev, "previously set fixed data rate %#x" | ||
181 | " is not compatible with the network\n", | ||
182 | priv->data_rate); | ||
183 | |||
184 | ret = -1; | ||
185 | goto done; | ||
186 | } | ||
187 | |||
188 | ret = 0; | ||
189 | done: | ||
190 | kfree(tmp); | ||
191 | return ret; | ||
192 | } | ||
193 | |||
194 | /* | ||
195 | * This function creates the intersection of the rates supported by a | ||
196 | * target BSS and our adapter settings for use in an assoc/join command. | ||
197 | */ | ||
198 | static int | ||
199 | mwifiex_setup_rates_from_bssdesc(struct mwifiex_private *priv, | ||
200 | struct mwifiex_bssdescriptor *bss_desc, | ||
201 | u8 *out_rates, u32 *out_rates_size) | ||
202 | { | ||
203 | u8 card_rates[MWIFIEX_SUPPORTED_RATES]; | ||
204 | u32 card_rates_size = 0; | ||
205 | |||
206 | /* Copy AP supported rates */ | ||
207 | memcpy(out_rates, bss_desc->supported_rates, MWIFIEX_SUPPORTED_RATES); | ||
208 | /* Get the STA supported rates */ | ||
209 | card_rates_size = mwifiex_get_active_data_rates(priv, card_rates); | ||
210 | /* Get the common rates between AP and STA supported rates */ | ||
211 | if (mwifiex_get_common_rates(priv, out_rates, MWIFIEX_SUPPORTED_RATES, | ||
212 | card_rates, card_rates_size)) { | ||
213 | *out_rates_size = 0; | ||
214 | dev_err(priv->adapter->dev, "%s: cannot get common rates\n", | ||
215 | __func__); | ||
216 | return -1; | ||
217 | } | ||
218 | |||
219 | *out_rates_size = | ||
220 | min_t(size_t, strlen(out_rates), MWIFIEX_SUPPORTED_RATES); | ||
221 | |||
222 | return 0; | ||
223 | } | ||
224 | |||
225 | /* | ||
226 | * This function updates the scan entry TSF timestamps to reflect | ||
227 | * a new association. | ||
228 | */ | ||
229 | static void | ||
230 | mwifiex_update_tsf_timestamps(struct mwifiex_private *priv, | ||
231 | struct mwifiex_bssdescriptor *new_bss_desc) | ||
232 | { | ||
233 | struct mwifiex_adapter *adapter = priv->adapter; | ||
234 | u32 table_idx; | ||
235 | long long new_tsf_base; | ||
236 | signed long long tsf_delta; | ||
237 | |||
238 | memcpy(&new_tsf_base, new_bss_desc->time_stamp, sizeof(new_tsf_base)); | ||
239 | |||
240 | tsf_delta = new_tsf_base - new_bss_desc->network_tsf; | ||
241 | |||
242 | dev_dbg(adapter->dev, "info: TSF: update TSF timestamps, " | ||
243 | "0x%016llx -> 0x%016llx\n", | ||
244 | new_bss_desc->network_tsf, new_tsf_base); | ||
245 | |||
246 | for (table_idx = 0; table_idx < adapter->num_in_scan_table; | ||
247 | table_idx++) | ||
248 | adapter->scan_table[table_idx].network_tsf += tsf_delta; | ||
249 | } | ||
250 | |||
251 | /* | ||
252 | * This function appends a WAPI IE. | ||
253 | * | ||
254 | * This function is called from the network join command preparation routine. | ||
255 | * | ||
256 | * If the IE buffer has been setup by the application, this routine appends | ||
257 | * the buffer as a WAPI TLV type to the request. | ||
258 | */ | ||
259 | static int | ||
260 | mwifiex_cmd_append_wapi_ie(struct mwifiex_private *priv, u8 **buffer) | ||
261 | { | ||
262 | int retLen = 0; | ||
263 | struct mwifiex_ie_types_header ie_header; | ||
264 | |||
265 | /* Null Checks */ | ||
266 | if (buffer == NULL) | ||
267 | return 0; | ||
268 | if (*buffer == NULL) | ||
269 | return 0; | ||
270 | |||
271 | /* | ||
272 | * If there is a wapi ie buffer setup, append it to the return | ||
273 | * parameter buffer pointer. | ||
274 | */ | ||
275 | if (priv->wapi_ie_len) { | ||
276 | dev_dbg(priv->adapter->dev, "cmd: append wapi ie %d to %p\n", | ||
277 | priv->wapi_ie_len, *buffer); | ||
278 | |||
279 | /* Wrap the generic IE buffer with a pass through TLV type */ | ||
280 | ie_header.type = cpu_to_le16(TLV_TYPE_WAPI_IE); | ||
281 | ie_header.len = cpu_to_le16(priv->wapi_ie_len); | ||
282 | memcpy(*buffer, &ie_header, sizeof(ie_header)); | ||
283 | |||
284 | /* Increment the return size and the return buffer pointer | ||
285 | param */ | ||
286 | *buffer += sizeof(ie_header); | ||
287 | retLen += sizeof(ie_header); | ||
288 | |||
289 | /* Copy the wapi IE buffer to the output buffer, advance | ||
290 | pointer */ | ||
291 | memcpy(*buffer, priv->wapi_ie, priv->wapi_ie_len); | ||
292 | |||
293 | /* Increment the return size and the return buffer pointer | ||
294 | param */ | ||
295 | *buffer += priv->wapi_ie_len; | ||
296 | retLen += priv->wapi_ie_len; | ||
297 | |||
298 | } | ||
299 | /* return the length appended to the buffer */ | ||
300 | return retLen; | ||
301 | } | ||
302 | |||
303 | /* | ||
304 | * This function appends rsn ie tlv for wpa/wpa2 security modes. | ||
305 | * It is called from the network join command preparation routine. | ||
306 | */ | ||
307 | static int mwifiex_append_rsn_ie_wpa_wpa2(struct mwifiex_private *priv, | ||
308 | u8 **buffer) | ||
309 | { | ||
310 | struct mwifiex_ie_types_rsn_param_set *rsn_ie_tlv; | ||
311 | int rsn_ie_len; | ||
312 | |||
313 | if (!buffer || !(*buffer)) | ||
314 | return 0; | ||
315 | |||
316 | rsn_ie_tlv = (struct mwifiex_ie_types_rsn_param_set *) (*buffer); | ||
317 | rsn_ie_tlv->header.type = cpu_to_le16((u16) priv->wpa_ie[0]); | ||
318 | rsn_ie_tlv->header.type = cpu_to_le16( | ||
319 | le16_to_cpu(rsn_ie_tlv->header.type) & 0x00FF); | ||
320 | rsn_ie_tlv->header.len = cpu_to_le16((u16) priv->wpa_ie[1]); | ||
321 | rsn_ie_tlv->header.len = cpu_to_le16(le16_to_cpu(rsn_ie_tlv->header.len) | ||
322 | & 0x00FF); | ||
323 | if (le16_to_cpu(rsn_ie_tlv->header.len) <= (sizeof(priv->wpa_ie) - 2)) | ||
324 | memcpy(rsn_ie_tlv->rsn_ie, &priv->wpa_ie[2], | ||
325 | le16_to_cpu(rsn_ie_tlv->header.len)); | ||
326 | else | ||
327 | return -1; | ||
328 | |||
329 | rsn_ie_len = sizeof(rsn_ie_tlv->header) + | ||
330 | le16_to_cpu(rsn_ie_tlv->header.len); | ||
331 | *buffer += rsn_ie_len; | ||
332 | |||
333 | return rsn_ie_len; | ||
334 | } | ||
335 | |||
336 | /* | ||
337 | * This function prepares command for association. | ||
338 | * | ||
339 | * This sets the following parameters - | ||
340 | * - Peer MAC address | ||
341 | * - Listen interval | ||
342 | * - Beacon interval | ||
343 | * - Capability information | ||
344 | * | ||
345 | * ...and the following TLVs, as required - | ||
346 | * - SSID TLV | ||
347 | * - PHY TLV | ||
348 | * - SS TLV | ||
349 | * - Rates TLV | ||
350 | * - Authentication TLV | ||
351 | * - Channel TLV | ||
352 | * - WPA/WPA2 IE | ||
353 | * - 11n TLV | ||
354 | * - Vendor specific TLV | ||
355 | * - WMM TLV | ||
356 | * - WAPI IE | ||
357 | * - Generic IE | ||
358 | * - TSF TLV | ||
359 | * | ||
360 | * Preparation also includes - | ||
361 | * - Setting command ID and proper size | ||
362 | * - Ensuring correct endian-ness | ||
363 | */ | ||
364 | int mwifiex_cmd_802_11_associate(struct mwifiex_private *priv, | ||
365 | struct host_cmd_ds_command *cmd, | ||
366 | void *data_buf) | ||
367 | { | ||
368 | struct host_cmd_ds_802_11_associate *assoc = &cmd->params.associate; | ||
369 | struct mwifiex_bssdescriptor *bss_desc; | ||
370 | struct mwifiex_ie_types_ssid_param_set *ssid_tlv; | ||
371 | struct mwifiex_ie_types_phy_param_set *phy_tlv; | ||
372 | struct mwifiex_ie_types_ss_param_set *ss_tlv; | ||
373 | struct mwifiex_ie_types_rates_param_set *rates_tlv; | ||
374 | struct mwifiex_ie_types_auth_type *auth_tlv; | ||
375 | struct mwifiex_ie_types_chan_list_param_set *chan_tlv; | ||
376 | u8 rates[MWIFIEX_SUPPORTED_RATES]; | ||
377 | u32 rates_size; | ||
378 | u16 tmp_cap; | ||
379 | u8 *pos; | ||
380 | int rsn_ie_len = 0; | ||
381 | |||
382 | bss_desc = (struct mwifiex_bssdescriptor *) data_buf; | ||
383 | pos = (u8 *) assoc; | ||
384 | |||
385 | mwifiex_cfg_tx_buf(priv, bss_desc); | ||
386 | |||
387 | cmd->command = cpu_to_le16(HostCmd_CMD_802_11_ASSOCIATE); | ||
388 | |||
389 | /* Save so we know which BSS Desc to use in the response handler */ | ||
390 | priv->attempted_bss_desc = bss_desc; | ||
391 | |||
392 | memcpy(assoc->peer_sta_addr, | ||
393 | bss_desc->mac_address, sizeof(assoc->peer_sta_addr)); | ||
394 | pos += sizeof(assoc->peer_sta_addr); | ||
395 | |||
396 | /* Set the listen interval */ | ||
397 | assoc->listen_interval = cpu_to_le16(priv->listen_interval); | ||
398 | /* Set the beacon period */ | ||
399 | assoc->beacon_period = cpu_to_le16(bss_desc->beacon_period); | ||
400 | |||
401 | pos += sizeof(assoc->cap_info_bitmap); | ||
402 | pos += sizeof(assoc->listen_interval); | ||
403 | pos += sizeof(assoc->beacon_period); | ||
404 | pos += sizeof(assoc->dtim_period); | ||
405 | |||
406 | ssid_tlv = (struct mwifiex_ie_types_ssid_param_set *) pos; | ||
407 | ssid_tlv->header.type = cpu_to_le16(WLAN_EID_SSID); | ||
408 | ssid_tlv->header.len = cpu_to_le16((u16) bss_desc->ssid.ssid_len); | ||
409 | memcpy(ssid_tlv->ssid, bss_desc->ssid.ssid, | ||
410 | le16_to_cpu(ssid_tlv->header.len)); | ||
411 | pos += sizeof(ssid_tlv->header) + le16_to_cpu(ssid_tlv->header.len); | ||
412 | |||
413 | phy_tlv = (struct mwifiex_ie_types_phy_param_set *) pos; | ||
414 | phy_tlv->header.type = cpu_to_le16(WLAN_EID_DS_PARAMS); | ||
415 | phy_tlv->header.len = cpu_to_le16(sizeof(phy_tlv->fh_ds.ds_param_set)); | ||
416 | memcpy(&phy_tlv->fh_ds.ds_param_set, | ||
417 | &bss_desc->phy_param_set.ds_param_set.current_chan, | ||
418 | sizeof(phy_tlv->fh_ds.ds_param_set)); | ||
419 | pos += sizeof(phy_tlv->header) + le16_to_cpu(phy_tlv->header.len); | ||
420 | |||
421 | ss_tlv = (struct mwifiex_ie_types_ss_param_set *) pos; | ||
422 | ss_tlv->header.type = cpu_to_le16(WLAN_EID_CF_PARAMS); | ||
423 | ss_tlv->header.len = cpu_to_le16(sizeof(ss_tlv->cf_ibss.cf_param_set)); | ||
424 | pos += sizeof(ss_tlv->header) + le16_to_cpu(ss_tlv->header.len); | ||
425 | |||
426 | /* Get the common rates supported between the driver and the BSS Desc */ | ||
427 | if (mwifiex_setup_rates_from_bssdesc | ||
428 | (priv, bss_desc, rates, &rates_size)) | ||
429 | return -1; | ||
430 | |||
431 | /* Save the data rates into Current BSS state structure */ | ||
432 | priv->curr_bss_params.num_of_rates = rates_size; | ||
433 | memcpy(&priv->curr_bss_params.data_rates, rates, rates_size); | ||
434 | |||
435 | /* Setup the Rates TLV in the association command */ | ||
436 | rates_tlv = (struct mwifiex_ie_types_rates_param_set *) pos; | ||
437 | rates_tlv->header.type = cpu_to_le16(WLAN_EID_SUPP_RATES); | ||
438 | rates_tlv->header.len = cpu_to_le16((u16) rates_size); | ||
439 | memcpy(rates_tlv->rates, rates, rates_size); | ||
440 | pos += sizeof(rates_tlv->header) + rates_size; | ||
441 | dev_dbg(priv->adapter->dev, "info: ASSOC_CMD: rates size = %d\n", | ||
442 | rates_size); | ||
443 | |||
444 | /* Add the Authentication type to be used for Auth frames if needed */ | ||
445 | if (priv->sec_info.authentication_mode != MWIFIEX_AUTH_MODE_AUTO) { | ||
446 | auth_tlv = (struct mwifiex_ie_types_auth_type *) pos; | ||
447 | auth_tlv->header.type = cpu_to_le16(TLV_TYPE_AUTH_TYPE); | ||
448 | auth_tlv->header.len = cpu_to_le16(sizeof(auth_tlv->auth_type)); | ||
449 | if (priv->sec_info.wep_status == MWIFIEX_802_11_WEP_ENABLED) | ||
450 | auth_tlv->auth_type = cpu_to_le16((u16) priv->sec_info. | ||
451 | authentication_mode); | ||
452 | else | ||
453 | auth_tlv->auth_type = | ||
454 | cpu_to_le16(MWIFIEX_AUTH_MODE_OPEN); | ||
455 | pos += sizeof(auth_tlv->header) + | ||
456 | le16_to_cpu(auth_tlv->header.len); | ||
457 | } | ||
458 | |||
459 | if (IS_SUPPORT_MULTI_BANDS(priv->adapter) | ||
460 | && !(ISSUPP_11NENABLED(priv->adapter->fw_cap_info) | ||
461 | && (!bss_desc->disable_11n) | ||
462 | && (priv->adapter->config_bands & BAND_GN | ||
463 | || priv->adapter->config_bands & BAND_AN) | ||
464 | && (bss_desc->bcn_ht_cap) | ||
465 | ) | ||
466 | ) { | ||
467 | /* Append a channel TLV for the channel the attempted AP was | ||
468 | found on */ | ||
469 | chan_tlv = (struct mwifiex_ie_types_chan_list_param_set *) pos; | ||
470 | chan_tlv->header.type = cpu_to_le16(TLV_TYPE_CHANLIST); | ||
471 | chan_tlv->header.len = | ||
472 | cpu_to_le16(sizeof(struct mwifiex_chan_scan_param_set)); | ||
473 | |||
474 | memset(chan_tlv->chan_scan_param, 0x00, | ||
475 | sizeof(struct mwifiex_chan_scan_param_set)); | ||
476 | chan_tlv->chan_scan_param[0].chan_number = | ||
477 | (bss_desc->phy_param_set.ds_param_set.current_chan); | ||
478 | dev_dbg(priv->adapter->dev, "info: Assoc: TLV Chan = %d\n", | ||
479 | chan_tlv->chan_scan_param[0].chan_number); | ||
480 | |||
481 | chan_tlv->chan_scan_param[0].radio_type = | ||
482 | mwifiex_band_to_radio_type((u8) bss_desc->bss_band); | ||
483 | |||
484 | dev_dbg(priv->adapter->dev, "info: Assoc: TLV Band = %d\n", | ||
485 | chan_tlv->chan_scan_param[0].radio_type); | ||
486 | pos += sizeof(chan_tlv->header) + | ||
487 | sizeof(struct mwifiex_chan_scan_param_set); | ||
488 | } | ||
489 | |||
490 | if (!priv->wps.session_enable) { | ||
491 | if (priv->sec_info.wpa_enabled || priv->sec_info.wpa2_enabled) | ||
492 | rsn_ie_len = mwifiex_append_rsn_ie_wpa_wpa2(priv, &pos); | ||
493 | |||
494 | if (rsn_ie_len == -1) | ||
495 | return -1; | ||
496 | } | ||
497 | |||
498 | if (ISSUPP_11NENABLED(priv->adapter->fw_cap_info) | ||
499 | && (!bss_desc->disable_11n) | ||
500 | && (priv->adapter->config_bands & BAND_GN | ||
501 | || priv->adapter->config_bands & BAND_AN)) | ||
502 | mwifiex_cmd_append_11n_tlv(priv, bss_desc, &pos); | ||
503 | |||
504 | /* Append vendor specific IE TLV */ | ||
505 | mwifiex_cmd_append_vsie_tlv(priv, MWIFIEX_VSIE_MASK_ASSOC, &pos); | ||
506 | |||
507 | mwifiex_wmm_process_association_req(priv, &pos, &bss_desc->wmm_ie, | ||
508 | bss_desc->bcn_ht_cap); | ||
509 | if (priv->sec_info.wapi_enabled && priv->wapi_ie_len) | ||
510 | mwifiex_cmd_append_wapi_ie(priv, &pos); | ||
511 | |||
512 | |||
513 | mwifiex_cmd_append_generic_ie(priv, &pos); | ||
514 | |||
515 | mwifiex_cmd_append_tsf_tlv(priv, &pos, bss_desc); | ||
516 | |||
517 | cmd->size = cpu_to_le16((u16) (pos - (u8 *) assoc) + S_DS_GEN); | ||
518 | |||
519 | /* Set the Capability info at last */ | ||
520 | tmp_cap = bss_desc->cap_info_bitmap; | ||
521 | |||
522 | if (priv->adapter->config_bands == BAND_B) | ||
523 | SHORT_SLOT_TIME_DISABLED(tmp_cap); | ||
524 | |||
525 | tmp_cap &= CAPINFO_MASK; | ||
526 | dev_dbg(priv->adapter->dev, "info: ASSOC_CMD: tmp_cap=%4X CAPINFO_MASK=%4lX\n", | ||
527 | tmp_cap, CAPINFO_MASK); | ||
528 | assoc->cap_info_bitmap = cpu_to_le16(tmp_cap); | ||
529 | |||
530 | return 0; | ||
531 | } | ||
532 | |||
533 | /* | ||
534 | * Association firmware command response handler | ||
535 | * | ||
536 | * The response buffer for the association command has the following | ||
537 | * memory layout. | ||
538 | * | ||
539 | * For cases where an association response was not received (indicated | ||
540 | * by the CapInfo and AId field): | ||
541 | * | ||
542 | * .------------------------------------------------------------. | ||
543 | * | Header(4 * sizeof(t_u16)): Standard command response hdr | | ||
544 | * .------------------------------------------------------------. | ||
545 | * | cap_info/Error Return(t_u16): | | ||
546 | * | 0xFFFF(-1): Internal error | | ||
547 | * | 0xFFFE(-2): Authentication unhandled message | | ||
548 | * | 0xFFFD(-3): Authentication refused | | ||
549 | * | 0xFFFC(-4): Timeout waiting for AP response | | ||
550 | * .------------------------------------------------------------. | ||
551 | * | status_code(t_u16): | | ||
552 | * | If cap_info is -1: | | ||
553 | * | An internal firmware failure prevented the | | ||
554 | * | command from being processed. The status_code | | ||
555 | * | will be set to 1. | | ||
556 | * | | | ||
557 | * | If cap_info is -2: | | ||
558 | * | An authentication frame was received but was | | ||
559 | * | not handled by the firmware. IEEE Status | | ||
560 | * | code for the failure is returned. | | ||
561 | * | | | ||
562 | * | If cap_info is -3: | | ||
563 | * | An authentication frame was received and the | | ||
564 | * | status_code is the IEEE Status reported in the | | ||
565 | * | response. | | ||
566 | * | | | ||
567 | * | If cap_info is -4: | | ||
568 | * | (1) Association response timeout | | ||
569 | * | (2) Authentication response timeout | | ||
570 | * .------------------------------------------------------------. | ||
571 | * | a_id(t_u16): 0xFFFF | | ||
572 | * .------------------------------------------------------------. | ||
573 | * | ||
574 | * | ||
575 | * For cases where an association response was received, the IEEE | ||
576 | * standard association response frame is returned: | ||
577 | * | ||
578 | * .------------------------------------------------------------. | ||
579 | * | Header(4 * sizeof(t_u16)): Standard command response hdr | | ||
580 | * .------------------------------------------------------------. | ||
581 | * | cap_info(t_u16): IEEE Capability | | ||
582 | * .------------------------------------------------------------. | ||
583 | * | status_code(t_u16): IEEE Status Code | | ||
584 | * .------------------------------------------------------------. | ||
585 | * | a_id(t_u16): IEEE Association ID | | ||
586 | * .------------------------------------------------------------. | ||
587 | * | IEEE IEs(variable): Any received IEs comprising the | | ||
588 | * | remaining portion of a received | | ||
589 | * | association response frame. | | ||
590 | * .------------------------------------------------------------. | ||
591 | * | ||
592 | * For simplistic handling, the status_code field can be used to determine | ||
593 | * an association success (0) or failure (non-zero). | ||
594 | */ | ||
595 | int mwifiex_ret_802_11_associate(struct mwifiex_private *priv, | ||
596 | struct host_cmd_ds_command *resp, void *wq_buf) | ||
597 | { | ||
598 | int ret = 0; | ||
599 | struct mwifiex_wait_queue *wait_queue = | ||
600 | (struct mwifiex_wait_queue *) wq_buf; | ||
601 | struct ieee_types_assoc_rsp *assoc_rsp; | ||
602 | struct mwifiex_bssdescriptor *bss_desc; | ||
603 | u8 enable_data = true; | ||
604 | |||
605 | assoc_rsp = (struct ieee_types_assoc_rsp *) &resp->params; | ||
606 | |||
607 | priv->assoc_rsp_size = min(le16_to_cpu(resp->size) - S_DS_GEN, | ||
608 | sizeof(priv->assoc_rsp_buf)); | ||
609 | |||
610 | memcpy(priv->assoc_rsp_buf, &resp->params, priv->assoc_rsp_size); | ||
611 | |||
612 | if (le16_to_cpu(assoc_rsp->status_code)) { | ||
613 | priv->adapter->dbg.num_cmd_assoc_failure++; | ||
614 | dev_err(priv->adapter->dev, "ASSOC_RESP: association failed, " | ||
615 | "status code = %d, error = 0x%x, a_id = 0x%x\n", | ||
616 | le16_to_cpu(assoc_rsp->status_code), | ||
617 | le16_to_cpu(assoc_rsp->cap_info_bitmap), | ||
618 | le16_to_cpu(assoc_rsp->a_id)); | ||
619 | |||
620 | ret = -1; | ||
621 | goto done; | ||
622 | } | ||
623 | |||
624 | /* Send a Media Connected event, according to the Spec */ | ||
625 | priv->media_connected = true; | ||
626 | |||
627 | priv->adapter->ps_state = PS_STATE_AWAKE; | ||
628 | priv->adapter->pps_uapsd_mode = false; | ||
629 | priv->adapter->tx_lock_flag = false; | ||
630 | |||
631 | /* Set the attempted BSSID Index to current */ | ||
632 | bss_desc = priv->attempted_bss_desc; | ||
633 | |||
634 | dev_dbg(priv->adapter->dev, "info: ASSOC_RESP: %s\n", | ||
635 | bss_desc->ssid.ssid); | ||
636 | |||
637 | /* Make a copy of current BSSID descriptor */ | ||
638 | memcpy(&priv->curr_bss_params.bss_descriptor, | ||
639 | bss_desc, sizeof(struct mwifiex_bssdescriptor)); | ||
640 | |||
641 | /* Update curr_bss_params */ | ||
642 | priv->curr_bss_params.bss_descriptor.channel | ||
643 | = bss_desc->phy_param_set.ds_param_set.current_chan; | ||
644 | |||
645 | priv->curr_bss_params.band = (u8) bss_desc->bss_band; | ||
646 | |||
647 | /* | ||
648 | * Adjust the timestamps in the scan table to be relative to the newly | ||
649 | * associated AP's TSF | ||
650 | */ | ||
651 | mwifiex_update_tsf_timestamps(priv, bss_desc); | ||
652 | |||
653 | if (bss_desc->wmm_ie.vend_hdr.element_id == WLAN_EID_VENDOR_SPECIFIC) | ||
654 | priv->curr_bss_params.wmm_enabled = true; | ||
655 | else | ||
656 | priv->curr_bss_params.wmm_enabled = false; | ||
657 | |||
658 | if ((priv->wmm_required || bss_desc->bcn_ht_cap) | ||
659 | && priv->curr_bss_params.wmm_enabled) | ||
660 | priv->wmm_enabled = true; | ||
661 | else | ||
662 | priv->wmm_enabled = false; | ||
663 | |||
664 | priv->curr_bss_params.wmm_uapsd_enabled = false; | ||
665 | |||
666 | if (priv->wmm_enabled) | ||
667 | priv->curr_bss_params.wmm_uapsd_enabled | ||
668 | = ((bss_desc->wmm_ie.qos_info_bitmap & | ||
669 | IEEE80211_WMM_IE_AP_QOSINFO_UAPSD) ? 1 : 0); | ||
670 | |||
671 | dev_dbg(priv->adapter->dev, "info: ASSOC_RESP: curr_pkt_filter is %#x\n", | ||
672 | priv->curr_pkt_filter); | ||
673 | if (priv->sec_info.wpa_enabled || priv->sec_info.wpa2_enabled) | ||
674 | priv->wpa_is_gtk_set = false; | ||
675 | |||
676 | if (priv->wmm_enabled) { | ||
677 | /* Don't re-enable carrier until we get the WMM_GET_STATUS | ||
678 | event */ | ||
679 | enable_data = false; | ||
680 | } else { | ||
681 | /* Since WMM is not enabled, setup the queues with the | ||
682 | defaults */ | ||
683 | mwifiex_wmm_setup_queue_priorities(priv, NULL); | ||
684 | mwifiex_wmm_setup_ac_downgrade(priv); | ||
685 | } | ||
686 | |||
687 | if (enable_data) | ||
688 | dev_dbg(priv->adapter->dev, | ||
689 | "info: post association, re-enabling data flow\n"); | ||
690 | |||
691 | /* Reset SNR/NF/RSSI values */ | ||
692 | priv->data_rssi_last = 0; | ||
693 | priv->data_nf_last = 0; | ||
694 | priv->data_rssi_avg = 0; | ||
695 | priv->data_nf_avg = 0; | ||
696 | priv->bcn_rssi_last = 0; | ||
697 | priv->bcn_nf_last = 0; | ||
698 | priv->bcn_rssi_avg = 0; | ||
699 | priv->bcn_nf_avg = 0; | ||
700 | priv->rxpd_rate = 0; | ||
701 | priv->rxpd_htinfo = 0; | ||
702 | |||
703 | mwifiex_save_curr_bcn(priv); | ||
704 | |||
705 | priv->adapter->dbg.num_cmd_assoc_success++; | ||
706 | |||
707 | dev_dbg(priv->adapter->dev, "info: ASSOC_RESP: associated\n"); | ||
708 | |||
709 | /* Add the ra_list here for infra mode as there will be only 1 ra | ||
710 | always */ | ||
711 | mwifiex_ralist_add(priv, | ||
712 | priv->curr_bss_params.bss_descriptor.mac_address); | ||
713 | |||
714 | if (!netif_carrier_ok(priv->netdev)) | ||
715 | netif_carrier_on(priv->netdev); | ||
716 | if (netif_queue_stopped(priv->netdev)) | ||
717 | netif_wake_queue(priv->netdev); | ||
718 | |||
719 | if (priv->sec_info.wpa_enabled || priv->sec_info.wpa2_enabled) | ||
720 | priv->scan_block = true; | ||
721 | |||
722 | done: | ||
723 | /* Need to indicate IOCTL complete */ | ||
724 | if (wait_queue) { | ||
725 | if (ret) { | ||
726 | if (assoc_rsp->status_code) | ||
727 | wait_queue->status = | ||
728 | le16_to_cpu(assoc_rsp->status_code); | ||
729 | else | ||
730 | wait_queue->status = MWIFIEX_ERROR_ASSOC_FAIL; | ||
731 | } else { | ||
732 | wait_queue->status = MWIFIEX_ERROR_NO_ERROR; | ||
733 | } | ||
734 | } | ||
735 | |||
736 | return ret; | ||
737 | } | ||
738 | |||
739 | /* | ||
740 | * This function prepares command for ad-hoc start. | ||
741 | * | ||
742 | * Driver will fill up SSID, BSS mode, IBSS parameters, physical | ||
743 | * parameters, probe delay, and capability information. Firmware | ||
744 | * will fill up beacon period, basic rates and operational rates. | ||
745 | * | ||
746 | * In addition, the following TLVs are added - | ||
747 | * - Channel TLV | ||
748 | * - Vendor specific IE | ||
749 | * - WPA/WPA2 IE | ||
750 | * - HT Capabilities IE | ||
751 | * - HT Information IE | ||
752 | * | ||
753 | * Preparation also includes - | ||
754 | * - Setting command ID and proper size | ||
755 | * - Ensuring correct endian-ness | ||
756 | */ | ||
757 | int | ||
758 | mwifiex_cmd_802_11_ad_hoc_start(struct mwifiex_private *priv, | ||
759 | struct host_cmd_ds_command *cmd, void *data_buf) | ||
760 | { | ||
761 | int ret = 0, rsn_ie_len = 0; | ||
762 | struct mwifiex_adapter *adapter = priv->adapter; | ||
763 | struct host_cmd_ds_802_11_ad_hoc_start *adhoc_start = | ||
764 | &cmd->params.adhoc_start; | ||
765 | struct mwifiex_bssdescriptor *bss_desc; | ||
766 | u32 cmd_append_size = 0; | ||
767 | u32 i; | ||
768 | u16 tmp_cap; | ||
769 | uint16_t ht_cap_info; | ||
770 | struct mwifiex_ie_types_chan_list_param_set *chan_tlv; | ||
771 | |||
772 | struct mwifiex_ie_types_htcap *ht_cap; | ||
773 | struct mwifiex_ie_types_htinfo *ht_info; | ||
774 | u8 *pos = (u8 *) adhoc_start + | ||
775 | sizeof(struct host_cmd_ds_802_11_ad_hoc_start); | ||
776 | |||
777 | if (!adapter) | ||
778 | return -1; | ||
779 | |||
780 | cmd->command = cpu_to_le16(HostCmd_CMD_802_11_AD_HOC_START); | ||
781 | |||
782 | bss_desc = &priv->curr_bss_params.bss_descriptor; | ||
783 | priv->attempted_bss_desc = bss_desc; | ||
784 | |||
785 | /* | ||
786 | * Fill in the parameters for 2 data structures: | ||
787 | * 1. struct host_cmd_ds_802_11_ad_hoc_start command | ||
788 | * 2. bss_desc | ||
789 | * Driver will fill up SSID, bss_mode,IBSS param, Physical Param, | ||
790 | * probe delay, and Cap info. | ||
791 | * Firmware will fill up beacon period, Basic rates | ||
792 | * and operational rates. | ||
793 | */ | ||
794 | |||
795 | memset(adhoc_start->ssid, 0, IEEE80211_MAX_SSID_LEN); | ||
796 | |||
797 | memcpy(adhoc_start->ssid, | ||
798 | ((struct mwifiex_802_11_ssid *) data_buf)->ssid, | ||
799 | ((struct mwifiex_802_11_ssid *) data_buf)->ssid_len); | ||
800 | |||
801 | dev_dbg(adapter->dev, "info: ADHOC_S_CMD: SSID = %s\n", | ||
802 | adhoc_start->ssid); | ||
803 | |||
804 | memset(bss_desc->ssid.ssid, 0, IEEE80211_MAX_SSID_LEN); | ||
805 | memcpy(bss_desc->ssid.ssid, | ||
806 | ((struct mwifiex_802_11_ssid *) data_buf)->ssid, | ||
807 | ((struct mwifiex_802_11_ssid *) data_buf)->ssid_len); | ||
808 | |||
809 | bss_desc->ssid.ssid_len = | ||
810 | ((struct mwifiex_802_11_ssid *) data_buf)->ssid_len; | ||
811 | |||
812 | /* Set the BSS mode */ | ||
813 | adhoc_start->bss_mode = HostCmd_BSS_MODE_IBSS; | ||
814 | bss_desc->bss_mode = MWIFIEX_BSS_MODE_IBSS; | ||
815 | adhoc_start->beacon_period = cpu_to_le16(priv->beacon_period); | ||
816 | bss_desc->beacon_period = priv->beacon_period; | ||
817 | |||
818 | /* Set Physical param set */ | ||
819 | /* Parameter IE Id */ | ||
820 | #define DS_PARA_IE_ID 3 | ||
821 | /* Parameter IE length */ | ||
822 | #define DS_PARA_IE_LEN 1 | ||
823 | |||
824 | adhoc_start->phy_param_set.ds_param_set.element_id = DS_PARA_IE_ID; | ||
825 | adhoc_start->phy_param_set.ds_param_set.len = DS_PARA_IE_LEN; | ||
826 | |||
827 | if (!mwifiex_get_cfp_by_band_and_channel_from_cfg80211 | ||
828 | (priv, adapter->adhoc_start_band, (u16) | ||
829 | priv->adhoc_channel)) { | ||
830 | struct mwifiex_chan_freq_power *cfp; | ||
831 | cfp = mwifiex_get_cfp_by_band_and_channel_from_cfg80211(priv, | ||
832 | adapter->adhoc_start_band, FIRST_VALID_CHANNEL); | ||
833 | if (cfp) | ||
834 | priv->adhoc_channel = (u8) cfp->channel; | ||
835 | } | ||
836 | |||
837 | if (!priv->adhoc_channel) { | ||
838 | dev_err(adapter->dev, "ADHOC_S_CMD: adhoc_channel cannot be 0\n"); | ||
839 | return -1; | ||
840 | } | ||
841 | |||
842 | dev_dbg(adapter->dev, "info: ADHOC_S_CMD: creating ADHOC on channel %d\n", | ||
843 | priv->adhoc_channel); | ||
844 | |||
845 | priv->curr_bss_params.bss_descriptor.channel = priv->adhoc_channel; | ||
846 | priv->curr_bss_params.band = adapter->adhoc_start_band; | ||
847 | |||
848 | bss_desc->channel = priv->adhoc_channel; | ||
849 | adhoc_start->phy_param_set.ds_param_set.current_chan = | ||
850 | priv->adhoc_channel; | ||
851 | |||
852 | memcpy(&bss_desc->phy_param_set, &adhoc_start->phy_param_set, | ||
853 | sizeof(union ieee_types_phy_param_set)); | ||
854 | |||
855 | /* Set IBSS param set */ | ||
856 | /* IBSS parameter IE Id */ | ||
857 | #define IBSS_PARA_IE_ID 6 | ||
858 | /* IBSS parameter IE length */ | ||
859 | #define IBSS_PARA_IE_LEN 2 | ||
860 | |||
861 | adhoc_start->ss_param_set.ibss_param_set.element_id = IBSS_PARA_IE_ID; | ||
862 | adhoc_start->ss_param_set.ibss_param_set.len = IBSS_PARA_IE_LEN; | ||
863 | adhoc_start->ss_param_set.ibss_param_set.atim_window | ||
864 | = cpu_to_le16(priv->atim_window); | ||
865 | memcpy(&bss_desc->ss_param_set, &adhoc_start->ss_param_set, | ||
866 | sizeof(union ieee_types_ss_param_set)); | ||
867 | |||
868 | /* Set Capability info */ | ||
869 | bss_desc->cap_info_bitmap |= WLAN_CAPABILITY_IBSS; | ||
870 | tmp_cap = le16_to_cpu(adhoc_start->cap_info_bitmap); | ||
871 | tmp_cap &= ~WLAN_CAPABILITY_ESS; | ||
872 | tmp_cap |= WLAN_CAPABILITY_IBSS; | ||
873 | |||
874 | /* Set up privacy in bss_desc */ | ||
875 | if (priv->sec_info.encryption_mode != MWIFIEX_ENCRYPTION_MODE_NONE) { | ||
876 | /* Ad-Hoc capability privacy on */ | ||
877 | dev_dbg(adapter->dev, | ||
878 | "info: ADHOC_S_CMD: wep_status set privacy to WEP\n"); | ||
879 | bss_desc->privacy = MWIFIEX_802_11_PRIV_FILTER_8021X_WEP; | ||
880 | tmp_cap |= WLAN_CAPABILITY_PRIVACY; | ||
881 | } else { | ||
882 | dev_dbg(adapter->dev, "info: ADHOC_S_CMD: wep_status NOT set," | ||
883 | " setting privacy to ACCEPT ALL\n"); | ||
884 | bss_desc->privacy = MWIFIEX_802_11_PRIV_FILTER_ACCEPT_ALL; | ||
885 | } | ||
886 | |||
887 | memset(adhoc_start->DataRate, 0, sizeof(adhoc_start->DataRate)); | ||
888 | mwifiex_get_active_data_rates(priv, adhoc_start->DataRate); | ||
889 | if ((adapter->adhoc_start_band & BAND_G) && | ||
890 | (priv->curr_pkt_filter & HostCmd_ACT_MAC_ADHOC_G_PROTECTION_ON)) { | ||
891 | ret = mwifiex_prepare_cmd(priv, HostCmd_CMD_MAC_CONTROL, | ||
892 | HostCmd_ACT_GEN_SET, | ||
893 | 0, NULL, &priv->curr_pkt_filter); | ||
894 | |||
895 | if (ret) { | ||
896 | dev_err(adapter->dev, | ||
897 | "ADHOC_S_CMD: G Protection config failed\n"); | ||
898 | return -1; | ||
899 | } | ||
900 | } | ||
901 | /* Find the last non zero */ | ||
902 | for (i = 0; i < sizeof(adhoc_start->DataRate) && | ||
903 | adhoc_start->DataRate[i]; | ||
904 | i++) | ||
905 | ; | ||
906 | |||
907 | priv->curr_bss_params.num_of_rates = i; | ||
908 | |||
909 | /* Copy the ad-hoc creating rates into Current BSS rate structure */ | ||
910 | memcpy(&priv->curr_bss_params.data_rates, | ||
911 | &adhoc_start->DataRate, priv->curr_bss_params.num_of_rates); | ||
912 | |||
913 | dev_dbg(adapter->dev, "info: ADHOC_S_CMD: rates=%02x %02x %02x %02x\n", | ||
914 | adhoc_start->DataRate[0], adhoc_start->DataRate[1], | ||
915 | adhoc_start->DataRate[2], adhoc_start->DataRate[3]); | ||
916 | |||
917 | dev_dbg(adapter->dev, "info: ADHOC_S_CMD: AD-HOC Start command is ready\n"); | ||
918 | |||
919 | if (IS_SUPPORT_MULTI_BANDS(adapter)) { | ||
920 | /* Append a channel TLV */ | ||
921 | chan_tlv = (struct mwifiex_ie_types_chan_list_param_set *) pos; | ||
922 | chan_tlv->header.type = cpu_to_le16(TLV_TYPE_CHANLIST); | ||
923 | chan_tlv->header.len = | ||
924 | cpu_to_le16(sizeof(struct mwifiex_chan_scan_param_set)); | ||
925 | |||
926 | memset(chan_tlv->chan_scan_param, 0x00, | ||
927 | sizeof(struct mwifiex_chan_scan_param_set)); | ||
928 | chan_tlv->chan_scan_param[0].chan_number = | ||
929 | (u8) priv->curr_bss_params.bss_descriptor.channel; | ||
930 | |||
931 | dev_dbg(adapter->dev, "info: ADHOC_S_CMD: TLV Chan = %d\n", | ||
932 | chan_tlv->chan_scan_param[0].chan_number); | ||
933 | |||
934 | chan_tlv->chan_scan_param[0].radio_type | ||
935 | = mwifiex_band_to_radio_type(priv->curr_bss_params.band); | ||
936 | if (adapter->adhoc_start_band & BAND_GN | ||
937 | || adapter->adhoc_start_band & BAND_AN) { | ||
938 | if (adapter->chan_offset == SEC_CHANNEL_ABOVE) | ||
939 | chan_tlv->chan_scan_param[0].radio_type |= | ||
940 | SECOND_CHANNEL_ABOVE; | ||
941 | else if (adapter->chan_offset == SEC_CHANNEL_BELOW) | ||
942 | chan_tlv->chan_scan_param[0].radio_type |= | ||
943 | SECOND_CHANNEL_BELOW; | ||
944 | } | ||
945 | dev_dbg(adapter->dev, "info: ADHOC_S_CMD: TLV Band = %d\n", | ||
946 | chan_tlv->chan_scan_param[0].radio_type); | ||
947 | pos += sizeof(chan_tlv->header) + | ||
948 | sizeof(struct mwifiex_chan_scan_param_set); | ||
949 | cmd_append_size += | ||
950 | sizeof(chan_tlv->header) + | ||
951 | sizeof(struct mwifiex_chan_scan_param_set); | ||
952 | } | ||
953 | |||
954 | /* Append vendor specific IE TLV */ | ||
955 | cmd_append_size += mwifiex_cmd_append_vsie_tlv(priv, | ||
956 | MWIFIEX_VSIE_MASK_ADHOC, &pos); | ||
957 | |||
958 | if (priv->sec_info.wpa_enabled) { | ||
959 | rsn_ie_len = mwifiex_append_rsn_ie_wpa_wpa2(priv, &pos); | ||
960 | if (rsn_ie_len == -1) | ||
961 | return -1; | ||
962 | cmd_append_size += rsn_ie_len; | ||
963 | } | ||
964 | |||
965 | if (adapter->adhoc_11n_enabled) { | ||
966 | { | ||
967 | ht_cap = (struct mwifiex_ie_types_htcap *) pos; | ||
968 | memset(ht_cap, 0, | ||
969 | sizeof(struct mwifiex_ie_types_htcap)); | ||
970 | ht_cap->header.type = | ||
971 | cpu_to_le16(WLAN_EID_HT_CAPABILITY); | ||
972 | ht_cap->header.len = | ||
973 | cpu_to_le16(sizeof(struct ieee80211_ht_cap)); | ||
974 | ht_cap_info = le16_to_cpu(ht_cap->ht_cap.cap_info); | ||
975 | |||
976 | SETHT_SHORTGI20(ht_cap_info); | ||
977 | if (adapter->chan_offset) { | ||
978 | SETHT_SHORTGI40(ht_cap_info); | ||
979 | SETHT_DSSSCCK40(ht_cap_info); | ||
980 | SETHT_SUPPCHANWIDTH(ht_cap_info); | ||
981 | SETHT_MCS32(ht_cap->ht_cap.mcs.rx_mask); | ||
982 | } | ||
983 | |||
984 | ht_cap->ht_cap.ampdu_params_info | ||
985 | = MAX_RX_AMPDU_SIZE_64K; | ||
986 | ht_cap->ht_cap.mcs.rx_mask[0] = 0xff; | ||
987 | pos += sizeof(struct mwifiex_ie_types_htcap); | ||
988 | cmd_append_size += | ||
989 | sizeof(struct mwifiex_ie_types_htcap); | ||
990 | } | ||
991 | { | ||
992 | ht_info = (struct mwifiex_ie_types_htinfo *) pos; | ||
993 | memset(ht_info, 0, | ||
994 | sizeof(struct mwifiex_ie_types_htinfo)); | ||
995 | ht_info->header.type = | ||
996 | cpu_to_le16(WLAN_EID_HT_INFORMATION); | ||
997 | ht_info->header.len = | ||
998 | cpu_to_le16(sizeof(struct ieee80211_ht_info)); | ||
999 | ht_info->ht_info.control_chan = | ||
1000 | (u8) priv->curr_bss_params.bss_descriptor. | ||
1001 | channel; | ||
1002 | if (adapter->chan_offset) { | ||
1003 | ht_info->ht_info.ht_param = | ||
1004 | adapter->chan_offset; | ||
1005 | SET_CHANWIDTH40(ht_info->ht_info.ht_param); | ||
1006 | } | ||
1007 | ht_info->ht_info.operation_mode = | ||
1008 | cpu_to_le16(NON_GREENFIELD_STAS); | ||
1009 | ht_info->ht_info.basic_set[0] = 0xff; | ||
1010 | pos += sizeof(struct mwifiex_ie_types_htinfo); | ||
1011 | cmd_append_size += | ||
1012 | sizeof(struct mwifiex_ie_types_htinfo); | ||
1013 | } | ||
1014 | } | ||
1015 | |||
1016 | cmd->size = cpu_to_le16((u16) | ||
1017 | (sizeof(struct host_cmd_ds_802_11_ad_hoc_start) | ||
1018 | + S_DS_GEN + cmd_append_size)); | ||
1019 | |||
1020 | if (adapter->adhoc_start_band == BAND_B) | ||
1021 | SHORT_SLOT_TIME_DISABLED(tmp_cap); | ||
1022 | else | ||
1023 | SHORT_SLOT_TIME_ENABLED(tmp_cap); | ||
1024 | |||
1025 | adhoc_start->cap_info_bitmap = cpu_to_le16(tmp_cap); | ||
1026 | |||
1027 | return 0; | ||
1028 | } | ||
1029 | |||
1030 | /* | ||
1031 | * This function prepares command for ad-hoc join. | ||
1032 | * | ||
1033 | * Most of the parameters are set up by copying from the target BSS descriptor | ||
1034 | * from the scan response. | ||
1035 | * | ||
1036 | * In addition, the following TLVs are added - | ||
1037 | * - Channel TLV | ||
1038 | * - Vendor specific IE | ||
1039 | * - WPA/WPA2 IE | ||
1040 | * - 11n IE | ||
1041 | * | ||
1042 | * Preparation also includes - | ||
1043 | * - Setting command ID and proper size | ||
1044 | * - Ensuring correct endian-ness | ||
1045 | */ | ||
1046 | int | ||
1047 | mwifiex_cmd_802_11_ad_hoc_join(struct mwifiex_private *priv, | ||
1048 | struct host_cmd_ds_command *cmd, void *data_buf) | ||
1049 | { | ||
1050 | int ret = 0, rsn_ie_len = 0; | ||
1051 | struct host_cmd_ds_802_11_ad_hoc_join *adhoc_join = | ||
1052 | &cmd->params.adhoc_join; | ||
1053 | struct mwifiex_bssdescriptor *bss_desc = | ||
1054 | (struct mwifiex_bssdescriptor *) data_buf; | ||
1055 | struct mwifiex_ie_types_chan_list_param_set *chan_tlv; | ||
1056 | u32 cmd_append_size = 0; | ||
1057 | u16 tmp_cap; | ||
1058 | u32 i, rates_size = 0; | ||
1059 | u16 curr_pkt_filter; | ||
1060 | u8 *pos = | ||
1061 | (u8 *) adhoc_join + | ||
1062 | sizeof(struct host_cmd_ds_802_11_ad_hoc_join); | ||
1063 | |||
1064 | /* Use G protection */ | ||
1065 | #define USE_G_PROTECTION 0x02 | ||
1066 | if (bss_desc->erp_flags & USE_G_PROTECTION) { | ||
1067 | curr_pkt_filter = | ||
1068 | priv-> | ||
1069 | curr_pkt_filter | HostCmd_ACT_MAC_ADHOC_G_PROTECTION_ON; | ||
1070 | |||
1071 | ret = mwifiex_prepare_cmd(priv, HostCmd_CMD_MAC_CONTROL, | ||
1072 | HostCmd_ACT_GEN_SET, 0, NULL, | ||
1073 | &curr_pkt_filter); | ||
1074 | if (ret) { | ||
1075 | dev_err(priv->adapter->dev, | ||
1076 | "ADHOC_J_CMD: G Protection config failed\n"); | ||
1077 | return -1; | ||
1078 | } | ||
1079 | } | ||
1080 | |||
1081 | priv->attempted_bss_desc = bss_desc; | ||
1082 | |||
1083 | cmd->command = cpu_to_le16(HostCmd_CMD_802_11_AD_HOC_JOIN); | ||
1084 | |||
1085 | adhoc_join->bss_descriptor.bss_mode = HostCmd_BSS_MODE_IBSS; | ||
1086 | |||
1087 | adhoc_join->bss_descriptor.beacon_period | ||
1088 | = cpu_to_le16(bss_desc->beacon_period); | ||
1089 | |||
1090 | memcpy(&adhoc_join->bss_descriptor.bssid, | ||
1091 | &bss_desc->mac_address, ETH_ALEN); | ||
1092 | |||
1093 | memcpy(&adhoc_join->bss_descriptor.ssid, | ||
1094 | &bss_desc->ssid.ssid, bss_desc->ssid.ssid_len); | ||
1095 | |||
1096 | memcpy(&adhoc_join->bss_descriptor.phy_param_set, | ||
1097 | &bss_desc->phy_param_set, | ||
1098 | sizeof(union ieee_types_phy_param_set)); | ||
1099 | |||
1100 | memcpy(&adhoc_join->bss_descriptor.ss_param_set, | ||
1101 | &bss_desc->ss_param_set, sizeof(union ieee_types_ss_param_set)); | ||
1102 | |||
1103 | tmp_cap = bss_desc->cap_info_bitmap; | ||
1104 | |||
1105 | tmp_cap &= CAPINFO_MASK; | ||
1106 | |||
1107 | dev_dbg(priv->adapter->dev, "info: ADHOC_J_CMD: tmp_cap=%4X" | ||
1108 | " CAPINFO_MASK=%4lX\n", tmp_cap, CAPINFO_MASK); | ||
1109 | |||
1110 | /* Information on BSSID descriptor passed to FW */ | ||
1111 | dev_dbg(priv->adapter->dev, "info: ADHOC_J_CMD: BSSID = %pM, SSID = %s\n", | ||
1112 | adhoc_join->bss_descriptor.bssid, | ||
1113 | adhoc_join->bss_descriptor.ssid); | ||
1114 | |||
1115 | for (i = 0; bss_desc->supported_rates[i] && | ||
1116 | i < MWIFIEX_SUPPORTED_RATES; | ||
1117 | i++) | ||
1118 | ; | ||
1119 | rates_size = i; | ||
1120 | |||
1121 | /* Copy Data Rates from the Rates recorded in scan response */ | ||
1122 | memset(adhoc_join->bss_descriptor.data_rates, 0, | ||
1123 | sizeof(adhoc_join->bss_descriptor.data_rates)); | ||
1124 | memcpy(adhoc_join->bss_descriptor.data_rates, | ||
1125 | bss_desc->supported_rates, rates_size); | ||
1126 | |||
1127 | /* Copy the adhoc join rates into Current BSS state structure */ | ||
1128 | priv->curr_bss_params.num_of_rates = rates_size; | ||
1129 | memcpy(&priv->curr_bss_params.data_rates, bss_desc->supported_rates, | ||
1130 | rates_size); | ||
1131 | |||
1132 | /* Copy the channel information */ | ||
1133 | priv->curr_bss_params.bss_descriptor.channel = bss_desc->channel; | ||
1134 | priv->curr_bss_params.band = (u8) bss_desc->bss_band; | ||
1135 | |||
1136 | if (priv->sec_info.wep_status == MWIFIEX_802_11_WEP_ENABLED | ||
1137 | || priv->sec_info.wpa_enabled) | ||
1138 | tmp_cap |= WLAN_CAPABILITY_PRIVACY; | ||
1139 | |||
1140 | if (IS_SUPPORT_MULTI_BANDS(priv->adapter)) { | ||
1141 | /* Append a channel TLV */ | ||
1142 | chan_tlv = (struct mwifiex_ie_types_chan_list_param_set *) pos; | ||
1143 | chan_tlv->header.type = cpu_to_le16(TLV_TYPE_CHANLIST); | ||
1144 | chan_tlv->header.len = | ||
1145 | cpu_to_le16(sizeof(struct mwifiex_chan_scan_param_set)); | ||
1146 | |||
1147 | memset(chan_tlv->chan_scan_param, 0x00, | ||
1148 | sizeof(struct mwifiex_chan_scan_param_set)); | ||
1149 | chan_tlv->chan_scan_param[0].chan_number = | ||
1150 | (bss_desc->phy_param_set.ds_param_set.current_chan); | ||
1151 | dev_dbg(priv->adapter->dev, "info: ADHOC_J_CMD: TLV Chan = %d\n", | ||
1152 | chan_tlv->chan_scan_param[0].chan_number); | ||
1153 | |||
1154 | chan_tlv->chan_scan_param[0].radio_type = | ||
1155 | mwifiex_band_to_radio_type((u8) bss_desc->bss_band); | ||
1156 | |||
1157 | dev_dbg(priv->adapter->dev, "info: ADHOC_J_CMD: TLV Band = %d\n", | ||
1158 | chan_tlv->chan_scan_param[0].radio_type); | ||
1159 | pos += sizeof(chan_tlv->header) + | ||
1160 | sizeof(struct mwifiex_chan_scan_param_set); | ||
1161 | cmd_append_size += sizeof(chan_tlv->header) + | ||
1162 | sizeof(struct mwifiex_chan_scan_param_set); | ||
1163 | } | ||
1164 | |||
1165 | if (priv->sec_info.wpa_enabled) | ||
1166 | rsn_ie_len = mwifiex_append_rsn_ie_wpa_wpa2(priv, &pos); | ||
1167 | if (rsn_ie_len == -1) | ||
1168 | return -1; | ||
1169 | cmd_append_size += rsn_ie_len; | ||
1170 | |||
1171 | if (ISSUPP_11NENABLED(priv->adapter->fw_cap_info)) | ||
1172 | cmd_append_size += mwifiex_cmd_append_11n_tlv(priv, | ||
1173 | bss_desc, &pos); | ||
1174 | |||
1175 | /* Append vendor specific IE TLV */ | ||
1176 | cmd_append_size += mwifiex_cmd_append_vsie_tlv(priv, | ||
1177 | MWIFIEX_VSIE_MASK_ADHOC, &pos); | ||
1178 | |||
1179 | cmd->size = cpu_to_le16((u16) | ||
1180 | (sizeof(struct host_cmd_ds_802_11_ad_hoc_join) | ||
1181 | + S_DS_GEN + cmd_append_size)); | ||
1182 | |||
1183 | adhoc_join->bss_descriptor.cap_info_bitmap = cpu_to_le16(tmp_cap); | ||
1184 | |||
1185 | return ret; | ||
1186 | } | ||
1187 | |||
1188 | /* | ||
1189 | * This function handles the command response of ad-hoc start and | ||
1190 | * ad-hoc join. | ||
1191 | * | ||
1192 | * The function generates a device-connected event to notify | ||
1193 | * the applications, in case of successful ad-hoc start/join, and | ||
1194 | * saves the beacon buffer. | ||
1195 | */ | ||
1196 | int mwifiex_ret_802_11_ad_hoc(struct mwifiex_private *priv, | ||
1197 | struct host_cmd_ds_command *resp, void *wq_buf) | ||
1198 | { | ||
1199 | int ret = 0; | ||
1200 | struct mwifiex_wait_queue *wait_queue = | ||
1201 | (struct mwifiex_wait_queue *) wq_buf; | ||
1202 | struct host_cmd_ds_802_11_ad_hoc_result *adhoc_result; | ||
1203 | struct mwifiex_bssdescriptor *bss_desc; | ||
1204 | u16 command = le16_to_cpu(resp->command); | ||
1205 | u16 result = le16_to_cpu(resp->result); | ||
1206 | |||
1207 | adhoc_result = &resp->params.adhoc_result; | ||
1208 | |||
1209 | bss_desc = priv->attempted_bss_desc; | ||
1210 | |||
1211 | /* Join result code 0 --> SUCCESS */ | ||
1212 | if (result) { | ||
1213 | dev_err(priv->adapter->dev, "ADHOC_RESP: failed\n"); | ||
1214 | if (priv->media_connected) | ||
1215 | mwifiex_reset_connect_state(priv); | ||
1216 | |||
1217 | memset(&priv->curr_bss_params.bss_descriptor, | ||
1218 | 0x00, sizeof(struct mwifiex_bssdescriptor)); | ||
1219 | |||
1220 | ret = -1; | ||
1221 | goto done; | ||
1222 | } | ||
1223 | |||
1224 | /* Send a Media Connected event, according to the Spec */ | ||
1225 | priv->media_connected = true; | ||
1226 | |||
1227 | if (command == HostCmd_CMD_802_11_AD_HOC_START) { | ||
1228 | dev_dbg(priv->adapter->dev, "info: ADHOC_S_RESP %s\n", | ||
1229 | bss_desc->ssid.ssid); | ||
1230 | |||
1231 | /* Update the created network descriptor with the new BSSID */ | ||
1232 | memcpy(bss_desc->mac_address, | ||
1233 | adhoc_result->bssid, ETH_ALEN); | ||
1234 | |||
1235 | priv->adhoc_state = ADHOC_STARTED; | ||
1236 | } else { | ||
1237 | /* | ||
1238 | * Now the join cmd should be successful. | ||
1239 | * If BSSID has changed use SSID to compare instead of BSSID | ||
1240 | */ | ||
1241 | dev_dbg(priv->adapter->dev, "info: ADHOC_J_RESP %s\n", | ||
1242 | bss_desc->ssid.ssid); | ||
1243 | |||
1244 | /* | ||
1245 | * Make a copy of current BSSID descriptor, only needed for | ||
1246 | * join since the current descriptor is already being used | ||
1247 | * for adhoc start | ||
1248 | */ | ||
1249 | memcpy(&priv->curr_bss_params.bss_descriptor, | ||
1250 | bss_desc, sizeof(struct mwifiex_bssdescriptor)); | ||
1251 | |||
1252 | priv->adhoc_state = ADHOC_JOINED; | ||
1253 | } | ||
1254 | |||
1255 | dev_dbg(priv->adapter->dev, "info: ADHOC_RESP: channel = %d\n", | ||
1256 | priv->adhoc_channel); | ||
1257 | dev_dbg(priv->adapter->dev, "info: ADHOC_RESP: BSSID = %pM\n", | ||
1258 | priv->curr_bss_params.bss_descriptor.mac_address); | ||
1259 | |||
1260 | if (!netif_carrier_ok(priv->netdev)) | ||
1261 | netif_carrier_on(priv->netdev); | ||
1262 | if (netif_queue_stopped(priv->netdev)) | ||
1263 | netif_wake_queue(priv->netdev); | ||
1264 | |||
1265 | mwifiex_save_curr_bcn(priv); | ||
1266 | |||
1267 | done: | ||
1268 | /* Need to indicate IOCTL complete */ | ||
1269 | if (wait_queue) { | ||
1270 | if (ret) | ||
1271 | wait_queue->status = MWIFIEX_ERROR_ASSOC_FAIL; | ||
1272 | else | ||
1273 | wait_queue->status = MWIFIEX_ERROR_NO_ERROR; | ||
1274 | |||
1275 | } | ||
1276 | |||
1277 | return ret; | ||
1278 | } | ||
1279 | |||
1280 | /* | ||
1281 | * This function associates to a specific BSS discovered in a scan. | ||
1282 | * | ||
1283 | * It clears any past association response stored for application | ||
1284 | * retrieval and calls the command preparation routine to send the | ||
1285 | * command to firmware. | ||
1286 | */ | ||
1287 | int mwifiex_associate(struct mwifiex_private *priv, | ||
1288 | void *wait_queue, struct mwifiex_bssdescriptor *bss_desc) | ||
1289 | { | ||
1290 | int ret = 0; | ||
1291 | u8 current_bssid[ETH_ALEN]; | ||
1292 | |||
1293 | /* Return error if the adapter or table entry is not marked as infra */ | ||
1294 | if ((priv->bss_mode != MWIFIEX_BSS_MODE_INFRA) || | ||
1295 | (bss_desc->bss_mode != MWIFIEX_BSS_MODE_INFRA)) | ||
1296 | return -1; | ||
1297 | |||
1298 | memcpy(¤t_bssid, | ||
1299 | &priv->curr_bss_params.bss_descriptor.mac_address, | ||
1300 | sizeof(current_bssid)); | ||
1301 | |||
1302 | /* Clear any past association response stored for application | ||
1303 | retrieval */ | ||
1304 | priv->assoc_rsp_size = 0; | ||
1305 | |||
1306 | ret = mwifiex_prepare_cmd(priv, HostCmd_CMD_802_11_ASSOCIATE, | ||
1307 | HostCmd_ACT_GEN_SET, 0, wait_queue, | ||
1308 | bss_desc); | ||
1309 | |||
1310 | return ret; | ||
1311 | } | ||
1312 | |||
1313 | /* | ||
1314 | * This function starts an ad-hoc network. | ||
1315 | * | ||
1316 | * It calls the command preparation routine to send the command to firmware. | ||
1317 | */ | ||
1318 | int | ||
1319 | mwifiex_adhoc_start(struct mwifiex_private *priv, | ||
1320 | void *wait_queue, struct mwifiex_802_11_ssid *adhoc_ssid) | ||
1321 | { | ||
1322 | int ret = 0; | ||
1323 | |||
1324 | dev_dbg(priv->adapter->dev, "info: Adhoc Channel = %d\n", | ||
1325 | priv->adhoc_channel); | ||
1326 | dev_dbg(priv->adapter->dev, "info: curr_bss_params.channel = %d\n", | ||
1327 | priv->curr_bss_params.bss_descriptor.channel); | ||
1328 | dev_dbg(priv->adapter->dev, "info: curr_bss_params.band = %d\n", | ||
1329 | priv->curr_bss_params.band); | ||
1330 | |||
1331 | ret = mwifiex_prepare_cmd(priv, HostCmd_CMD_802_11_AD_HOC_START, | ||
1332 | HostCmd_ACT_GEN_SET, 0, wait_queue, | ||
1333 | adhoc_ssid); | ||
1334 | |||
1335 | return ret; | ||
1336 | } | ||
1337 | |||
1338 | /* | ||
1339 | * This function joins an ad-hoc network found in a previous scan. | ||
1340 | * | ||
1341 | * It calls the command preparation routine to send the command to firmware, | ||
1342 | * if already not connected to the requested SSID. | ||
1343 | */ | ||
1344 | int mwifiex_adhoc_join(struct mwifiex_private *priv, | ||
1345 | void *wait_queue, struct mwifiex_bssdescriptor *bss_desc) | ||
1346 | { | ||
1347 | int ret = 0; | ||
1348 | |||
1349 | dev_dbg(priv->adapter->dev, "info: adhoc join: curr_bss ssid =%s\n", | ||
1350 | priv->curr_bss_params.bss_descriptor.ssid.ssid); | ||
1351 | dev_dbg(priv->adapter->dev, "info: adhoc join: curr_bss ssid_len =%u\n", | ||
1352 | priv->curr_bss_params.bss_descriptor.ssid.ssid_len); | ||
1353 | dev_dbg(priv->adapter->dev, "info: adhoc join: ssid =%s\n", | ||
1354 | bss_desc->ssid.ssid); | ||
1355 | dev_dbg(priv->adapter->dev, "info: adhoc join: ssid_len =%u\n", | ||
1356 | bss_desc->ssid.ssid_len); | ||
1357 | |||
1358 | /* Check if the requested SSID is already joined */ | ||
1359 | if (priv->curr_bss_params.bss_descriptor.ssid.ssid_len && | ||
1360 | !mwifiex_ssid_cmp(&bss_desc->ssid, | ||
1361 | &priv->curr_bss_params.bss_descriptor.ssid) && | ||
1362 | (priv->curr_bss_params.bss_descriptor.bss_mode == | ||
1363 | MWIFIEX_BSS_MODE_IBSS)) { | ||
1364 | dev_dbg(priv->adapter->dev, "info: ADHOC_J_CMD: new ad-hoc SSID" | ||
1365 | " is the same as current; not attempting to re-join\n"); | ||
1366 | return -1; | ||
1367 | } | ||
1368 | |||
1369 | dev_dbg(priv->adapter->dev, "info: curr_bss_params.channel = %d\n", | ||
1370 | priv->curr_bss_params.bss_descriptor.channel); | ||
1371 | dev_dbg(priv->adapter->dev, "info: curr_bss_params.band = %c\n", | ||
1372 | priv->curr_bss_params.band); | ||
1373 | |||
1374 | ret = mwifiex_prepare_cmd(priv, HostCmd_CMD_802_11_AD_HOC_JOIN, | ||
1375 | HostCmd_ACT_GEN_SET, 0, wait_queue, | ||
1376 | bss_desc); | ||
1377 | |||
1378 | return ret; | ||
1379 | } | ||
1380 | |||
1381 | /* | ||
1382 | * This function deauthenticates/disconnects from infra network by sending | ||
1383 | * deauthentication request. | ||
1384 | */ | ||
1385 | static int mwifiex_deauthenticate_infra(struct mwifiex_private *priv, | ||
1386 | struct mwifiex_wait_queue *wait, | ||
1387 | u8 *mac) | ||
1388 | { | ||
1389 | u8 mac_address[ETH_ALEN]; | ||
1390 | int ret = 0; | ||
1391 | u8 zero_mac[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 }; | ||
1392 | |||
1393 | if (mac) { | ||
1394 | if (!memcmp(mac, zero_mac, sizeof(zero_mac))) | ||
1395 | memcpy((u8 *) &mac_address, | ||
1396 | (u8 *) &priv->curr_bss_params.bss_descriptor. | ||
1397 | mac_address, ETH_ALEN); | ||
1398 | else | ||
1399 | memcpy((u8 *) &mac_address, (u8 *) mac, ETH_ALEN); | ||
1400 | } else { | ||
1401 | memcpy((u8 *) &mac_address, (u8 *) &priv->curr_bss_params. | ||
1402 | bss_descriptor.mac_address, ETH_ALEN); | ||
1403 | } | ||
1404 | |||
1405 | ret = mwifiex_prepare_cmd(priv, HostCmd_CMD_802_11_DEAUTHENTICATE, | ||
1406 | HostCmd_ACT_GEN_SET, 0, wait, &mac_address); | ||
1407 | |||
1408 | if (!ret && wait) | ||
1409 | ret = -EINPROGRESS; | ||
1410 | |||
1411 | return ret; | ||
1412 | } | ||
1413 | |||
1414 | /* | ||
1415 | * This function deauthenticates/disconnects from a BSS. | ||
1416 | * | ||
1417 | * In case of infra made, it sends deauthentication request, and | ||
1418 | * in case of ad-hoc mode, a stop network request is sent to the firmware. | ||
1419 | */ | ||
1420 | int mwifiex_deauthenticate(struct mwifiex_private *priv, | ||
1421 | struct mwifiex_wait_queue *wait, u8 *mac) | ||
1422 | { | ||
1423 | int ret = 0; | ||
1424 | |||
1425 | if (priv->media_connected) { | ||
1426 | if (priv->bss_mode == MWIFIEX_BSS_MODE_INFRA) { | ||
1427 | ret = mwifiex_deauthenticate_infra(priv, wait, mac); | ||
1428 | } else if (priv->bss_mode == MWIFIEX_BSS_MODE_IBSS) { | ||
1429 | ret = mwifiex_prepare_cmd(priv, | ||
1430 | HostCmd_CMD_802_11_AD_HOC_STOP, | ||
1431 | HostCmd_ACT_GEN_SET, 0, wait, NULL); | ||
1432 | |||
1433 | if (!ret && wait) | ||
1434 | ret = -EINPROGRESS; | ||
1435 | } | ||
1436 | } | ||
1437 | |||
1438 | return ret; | ||
1439 | } | ||
1440 | |||
1441 | /* | ||
1442 | * This function converts band to radio type used in channel TLV. | ||
1443 | */ | ||
1444 | u8 | ||
1445 | mwifiex_band_to_radio_type(u8 band) | ||
1446 | { | ||
1447 | u8 ret_radio_type; | ||
1448 | |||
1449 | switch (band) { | ||
1450 | case BAND_A: | ||
1451 | case BAND_AN: | ||
1452 | case BAND_A | BAND_AN: | ||
1453 | ret_radio_type = HostCmd_SCAN_RADIO_TYPE_A; | ||
1454 | break; | ||
1455 | case BAND_B: | ||
1456 | case BAND_G: | ||
1457 | case BAND_B | BAND_G: | ||
1458 | default: | ||
1459 | ret_radio_type = HostCmd_SCAN_RADIO_TYPE_BG; | ||
1460 | break; | ||
1461 | } | ||
1462 | |||
1463 | return ret_radio_type; | ||
1464 | } | ||
diff --git a/drivers/net/wireless/mwifiex/main.c b/drivers/net/wireless/mwifiex/main.c new file mode 100644 index 000000000000..ed89ca41a902 --- /dev/null +++ b/drivers/net/wireless/mwifiex/main.c | |||
@@ -0,0 +1,1102 @@ | |||
1 | /* | ||
2 | * Marvell Wireless LAN device driver: major functions | ||
3 | * | ||
4 | * Copyright (C) 2011, Marvell International Ltd. | ||
5 | * | ||
6 | * This software file (the "File") is distributed by Marvell International | ||
7 | * Ltd. under the terms of the GNU General Public License Version 2, June 1991 | ||
8 | * (the "License"). You may use, redistribute and/or modify this File in | ||
9 | * accordance with the terms and conditions of the License, a copy of which | ||
10 | * is available by writing to the Free Software Foundation, Inc., | ||
11 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the | ||
12 | * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. | ||
13 | * | ||
14 | * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE | ||
15 | * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE | ||
16 | * ARE EXPRESSLY DISCLAIMED. The License provides additional details about | ||
17 | * this warranty disclaimer. | ||
18 | */ | ||
19 | |||
20 | #include "main.h" | ||
21 | #include "wmm.h" | ||
22 | #include "cfg80211.h" | ||
23 | #include "11n.h" | ||
24 | |||
25 | #define VERSION "1.0" | ||
26 | |||
27 | const char driver_version[] = "mwifiex " VERSION " (%s) "; | ||
28 | |||
29 | struct mwifiex_adapter *g_adapter; | ||
30 | EXPORT_SYMBOL_GPL(g_adapter); | ||
31 | |||
32 | static struct mwifiex_bss_attr mwifiex_bss_sta[] = { | ||
33 | {MWIFIEX_BSS_TYPE_STA, MWIFIEX_DATA_FRAME_TYPE_ETH_II, true, 0, 0}, | ||
34 | }; | ||
35 | |||
36 | static int drv_mode = DRV_MODE_STA; | ||
37 | |||
38 | static char fw_name[32] = DEFAULT_FW_NAME; | ||
39 | |||
40 | /* Supported drv_mode table */ | ||
41 | static struct mwifiex_drv_mode mwifiex_drv_mode_tbl[] = { | ||
42 | { | ||
43 | /* drv_mode */ | ||
44 | .drv_mode = DRV_MODE_STA, | ||
45 | /* intf number */ | ||
46 | .intf_num = ARRAY_SIZE(mwifiex_bss_sta), | ||
47 | /* bss_attr */ | ||
48 | .bss_attr = mwifiex_bss_sta, | ||
49 | } | ||
50 | , | ||
51 | }; | ||
52 | |||
53 | /* | ||
54 | * This function registers the device and performs all the necessary | ||
55 | * initializations. | ||
56 | * | ||
57 | * The following initialization operations are performed - | ||
58 | * - Allocate adapter structure | ||
59 | * - Save interface specific operations table in adapter | ||
60 | * - Call interface specific initialization routine | ||
61 | * - Allocate private structures | ||
62 | * - Set default adapter structure parameters | ||
63 | * - Initialize locks | ||
64 | * | ||
65 | * In case of any errors during inittialization, this function also ensures | ||
66 | * proper cleanup before exiting. | ||
67 | */ | ||
68 | static int mwifiex_register(void *card, struct mwifiex_if_ops *if_ops, | ||
69 | struct mwifiex_device *mdevice, void **padapter) | ||
70 | { | ||
71 | int ret = 0; | ||
72 | struct mwifiex_adapter *adapter = NULL; | ||
73 | u8 i = 0; | ||
74 | |||
75 | adapter = kzalloc(sizeof(struct mwifiex_adapter), GFP_KERNEL); | ||
76 | /* Allocate memory for adapter structure */ | ||
77 | if (!adapter) | ||
78 | return -1; | ||
79 | |||
80 | g_adapter = adapter; | ||
81 | adapter->card = card; | ||
82 | |||
83 | /* Save interface specific operations in adapter */ | ||
84 | memmove(&adapter->if_ops, if_ops, sizeof(struct mwifiex_if_ops)); | ||
85 | |||
86 | /* card specific initialization has been deferred until now .. */ | ||
87 | ret = adapter->if_ops.init_if(adapter); | ||
88 | if (ret) | ||
89 | goto error; | ||
90 | |||
91 | adapter->priv_num = 0; | ||
92 | for (i = 0; i < MWIFIEX_MAX_BSS_NUM; i++) { | ||
93 | adapter->priv[i] = NULL; | ||
94 | |||
95 | if (!mdevice->bss_attr[i].active) | ||
96 | continue; | ||
97 | |||
98 | /* For valid bss_attr, | ||
99 | allocate memory for private structure */ | ||
100 | adapter->priv[i] = kzalloc(sizeof(struct mwifiex_private), | ||
101 | GFP_KERNEL); | ||
102 | if (!adapter->priv[i]) { | ||
103 | dev_err(adapter->dev, "%s: failed to alloc priv[%d]\n", | ||
104 | __func__, i); | ||
105 | goto error; | ||
106 | } | ||
107 | |||
108 | adapter->priv_num++; | ||
109 | memset(adapter->priv[i], 0, | ||
110 | sizeof(struct mwifiex_private)); | ||
111 | adapter->priv[i]->adapter = adapter; | ||
112 | /* Save bss_type, frame_type & bss_priority */ | ||
113 | adapter->priv[i]->bss_type = (u8) mdevice->bss_attr[i].bss_type; | ||
114 | adapter->priv[i]->frame_type = | ||
115 | (u8) mdevice->bss_attr[i].frame_type; | ||
116 | adapter->priv[i]->bss_priority = | ||
117 | (u8) mdevice->bss_attr[i].bss_priority; | ||
118 | if (mdevice->bss_attr[i].bss_type == MWIFIEX_BSS_TYPE_STA) | ||
119 | adapter->priv[i]->bss_role = MWIFIEX_BSS_ROLE_STA; | ||
120 | else if (mdevice->bss_attr[i].bss_type == MWIFIEX_BSS_TYPE_UAP) | ||
121 | adapter->priv[i]->bss_role = MWIFIEX_BSS_ROLE_UAP; | ||
122 | |||
123 | /* Save bss_index & bss_num */ | ||
124 | adapter->priv[i]->bss_index = i; | ||
125 | adapter->priv[i]->bss_num = mdevice->bss_attr[i].bss_num; | ||
126 | } | ||
127 | |||
128 | /* Initialize lock variables */ | ||
129 | if (mwifiex_init_lock_list(adapter)) | ||
130 | goto error; | ||
131 | |||
132 | init_timer(&adapter->cmd_timer); | ||
133 | adapter->cmd_timer.function = mwifiex_cmd_timeout_func; | ||
134 | adapter->cmd_timer.data = (unsigned long) adapter; | ||
135 | |||
136 | /* Return pointer of struct mwifiex_adapter */ | ||
137 | *padapter = adapter; | ||
138 | return 0; | ||
139 | |||
140 | error: | ||
141 | dev_dbg(adapter->dev, "info: leave mwifiex_register with error\n"); | ||
142 | |||
143 | /* Free lock variables */ | ||
144 | mwifiex_free_lock_list(adapter); | ||
145 | for (i = 0; i < MWIFIEX_MAX_BSS_NUM; i++) | ||
146 | kfree(adapter->priv[i]); | ||
147 | kfree(adapter); | ||
148 | |||
149 | return -1; | ||
150 | } | ||
151 | |||
152 | /* | ||
153 | * This function unregisters the device and performs all the necessary | ||
154 | * cleanups. | ||
155 | * | ||
156 | * The following cleanup operations are performed - | ||
157 | * - Free the timers | ||
158 | * - Free beacon buffers | ||
159 | * - Free private structures | ||
160 | * - Free adapter structure | ||
161 | */ | ||
162 | static int mwifiex_unregister(struct mwifiex_adapter *adapter) | ||
163 | { | ||
164 | s32 i = 0; | ||
165 | |||
166 | del_timer(&adapter->cmd_timer); | ||
167 | |||
168 | /* Free private structures */ | ||
169 | for (i = 0; i < adapter->priv_num; i++) { | ||
170 | if (adapter->priv[i]) { | ||
171 | mwifiex_free_curr_bcn(adapter->priv[i]); | ||
172 | kfree(adapter->priv[i]); | ||
173 | } | ||
174 | } | ||
175 | |||
176 | kfree(adapter); | ||
177 | return 0; | ||
178 | } | ||
179 | |||
180 | /* | ||
181 | * The main process. | ||
182 | * | ||
183 | * This function is the main procedure of the driver and handles various driver | ||
184 | * operations. It runs in a loop and provides the core functionalities. | ||
185 | * | ||
186 | * The main responsibilities of this function are - | ||
187 | * - Ensure concurrency control | ||
188 | * - Handle pending interrupts and call interrupt handlers | ||
189 | * - Wake up the card if required | ||
190 | * - Handle command responses and call response handlers | ||
191 | * - Handle events and call event handlers | ||
192 | * - Execute pending commands | ||
193 | * - Transmit pending data packets | ||
194 | */ | ||
195 | int mwifiex_main_process(struct mwifiex_adapter *adapter) | ||
196 | { | ||
197 | int ret = 0; | ||
198 | unsigned long flags; | ||
199 | |||
200 | spin_lock_irqsave(&adapter->main_proc_lock, flags); | ||
201 | |||
202 | /* Check if already processing */ | ||
203 | if (adapter->mwifiex_processing) { | ||
204 | spin_unlock_irqrestore(&adapter->main_proc_lock, flags); | ||
205 | goto exit_main_proc; | ||
206 | } else { | ||
207 | adapter->mwifiex_processing = true; | ||
208 | spin_unlock_irqrestore(&adapter->main_proc_lock, flags); | ||
209 | } | ||
210 | process_start: | ||
211 | do { | ||
212 | if ((adapter->hw_status == MWIFIEX_HW_STATUS_CLOSING) || | ||
213 | (adapter->hw_status == MWIFIEX_HW_STATUS_NOT_READY)) | ||
214 | break; | ||
215 | |||
216 | /* Handle pending interrupt if any */ | ||
217 | if (adapter->int_status) { | ||
218 | if (adapter->hs_activated) | ||
219 | mwifiex_process_hs_config(adapter); | ||
220 | adapter->if_ops.process_int_status(adapter); | ||
221 | } | ||
222 | |||
223 | /* Need to wake up the card ? */ | ||
224 | if ((adapter->ps_state == PS_STATE_SLEEP) && | ||
225 | (adapter->pm_wakeup_card_req && | ||
226 | !adapter->pm_wakeup_fw_try) && | ||
227 | (is_command_pending(adapter) | ||
228 | || !mwifiex_wmm_lists_empty(adapter))) { | ||
229 | adapter->pm_wakeup_fw_try = true; | ||
230 | adapter->if_ops.wakeup(adapter); | ||
231 | continue; | ||
232 | } | ||
233 | if (IS_CARD_RX_RCVD(adapter)) { | ||
234 | adapter->pm_wakeup_fw_try = false; | ||
235 | if (adapter->ps_state == PS_STATE_SLEEP) | ||
236 | adapter->ps_state = PS_STATE_AWAKE; | ||
237 | } else { | ||
238 | /* We have tried to wakeup the card already */ | ||
239 | if (adapter->pm_wakeup_fw_try) | ||
240 | break; | ||
241 | if (adapter->ps_state != PS_STATE_AWAKE || | ||
242 | adapter->tx_lock_flag) | ||
243 | break; | ||
244 | |||
245 | if (adapter->scan_processing || adapter->data_sent | ||
246 | || mwifiex_wmm_lists_empty(adapter)) { | ||
247 | if (adapter->cmd_sent || adapter->curr_cmd | ||
248 | || (!is_command_pending(adapter))) | ||
249 | break; | ||
250 | } | ||
251 | } | ||
252 | |||
253 | /* Check for Cmd Resp */ | ||
254 | if (adapter->cmd_resp_received) { | ||
255 | adapter->cmd_resp_received = false; | ||
256 | mwifiex_process_cmdresp(adapter); | ||
257 | |||
258 | /* call mwifiex back when init_fw is done */ | ||
259 | if (adapter->hw_status == MWIFIEX_HW_STATUS_INIT_DONE) { | ||
260 | adapter->hw_status = MWIFIEX_HW_STATUS_READY; | ||
261 | mwifiex_init_fw_complete(adapter); | ||
262 | } | ||
263 | } | ||
264 | |||
265 | /* Check for event */ | ||
266 | if (adapter->event_received) { | ||
267 | adapter->event_received = false; | ||
268 | mwifiex_process_event(adapter); | ||
269 | } | ||
270 | |||
271 | /* Check if we need to confirm Sleep Request | ||
272 | received previously */ | ||
273 | if (adapter->ps_state == PS_STATE_PRE_SLEEP) { | ||
274 | if (!adapter->cmd_sent && !adapter->curr_cmd) | ||
275 | mwifiex_check_ps_cond(adapter); | ||
276 | } | ||
277 | |||
278 | /* * The ps_state may have been changed during processing of | ||
279 | * Sleep Request event. | ||
280 | */ | ||
281 | if ((adapter->ps_state == PS_STATE_SLEEP) | ||
282 | || (adapter->ps_state == PS_STATE_PRE_SLEEP) | ||
283 | || (adapter->ps_state == PS_STATE_SLEEP_CFM) | ||
284 | || adapter->tx_lock_flag) | ||
285 | continue; | ||
286 | |||
287 | if (!adapter->cmd_sent && !adapter->curr_cmd) { | ||
288 | if (mwifiex_exec_next_cmd(adapter) == -1) { | ||
289 | ret = -1; | ||
290 | break; | ||
291 | } | ||
292 | } | ||
293 | |||
294 | if (!adapter->scan_processing && !adapter->data_sent && | ||
295 | !mwifiex_wmm_lists_empty(adapter)) { | ||
296 | mwifiex_wmm_process_tx(adapter); | ||
297 | if (adapter->hs_activated) { | ||
298 | adapter->is_hs_configured = false; | ||
299 | mwifiex_hs_activated_event | ||
300 | (mwifiex_get_priv | ||
301 | (adapter, MWIFIEX_BSS_ROLE_ANY), | ||
302 | false); | ||
303 | } | ||
304 | } | ||
305 | |||
306 | if (adapter->delay_null_pkt && !adapter->cmd_sent && | ||
307 | !adapter->curr_cmd && !is_command_pending(adapter) | ||
308 | && mwifiex_wmm_lists_empty(adapter)) { | ||
309 | if (!mwifiex_send_null_packet | ||
310 | (mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_STA), | ||
311 | MWIFIEX_TxPD_POWER_MGMT_NULL_PACKET | | ||
312 | MWIFIEX_TxPD_POWER_MGMT_LAST_PACKET)) { | ||
313 | adapter->delay_null_pkt = false; | ||
314 | adapter->ps_state = PS_STATE_SLEEP; | ||
315 | } | ||
316 | break; | ||
317 | } | ||
318 | } while (true); | ||
319 | |||
320 | if ((adapter->int_status) || IS_CARD_RX_RCVD(adapter)) | ||
321 | goto process_start; | ||
322 | |||
323 | spin_lock_irqsave(&adapter->main_proc_lock, flags); | ||
324 | adapter->mwifiex_processing = false; | ||
325 | spin_unlock_irqrestore(&adapter->main_proc_lock, flags); | ||
326 | |||
327 | exit_main_proc: | ||
328 | if (adapter->hw_status == MWIFIEX_HW_STATUS_CLOSING) | ||
329 | mwifiex_shutdown_drv(adapter); | ||
330 | return ret; | ||
331 | } | ||
332 | |||
333 | /* | ||
334 | * This function initializes the software. | ||
335 | * | ||
336 | * The main work includes allocating and initializing the adapter structure | ||
337 | * and initializing the private structures. | ||
338 | */ | ||
339 | static int | ||
340 | mwifiex_init_sw(void *card, struct mwifiex_if_ops *if_ops, void **pmwifiex) | ||
341 | { | ||
342 | int i; | ||
343 | struct mwifiex_device device; | ||
344 | struct mwifiex_drv_mode *drv_mode_ptr; | ||
345 | |||
346 | /* find mwifiex_drv_mode entry from mwifiex_drv_mode_tbl */ | ||
347 | drv_mode_ptr = NULL; | ||
348 | for (i = 0; i < ARRAY_SIZE(mwifiex_drv_mode_tbl); i++) { | ||
349 | if (mwifiex_drv_mode_tbl[i].drv_mode == drv_mode) { | ||
350 | drv_mode_ptr = &mwifiex_drv_mode_tbl[i]; | ||
351 | break; | ||
352 | } | ||
353 | } | ||
354 | |||
355 | if (!drv_mode_ptr) { | ||
356 | pr_err("invalid drv_mode=%d\n", drv_mode); | ||
357 | return -1; | ||
358 | } | ||
359 | |||
360 | memset(&device, 0, sizeof(struct mwifiex_device)); | ||
361 | |||
362 | for (i = 0; i < drv_mode_ptr->intf_num; i++) { | ||
363 | device.bss_attr[i].bss_type = | ||
364 | drv_mode_ptr->bss_attr[i].bss_type; | ||
365 | device.bss_attr[i].frame_type = | ||
366 | drv_mode_ptr->bss_attr[i].frame_type; | ||
367 | device.bss_attr[i].active = drv_mode_ptr->bss_attr[i].active; | ||
368 | device.bss_attr[i].bss_priority = | ||
369 | drv_mode_ptr->bss_attr[i].bss_priority; | ||
370 | device.bss_attr[i].bss_num = drv_mode_ptr->bss_attr[i].bss_num; | ||
371 | } | ||
372 | |||
373 | if (mwifiex_register(card, if_ops, &device, pmwifiex)) | ||
374 | return -1; | ||
375 | |||
376 | return 0; | ||
377 | } | ||
378 | |||
379 | /* | ||
380 | * This function frees the adapter structure. | ||
381 | * | ||
382 | * Additionally, this closes the netlink socket, frees the timers | ||
383 | * and private structures. | ||
384 | */ | ||
385 | static void mwifiex_free_adapter(struct mwifiex_adapter *adapter) | ||
386 | { | ||
387 | if (!adapter) { | ||
388 | pr_err("%s: adapter is NULL\n", __func__); | ||
389 | return; | ||
390 | } | ||
391 | |||
392 | mwifiex_unregister(adapter); | ||
393 | pr_debug("info: %s: free adapter\n", __func__); | ||
394 | } | ||
395 | |||
396 | /* | ||
397 | * This function initializes the hardware and firmware. | ||
398 | * | ||
399 | * The main initialization steps followed are - | ||
400 | * - Download the correct firmware to card | ||
401 | * - Allocate and initialize the adapter structure | ||
402 | * - Initialize the private structures | ||
403 | * - Issue the init commands to firmware | ||
404 | */ | ||
405 | static int mwifiex_init_hw_fw(struct mwifiex_adapter *adapter) | ||
406 | { | ||
407 | int ret = 0; | ||
408 | int err; | ||
409 | struct mwifiex_fw_image fw; | ||
410 | |||
411 | memset(&fw, 0, sizeof(struct mwifiex_fw_image)); | ||
412 | |||
413 | switch (adapter->revision_id) { | ||
414 | case SD8787_W0: | ||
415 | case SD8787_W1: | ||
416 | strcpy(fw_name, SD8787_W1_FW_NAME); | ||
417 | break; | ||
418 | case SD8787_A0: | ||
419 | case SD8787_A1: | ||
420 | strcpy(fw_name, SD8787_AX_FW_NAME); | ||
421 | break; | ||
422 | default: | ||
423 | break; | ||
424 | } | ||
425 | |||
426 | err = request_firmware(&adapter->firmware, fw_name, adapter->dev); | ||
427 | if (err < 0) { | ||
428 | dev_err(adapter->dev, "request_firmware() returned" | ||
429 | " error code %#x\n", err); | ||
430 | ret = -1; | ||
431 | goto done; | ||
432 | } | ||
433 | fw.fw_buf = (u8 *) adapter->firmware->data; | ||
434 | fw.fw_len = adapter->firmware->size; | ||
435 | |||
436 | ret = mwifiex_dnld_fw(adapter, &fw); | ||
437 | if (ret == -1) | ||
438 | goto done; | ||
439 | |||
440 | dev_notice(adapter->dev, "WLAN FW is active\n"); | ||
441 | |||
442 | adapter->init_wait_q_woken = false; | ||
443 | ret = mwifiex_init_fw(adapter); | ||
444 | if (ret == -1) { | ||
445 | goto done; | ||
446 | } else if (!ret) { | ||
447 | adapter->hw_status = MWIFIEX_HW_STATUS_READY; | ||
448 | goto done; | ||
449 | } | ||
450 | /* Wait for mwifiex_init to complete */ | ||
451 | wait_event_interruptible(adapter->init_wait_q, | ||
452 | adapter->init_wait_q_woken); | ||
453 | if (adapter->hw_status != MWIFIEX_HW_STATUS_READY) { | ||
454 | ret = -1; | ||
455 | goto done; | ||
456 | } | ||
457 | ret = 0; | ||
458 | |||
459 | done: | ||
460 | if (adapter->firmware) | ||
461 | release_firmware(adapter->firmware); | ||
462 | if (ret) | ||
463 | ret = -1; | ||
464 | return ret; | ||
465 | } | ||
466 | |||
467 | /* | ||
468 | * This function fills a driver buffer. | ||
469 | * | ||
470 | * The function associates a given SKB with the provided driver buffer | ||
471 | * and also updates some of the SKB parameters, including IP header, | ||
472 | * priority and timestamp. | ||
473 | */ | ||
474 | static void | ||
475 | mwifiex_fill_buffer(struct sk_buff *skb) | ||
476 | { | ||
477 | struct ethhdr *eth = NULL; | ||
478 | struct iphdr *iph; | ||
479 | struct timeval tv; | ||
480 | u8 tid = 0; | ||
481 | |||
482 | eth = (struct ethhdr *) skb->data; | ||
483 | switch (eth->h_proto) { | ||
484 | case __constant_htons(ETH_P_IP): | ||
485 | iph = ip_hdr(skb); | ||
486 | tid = IPTOS_PREC(iph->tos); | ||
487 | pr_debug("data: packet type ETH_P_IP: %04x, tid=%#x prio=%#x\n", | ||
488 | eth->h_proto, tid, skb->priority); | ||
489 | break; | ||
490 | case __constant_htons(ETH_P_ARP): | ||
491 | pr_debug("data: ARP packet: %04x\n", eth->h_proto); | ||
492 | default: | ||
493 | break; | ||
494 | } | ||
495 | /* Offset for TOS field in the IP header */ | ||
496 | #define IPTOS_OFFSET 5 | ||
497 | tid = (tid >> IPTOS_OFFSET); | ||
498 | skb->priority = tid; | ||
499 | /* Record the current time the packet was queued; used to | ||
500 | determine the amount of time the packet was queued in | ||
501 | the driver before it was sent to the firmware. | ||
502 | The delay is then sent along with the packet to the | ||
503 | firmware for aggregate delay calculation for stats and | ||
504 | MSDU lifetime expiry. | ||
505 | */ | ||
506 | do_gettimeofday(&tv); | ||
507 | skb->tstamp = timeval_to_ktime(tv); | ||
508 | return; | ||
509 | } | ||
510 | |||
511 | /* | ||
512 | * CFG802.11 network device handler for open. | ||
513 | * | ||
514 | * Starts the data queue. | ||
515 | */ | ||
516 | static int | ||
517 | mwifiex_open(struct net_device *dev) | ||
518 | { | ||
519 | netif_start_queue(dev); | ||
520 | return 0; | ||
521 | } | ||
522 | |||
523 | /* | ||
524 | * CFG802.11 network device handler for close. | ||
525 | */ | ||
526 | static int | ||
527 | mwifiex_close(struct net_device *dev) | ||
528 | { | ||
529 | return 0; | ||
530 | } | ||
531 | |||
532 | /* | ||
533 | * CFG802.11 network device handler for data transmission. | ||
534 | */ | ||
535 | static int | ||
536 | mwifiex_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) | ||
537 | { | ||
538 | struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); | ||
539 | struct sk_buff *new_skb = NULL; | ||
540 | struct mwifiex_txinfo *tx_info; | ||
541 | |||
542 | dev_dbg(priv->adapter->dev, "data: %lu BSS(%d): Data <= kernel\n", | ||
543 | jiffies, priv->bss_index); | ||
544 | |||
545 | if (priv->adapter->surprise_removed) { | ||
546 | kfree(skb); | ||
547 | priv->stats.tx_dropped++; | ||
548 | return 0; | ||
549 | } | ||
550 | if (!skb->len || (skb->len > ETH_FRAME_LEN)) { | ||
551 | dev_err(priv->adapter->dev, "Tx: bad skb len %d\n", skb->len); | ||
552 | kfree(skb); | ||
553 | priv->stats.tx_dropped++; | ||
554 | return 0; | ||
555 | } | ||
556 | if (skb_headroom(skb) < MWIFIEX_MIN_DATA_HEADER_LEN) { | ||
557 | dev_dbg(priv->adapter->dev, | ||
558 | "data: Tx: insufficient skb headroom %d\n", | ||
559 | skb_headroom(skb)); | ||
560 | /* Insufficient skb headroom - allocate a new skb */ | ||
561 | new_skb = | ||
562 | skb_realloc_headroom(skb, MWIFIEX_MIN_DATA_HEADER_LEN); | ||
563 | if (unlikely(!new_skb)) { | ||
564 | dev_err(priv->adapter->dev, "Tx: cannot alloca new_skb\n"); | ||
565 | kfree(skb); | ||
566 | priv->stats.tx_dropped++; | ||
567 | return 0; | ||
568 | } | ||
569 | kfree_skb(skb); | ||
570 | skb = new_skb; | ||
571 | dev_dbg(priv->adapter->dev, "info: new skb headroomd %d\n", | ||
572 | skb_headroom(skb)); | ||
573 | } | ||
574 | |||
575 | tx_info = MWIFIEX_SKB_TXCB(skb); | ||
576 | tx_info->bss_index = priv->bss_index; | ||
577 | mwifiex_fill_buffer(skb); | ||
578 | |||
579 | mwifiex_wmm_add_buf_txqueue(priv->adapter, skb); | ||
580 | atomic_inc(&priv->adapter->tx_pending); | ||
581 | |||
582 | if (atomic_read(&priv->adapter->tx_pending) >= MAX_TX_PENDING) { | ||
583 | netif_stop_queue(priv->netdev); | ||
584 | dev->trans_start = jiffies; | ||
585 | } | ||
586 | |||
587 | queue_work(priv->adapter->workqueue, &priv->adapter->main_work); | ||
588 | |||
589 | return 0; | ||
590 | } | ||
591 | |||
592 | /* | ||
593 | * CFG802.11 network device handler for setting MAC address. | ||
594 | */ | ||
595 | static int | ||
596 | mwifiex_set_mac_address(struct net_device *dev, void *addr) | ||
597 | { | ||
598 | struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); | ||
599 | struct sockaddr *hw_addr = (struct sockaddr *) addr; | ||
600 | |||
601 | memcpy(priv->curr_addr, hw_addr->sa_data, ETH_ALEN); | ||
602 | |||
603 | if (mwifiex_request_set_mac_address(priv)) { | ||
604 | dev_err(priv->adapter->dev, "set MAC address failed\n"); | ||
605 | return -EFAULT; | ||
606 | } | ||
607 | memcpy(dev->dev_addr, priv->curr_addr, ETH_ALEN); | ||
608 | |||
609 | return 0; | ||
610 | } | ||
611 | |||
612 | /* | ||
613 | * CFG802.11 network device handler for setting multicast list. | ||
614 | */ | ||
615 | static void mwifiex_set_multicast_list(struct net_device *dev) | ||
616 | { | ||
617 | struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); | ||
618 | mwifiex_request_set_multicast_list(priv, dev); | ||
619 | } | ||
620 | |||
621 | /* | ||
622 | * CFG802.11 network device handler for transmission timeout. | ||
623 | */ | ||
624 | static void | ||
625 | mwifiex_tx_timeout(struct net_device *dev) | ||
626 | { | ||
627 | struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); | ||
628 | |||
629 | dev_err(priv->adapter->dev, "%lu : Tx timeout, bss_index=%d\n", | ||
630 | jiffies, priv->bss_index); | ||
631 | dev->trans_start = jiffies; | ||
632 | priv->num_tx_timeout++; | ||
633 | } | ||
634 | |||
635 | /* | ||
636 | * CFG802.11 network device handler for statistics retrieval. | ||
637 | */ | ||
638 | static struct net_device_stats *mwifiex_get_stats(struct net_device *dev) | ||
639 | { | ||
640 | struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); | ||
641 | |||
642 | return &priv->stats; | ||
643 | } | ||
644 | |||
645 | /* Network device handlers */ | ||
646 | static const struct net_device_ops mwifiex_netdev_ops = { | ||
647 | .ndo_open = mwifiex_open, | ||
648 | .ndo_stop = mwifiex_close, | ||
649 | .ndo_start_xmit = mwifiex_hard_start_xmit, | ||
650 | .ndo_set_mac_address = mwifiex_set_mac_address, | ||
651 | .ndo_tx_timeout = mwifiex_tx_timeout, | ||
652 | .ndo_get_stats = mwifiex_get_stats, | ||
653 | .ndo_set_multicast_list = mwifiex_set_multicast_list, | ||
654 | }; | ||
655 | |||
656 | /* | ||
657 | * This function initializes the private structure parameters. | ||
658 | * | ||
659 | * The following wait queues are initialized - | ||
660 | * - IOCTL wait queue | ||
661 | * - Command wait queue | ||
662 | * - Statistics wait queue | ||
663 | * | ||
664 | * ...and the following default parameters are set - | ||
665 | * - Current key index : Set to 0 | ||
666 | * - Rate index : Set to auto | ||
667 | * - Media connected : Set to disconnected | ||
668 | * - Adhoc link sensed : Set to false | ||
669 | * - Nick name : Set to null | ||
670 | * - Number of Tx timeout : Set to 0 | ||
671 | * - Device address : Set to current address | ||
672 | * | ||
673 | * In addition, the CFG80211 work queue is also created. | ||
674 | */ | ||
675 | static void | ||
676 | mwifiex_init_priv_params(struct mwifiex_private *priv, struct net_device *dev) | ||
677 | { | ||
678 | dev->netdev_ops = &mwifiex_netdev_ops; | ||
679 | /* Initialize private structure */ | ||
680 | init_waitqueue_head(&priv->ioctl_wait_q); | ||
681 | init_waitqueue_head(&priv->cmd_wait_q); | ||
682 | init_waitqueue_head(&priv->w_stats_wait_q); | ||
683 | priv->current_key_index = 0; | ||
684 | priv->media_connected = false; | ||
685 | memset(&priv->nick_name, 0, sizeof(priv->nick_name)); | ||
686 | priv->num_tx_timeout = 0; | ||
687 | priv->workqueue = create_singlethread_workqueue("cfg80211_wq"); | ||
688 | INIT_WORK(&priv->cfg_workqueue, mwifiex_cfg80211_results); | ||
689 | memcpy(dev->dev_addr, priv->curr_addr, ETH_ALEN); | ||
690 | } | ||
691 | |||
692 | /* | ||
693 | * This function adds a new logical interface. | ||
694 | * | ||
695 | * It allocates, initializes and registers the interface by performing | ||
696 | * the following opearations - | ||
697 | * - Allocate a new net device structure | ||
698 | * - Assign device name | ||
699 | * - Register the new device with CFG80211 subsystem | ||
700 | * - Initialize semaphore and private structure | ||
701 | * - Register the new device with kernel | ||
702 | * - Create the complete debug FS structure if configured | ||
703 | */ | ||
704 | static struct mwifiex_private *mwifiex_add_interface( | ||
705 | struct mwifiex_adapter *adapter, | ||
706 | u8 bss_index, u8 bss_type) | ||
707 | { | ||
708 | struct net_device *dev = NULL; | ||
709 | struct mwifiex_private *priv = NULL; | ||
710 | void *mdev_priv = NULL; | ||
711 | |||
712 | dev = alloc_netdev_mq(sizeof(struct mwifiex_private *), "mlan%d", | ||
713 | ether_setup, 1); | ||
714 | if (!dev) { | ||
715 | dev_err(adapter->dev, "no memory available for netdevice\n"); | ||
716 | goto error; | ||
717 | } | ||
718 | if (dev_alloc_name(dev, dev->name)) { | ||
719 | dev_err(adapter->dev, "unable to alloc name for netdevice\n"); | ||
720 | goto error; | ||
721 | } | ||
722 | |||
723 | if (mwifiex_register_cfg80211(dev, adapter->priv[bss_index]->curr_addr, | ||
724 | adapter->priv[bss_index]) != 0) { | ||
725 | dev_err(adapter->dev, "cannot register netdevice with cfg80211\n"); | ||
726 | goto error; | ||
727 | } | ||
728 | /* Save the priv pointer in netdev */ | ||
729 | priv = adapter->priv[bss_index]; | ||
730 | mdev_priv = netdev_priv(dev); | ||
731 | *((unsigned long *) mdev_priv) = (unsigned long) priv; | ||
732 | |||
733 | priv->netdev = dev; | ||
734 | |||
735 | sema_init(&priv->async_sem, 1); | ||
736 | priv->scan_pending_on_block = false; | ||
737 | |||
738 | mwifiex_init_priv_params(priv, dev); | ||
739 | |||
740 | SET_NETDEV_DEV(dev, adapter->dev); | ||
741 | |||
742 | /* Register network device */ | ||
743 | if (register_netdev(dev)) { | ||
744 | dev_err(adapter->dev, "cannot register virtual network device\n"); | ||
745 | goto error; | ||
746 | } | ||
747 | |||
748 | dev_dbg(adapter->dev, "info: %s: Marvell 802.11 Adapter\n", dev->name); | ||
749 | #ifdef CONFIG_DEBUG_FS | ||
750 | mwifiex_dev_debugfs_init(priv); | ||
751 | #endif | ||
752 | return priv; | ||
753 | error: | ||
754 | if (dev) | ||
755 | free_netdev(dev); | ||
756 | return NULL; | ||
757 | } | ||
758 | |||
759 | /* | ||
760 | * This function removes a logical interface. | ||
761 | * | ||
762 | * It deregisters, resets and frees the interface by performing | ||
763 | * the following operations - | ||
764 | * - Disconnect the device if connected, send wireless event to | ||
765 | * notify applications. | ||
766 | * - Remove the debug FS structure if configured | ||
767 | * - Unregister the device from kernel | ||
768 | * - Free the net device structure | ||
769 | * - Cancel all works and destroy work queue | ||
770 | * - Unregister and free the wireless device from CFG80211 subsystem | ||
771 | */ | ||
772 | static void | ||
773 | mwifiex_remove_interface(struct mwifiex_adapter *adapter, u8 bss_index) | ||
774 | { | ||
775 | struct net_device *dev = NULL; | ||
776 | struct mwifiex_private *priv = adapter->priv[bss_index]; | ||
777 | |||
778 | if (!priv) | ||
779 | return; | ||
780 | dev = priv->netdev; | ||
781 | |||
782 | if (priv->media_connected) | ||
783 | priv->media_connected = false; | ||
784 | |||
785 | #ifdef CONFIG_DEBUG_FS | ||
786 | mwifiex_dev_debugfs_remove(priv); | ||
787 | #endif | ||
788 | /* Last reference is our one */ | ||
789 | dev_dbg(adapter->dev, "info: %s: refcnt = %d\n", | ||
790 | dev->name, netdev_refcnt_read(dev)); | ||
791 | |||
792 | if (dev->reg_state == NETREG_REGISTERED) | ||
793 | unregister_netdev(dev); | ||
794 | |||
795 | /* Clear the priv in adapter */ | ||
796 | priv->netdev = NULL; | ||
797 | if (dev) | ||
798 | free_netdev(dev); | ||
799 | |||
800 | cancel_work_sync(&priv->cfg_workqueue); | ||
801 | flush_workqueue(priv->workqueue); | ||
802 | destroy_workqueue(priv->workqueue); | ||
803 | wiphy_unregister(priv->wdev->wiphy); | ||
804 | wiphy_free(priv->wdev->wiphy); | ||
805 | kfree(priv->wdev); | ||
806 | |||
807 | return; | ||
808 | } | ||
809 | |||
810 | /* | ||
811 | * Sends IOCTL request to shutdown firmware. | ||
812 | * | ||
813 | * This function allocates the IOCTL request buffer, fills it | ||
814 | * with requisite parameters and calls the IOCTL handler. | ||
815 | */ | ||
816 | int mwifiex_shutdown_fw(struct mwifiex_private *priv, u8 wait_option) | ||
817 | { | ||
818 | struct mwifiex_wait_queue *wait = NULL; | ||
819 | int status = 0; | ||
820 | |||
821 | /* Allocate an IOCTL request buffer */ | ||
822 | wait = mwifiex_alloc_fill_wait_queue(priv, wait_option); | ||
823 | if (!wait) | ||
824 | return -ENOMEM; | ||
825 | |||
826 | status = mwifiex_misc_ioctl_init_shutdown(priv->adapter, wait, | ||
827 | MWIFIEX_FUNC_SHUTDOWN); | ||
828 | |||
829 | status = mwifiex_request_ioctl(priv, wait, status, wait_option); | ||
830 | |||
831 | kfree(wait); | ||
832 | return status; | ||
833 | } | ||
834 | EXPORT_SYMBOL_GPL(mwifiex_shutdown_fw); | ||
835 | |||
836 | /* | ||
837 | * This function check if command is pending. | ||
838 | */ | ||
839 | int is_command_pending(struct mwifiex_adapter *adapter) | ||
840 | { | ||
841 | unsigned long flags; | ||
842 | int is_cmd_pend_q_empty; | ||
843 | |||
844 | spin_lock_irqsave(&adapter->cmd_pending_q_lock, flags); | ||
845 | is_cmd_pend_q_empty = list_empty(&adapter->cmd_pending_q); | ||
846 | spin_unlock_irqrestore(&adapter->cmd_pending_q_lock, flags); | ||
847 | |||
848 | return !is_cmd_pend_q_empty; | ||
849 | } | ||
850 | |||
851 | /* | ||
852 | * This function returns the correct private structure pointer based | ||
853 | * upon the BSS number. | ||
854 | */ | ||
855 | struct mwifiex_private * | ||
856 | mwifiex_bss_index_to_priv(struct mwifiex_adapter *adapter, u8 bss_index) | ||
857 | { | ||
858 | if (!adapter || (bss_index >= adapter->priv_num)) | ||
859 | return NULL; | ||
860 | return adapter->priv[bss_index]; | ||
861 | } | ||
862 | |||
863 | /* | ||
864 | * This is the main work queue function. | ||
865 | * | ||
866 | * It handles the main process, which in turn handles the complete | ||
867 | * driver operations. | ||
868 | */ | ||
869 | static void mwifiex_main_work_queue(struct work_struct *work) | ||
870 | { | ||
871 | struct mwifiex_adapter *adapter = | ||
872 | container_of(work, struct mwifiex_adapter, main_work); | ||
873 | |||
874 | if (adapter->surprise_removed) | ||
875 | return; | ||
876 | mwifiex_main_process(adapter); | ||
877 | } | ||
878 | |||
879 | /* | ||
880 | * This function cancels all works in the queue and destroys | ||
881 | * the main workqueue. | ||
882 | */ | ||
883 | static void | ||
884 | mwifiex_terminate_workqueue(struct mwifiex_adapter *adapter) | ||
885 | { | ||
886 | flush_workqueue(adapter->workqueue); | ||
887 | destroy_workqueue(adapter->workqueue); | ||
888 | adapter->workqueue = NULL; | ||
889 | } | ||
890 | |||
891 | /* | ||
892 | * This function adds the card. | ||
893 | * | ||
894 | * This function follows the following major steps to set up the device - | ||
895 | * - Initialize software. This includes probing the card, registering | ||
896 | * the interface operations table, and allocating/initializing the | ||
897 | * adapter structure | ||
898 | * - Set up the netlink socket | ||
899 | * - Create and start the main work queue | ||
900 | * - Register the device | ||
901 | * - Initialize firmware and hardware | ||
902 | * - Add logical interfaces | ||
903 | */ | ||
904 | int | ||
905 | mwifiex_add_card(void *card, struct semaphore *sem, | ||
906 | struct mwifiex_if_ops *if_ops) | ||
907 | { | ||
908 | int status = 0; | ||
909 | int i; | ||
910 | struct mwifiex_adapter *adapter = NULL; | ||
911 | struct mwifiex_drv_mode *drv_mode_info = &mwifiex_drv_mode_tbl[0]; | ||
912 | |||
913 | if (down_interruptible(sem)) | ||
914 | goto exit_sem_err; | ||
915 | |||
916 | if (mwifiex_init_sw(card, if_ops, (void **) &adapter)) { | ||
917 | pr_err("%s: software init failed\n", __func__); | ||
918 | goto err_init_sw; | ||
919 | } | ||
920 | |||
921 | adapter->drv_mode = drv_mode_info; | ||
922 | |||
923 | adapter->hw_status = MWIFIEX_HW_STATUS_INITIALIZING; | ||
924 | /* PnP and power profile */ | ||
925 | adapter->surprise_removed = false; | ||
926 | init_waitqueue_head(&adapter->init_wait_q); | ||
927 | adapter->is_suspended = false; | ||
928 | adapter->hs_activated = false; | ||
929 | init_waitqueue_head(&adapter->hs_activate_wait_q); | ||
930 | |||
931 | /* Create workqueue */ | ||
932 | adapter->workqueue = create_workqueue("MWIFIEX_WORK_QUEUE"); | ||
933 | if (!adapter->workqueue) | ||
934 | goto err_kmalloc; | ||
935 | |||
936 | INIT_WORK(&adapter->main_work, mwifiex_main_work_queue); | ||
937 | |||
938 | /* Register the device. Fill up the private data structure with relevant | ||
939 | information from the card and request for the required IRQ. */ | ||
940 | if (adapter->if_ops.register_dev(adapter)) { | ||
941 | pr_err("%s: failed to register mwifiex device\n", __func__); | ||
942 | goto err_registerdev; | ||
943 | } | ||
944 | |||
945 | /* Init FW and HW */ | ||
946 | if (mwifiex_init_hw_fw(adapter)) { | ||
947 | pr_err("%s: firmware init failed\n", __func__); | ||
948 | goto err_init_fw; | ||
949 | } | ||
950 | /* Add interfaces */ | ||
951 | for (i = 0; i < drv_mode_info->intf_num; i++) { | ||
952 | if (!mwifiex_add_interface(adapter, i, | ||
953 | adapter->drv_mode->bss_attr[i].bss_type)) { | ||
954 | status = -1; | ||
955 | break; | ||
956 | } | ||
957 | } | ||
958 | if (status) | ||
959 | goto err_add_intf; | ||
960 | |||
961 | up(sem); | ||
962 | |||
963 | return 0; | ||
964 | |||
965 | err_add_intf: | ||
966 | for (i = 0; i < adapter->priv_num; i++) | ||
967 | mwifiex_remove_interface(adapter, i); | ||
968 | err_init_fw: | ||
969 | /* Unregister device */ | ||
970 | pr_debug("info: %s: unregister device\n", __func__); | ||
971 | adapter->if_ops.unregister_dev(adapter); | ||
972 | err_registerdev: | ||
973 | adapter->surprise_removed = true; | ||
974 | mwifiex_terminate_workqueue(adapter); | ||
975 | err_kmalloc: | ||
976 | if ((adapter->hw_status == MWIFIEX_HW_STATUS_FW_READY) || | ||
977 | (adapter->hw_status == MWIFIEX_HW_STATUS_READY)) { | ||
978 | pr_debug("info: %s: shutdown mwifiex\n", __func__); | ||
979 | adapter->init_wait_q_woken = false; | ||
980 | status = mwifiex_shutdown_drv(adapter); | ||
981 | if (status == -EINPROGRESS) | ||
982 | wait_event_interruptible(adapter->init_wait_q, | ||
983 | adapter->init_wait_q_woken); | ||
984 | } | ||
985 | |||
986 | mwifiex_free_adapter(adapter); | ||
987 | |||
988 | err_init_sw: | ||
989 | up(sem); | ||
990 | |||
991 | exit_sem_err: | ||
992 | return -1; | ||
993 | } | ||
994 | EXPORT_SYMBOL_GPL(mwifiex_add_card); | ||
995 | |||
996 | /* | ||
997 | * This function removes the card. | ||
998 | * | ||
999 | * This function follows the following major steps to remove the device - | ||
1000 | * - Stop data traffic | ||
1001 | * - Shutdown firmware | ||
1002 | * - Remove the logical interfaces | ||
1003 | * - Terminate the work queue | ||
1004 | * - Unregister the device | ||
1005 | * - Free the adapter structure | ||
1006 | */ | ||
1007 | int mwifiex_remove_card(struct mwifiex_adapter *adapter, struct semaphore *sem) | ||
1008 | { | ||
1009 | struct mwifiex_private *priv = NULL; | ||
1010 | int status; | ||
1011 | int i; | ||
1012 | |||
1013 | if (down_interruptible(sem)) | ||
1014 | goto exit_sem_err; | ||
1015 | |||
1016 | if (!adapter) | ||
1017 | goto exit_remove; | ||
1018 | |||
1019 | adapter->surprise_removed = true; | ||
1020 | |||
1021 | /* Stop data */ | ||
1022 | for (i = 0; i < adapter->priv_num; i++) { | ||
1023 | priv = adapter->priv[i]; | ||
1024 | if (priv) { | ||
1025 | if (!netif_queue_stopped(priv->netdev)) | ||
1026 | netif_stop_queue(priv->netdev); | ||
1027 | if (netif_carrier_ok(priv->netdev)) | ||
1028 | netif_carrier_off(priv->netdev); | ||
1029 | } | ||
1030 | } | ||
1031 | |||
1032 | dev_dbg(adapter->dev, "cmd: calling mwifiex_shutdown_drv...\n"); | ||
1033 | adapter->init_wait_q_woken = false; | ||
1034 | status = mwifiex_shutdown_drv(adapter); | ||
1035 | if (status == -EINPROGRESS) | ||
1036 | wait_event_interruptible(adapter->init_wait_q, | ||
1037 | adapter->init_wait_q_woken); | ||
1038 | dev_dbg(adapter->dev, "cmd: mwifiex_shutdown_drv done\n"); | ||
1039 | if (atomic_read(&adapter->rx_pending) || | ||
1040 | atomic_read(&adapter->tx_pending) || | ||
1041 | atomic_read(&adapter->ioctl_pending)) { | ||
1042 | dev_err(adapter->dev, "rx_pending=%d, tx_pending=%d, " | ||
1043 | "ioctl_pending=%d\n", | ||
1044 | atomic_read(&adapter->rx_pending), | ||
1045 | atomic_read(&adapter->tx_pending), | ||
1046 | atomic_read(&adapter->ioctl_pending)); | ||
1047 | } | ||
1048 | |||
1049 | /* Remove interface */ | ||
1050 | for (i = 0; i < adapter->priv_num; i++) | ||
1051 | mwifiex_remove_interface(adapter, i); | ||
1052 | |||
1053 | mwifiex_terminate_workqueue(adapter); | ||
1054 | |||
1055 | /* Unregister device */ | ||
1056 | dev_dbg(adapter->dev, "info: unregister device\n"); | ||
1057 | adapter->if_ops.unregister_dev(adapter); | ||
1058 | /* Free adapter structure */ | ||
1059 | dev_dbg(adapter->dev, "info: free adapter\n"); | ||
1060 | mwifiex_free_adapter(adapter); | ||
1061 | |||
1062 | exit_remove: | ||
1063 | up(sem); | ||
1064 | exit_sem_err: | ||
1065 | return 0; | ||
1066 | } | ||
1067 | EXPORT_SYMBOL_GPL(mwifiex_remove_card); | ||
1068 | |||
1069 | /* | ||
1070 | * This function initializes the module. | ||
1071 | * | ||
1072 | * The debug FS is also initialized if configured. | ||
1073 | */ | ||
1074 | static int | ||
1075 | mwifiex_init_module(void) | ||
1076 | { | ||
1077 | #ifdef CONFIG_DEBUG_FS | ||
1078 | mwifiex_debugfs_init(); | ||
1079 | #endif | ||
1080 | return 0; | ||
1081 | } | ||
1082 | |||
1083 | /* | ||
1084 | * This function cleans up the module. | ||
1085 | * | ||
1086 | * The debug FS is removed if available. | ||
1087 | */ | ||
1088 | static void | ||
1089 | mwifiex_cleanup_module(void) | ||
1090 | { | ||
1091 | #ifdef CONFIG_DEBUG_FS | ||
1092 | mwifiex_debugfs_remove(); | ||
1093 | #endif | ||
1094 | } | ||
1095 | |||
1096 | module_init(mwifiex_init_module); | ||
1097 | module_exit(mwifiex_cleanup_module); | ||
1098 | |||
1099 | MODULE_AUTHOR("Marvell International Ltd."); | ||
1100 | MODULE_DESCRIPTION("Marvell WiFi-Ex Driver version " VERSION); | ||
1101 | MODULE_VERSION(VERSION); | ||
1102 | MODULE_LICENSE("GPL v2"); | ||
diff --git a/drivers/net/wireless/mwifiex/main.h b/drivers/net/wireless/mwifiex/main.h new file mode 100644 index 000000000000..2b0ad8e3d6e2 --- /dev/null +++ b/drivers/net/wireless/mwifiex/main.h | |||
@@ -0,0 +1,1081 @@ | |||
1 | /* | ||
2 | * Marvell Wireless LAN device driver: major data structures and prototypes | ||
3 | * | ||
4 | * Copyright (C) 2011, Marvell International Ltd. | ||
5 | * | ||
6 | * This software file (the "File") is distributed by Marvell International | ||
7 | * Ltd. under the terms of the GNU General Public License Version 2, June 1991 | ||
8 | * (the "License"). You may use, redistribute and/or modify this File in | ||
9 | * accordance with the terms and conditions of the License, a copy of which | ||
10 | * is available by writing to the Free Software Foundation, Inc., | ||
11 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the | ||
12 | * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. | ||
13 | * | ||
14 | * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE | ||
15 | * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE | ||
16 | * ARE EXPRESSLY DISCLAIMED. The License provides additional details about | ||
17 | * this warranty disclaimer. | ||
18 | */ | ||
19 | |||
20 | #ifndef _MWIFIEX_MAIN_H_ | ||
21 | #define _MWIFIEX_MAIN_H_ | ||
22 | |||
23 | #include <linux/kernel.h> | ||
24 | #include <linux/module.h> | ||
25 | #include <linux/sched.h> | ||
26 | #include <linux/semaphore.h> | ||
27 | #include <linux/ip.h> | ||
28 | #include <linux/skbuff.h> | ||
29 | #include <linux/if_arp.h> | ||
30 | #include <linux/etherdevice.h> | ||
31 | #include <net/sock.h> | ||
32 | #include <net/lib80211.h> | ||
33 | #include <linux/firmware.h> | ||
34 | #include <linux/ctype.h> | ||
35 | |||
36 | #include "decl.h" | ||
37 | #include "ioctl.h" | ||
38 | #include "util.h" | ||
39 | #include "fw.h" | ||
40 | |||
41 | extern const char driver_version[]; | ||
42 | extern struct mwifiex_adapter *g_adapter; | ||
43 | |||
44 | enum { | ||
45 | MWIFIEX_NO_WAIT, | ||
46 | MWIFIEX_IOCTL_WAIT, | ||
47 | MWIFIEX_CMD_WAIT, | ||
48 | MWIFIEX_PROC_WAIT, | ||
49 | MWIFIEX_WSTATS_WAIT | ||
50 | }; | ||
51 | |||
52 | #define DRV_MODE_STA 0x1 | ||
53 | #define DRV_MODE_UAP 0x2 | ||
54 | #define DRV_MODE_UAP_STA 0x3 | ||
55 | |||
56 | #define SD8787_W0 0x30 | ||
57 | #define SD8787_W1 0x31 | ||
58 | #define SD8787_A0 0x40 | ||
59 | #define SD8787_A1 0x41 | ||
60 | |||
61 | #define DEFAULT_FW_NAME "mrvl/sd8787_uapsta.bin" | ||
62 | #define SD8787_W1_FW_NAME "mrvl/sd8787_uapsta_w1.bin" | ||
63 | #define SD8787_AX_FW_NAME "mrvl/sd8787_uapsta.bin" | ||
64 | |||
65 | struct mwifiex_drv_mode { | ||
66 | u16 drv_mode; | ||
67 | u16 intf_num; | ||
68 | struct mwifiex_bss_attr *bss_attr; | ||
69 | }; | ||
70 | |||
71 | |||
72 | #define MWIFIEX_DEFAULT_WATCHDOG_TIMEOUT (5 * HZ) | ||
73 | |||
74 | #define MWIFIEX_TIMER_10S 10000 | ||
75 | #define MWIFIEX_TIMER_1S 1000 | ||
76 | |||
77 | #define NL_MAX_PAYLOAD 1024 | ||
78 | #define NL_MULTICAST_GROUP 1 | ||
79 | |||
80 | #define MAX_TX_PENDING 60 | ||
81 | |||
82 | #define HEADER_ALIGNMENT 8 | ||
83 | |||
84 | #define MWIFIEX_UPLD_SIZE (2312) | ||
85 | |||
86 | #define MAX_EVENT_SIZE 1024 | ||
87 | |||
88 | #define ARP_FILTER_MAX_BUF_SIZE 68 | ||
89 | |||
90 | #define MWIFIEX_KEY_BUFFER_SIZE 16 | ||
91 | #define MWIFIEX_DEFAULT_LISTEN_INTERVAL 10 | ||
92 | #define MWIFIEX_MAX_REGION_CODE 7 | ||
93 | |||
94 | #define DEFAULT_BCN_AVG_FACTOR 8 | ||
95 | #define DEFAULT_DATA_AVG_FACTOR 8 | ||
96 | |||
97 | #define FIRST_VALID_CHANNEL 0xff | ||
98 | #define DEFAULT_AD_HOC_CHANNEL 6 | ||
99 | #define DEFAULT_AD_HOC_CHANNEL_A 36 | ||
100 | |||
101 | #define DEFAULT_BCN_MISS_TIMEOUT 5 | ||
102 | |||
103 | #define MAX_SCAN_BEACON_BUFFER 8000 | ||
104 | |||
105 | #define SCAN_BEACON_ENTRY_PAD 6 | ||
106 | |||
107 | #define MWIFIEX_PASSIVE_SCAN_CHAN_TIME 200 | ||
108 | #define MWIFIEX_ACTIVE_SCAN_CHAN_TIME 200 | ||
109 | #define MWIFIEX_SPECIFIC_SCAN_CHAN_TIME 110 | ||
110 | |||
111 | #define SCAN_RSSI(RSSI) (0x100 - ((u8)(RSSI))) | ||
112 | |||
113 | #define MWIFIEX_MAX_TOTAL_SCAN_TIME (MWIFIEX_TIMER_10S - MWIFIEX_TIMER_1S) | ||
114 | |||
115 | #define RSN_GTK_OUI_OFFSET 2 | ||
116 | |||
117 | #define MWIFIEX_OUI_NOT_PRESENT 0 | ||
118 | #define MWIFIEX_OUI_PRESENT 1 | ||
119 | |||
120 | #define IS_CARD_RX_RCVD(adapter) (adapter->cmd_resp_received || \ | ||
121 | adapter->event_received || \ | ||
122 | adapter->data_received) | ||
123 | |||
124 | #define MWIFIEX_TYPE_CMD 1 | ||
125 | #define MWIFIEX_TYPE_DATA 0 | ||
126 | #define MWIFIEX_TYPE_EVENT 3 | ||
127 | |||
128 | #define DBG_CMD_NUM 5 | ||
129 | |||
130 | #define MAX_BITMAP_RATES_SIZE 10 | ||
131 | |||
132 | #define MAX_CHANNEL_BAND_BG 14 | ||
133 | |||
134 | #define MAX_FREQUENCY_BAND_BG 2484 | ||
135 | |||
136 | struct mwifiex_dbg { | ||
137 | u32 num_cmd_host_to_card_failure; | ||
138 | u32 num_cmd_sleep_cfm_host_to_card_failure; | ||
139 | u32 num_tx_host_to_card_failure; | ||
140 | u32 num_event_deauth; | ||
141 | u32 num_event_disassoc; | ||
142 | u32 num_event_link_lost; | ||
143 | u32 num_cmd_deauth; | ||
144 | u32 num_cmd_assoc_success; | ||
145 | u32 num_cmd_assoc_failure; | ||
146 | u32 num_tx_timeout; | ||
147 | u32 num_cmd_timeout; | ||
148 | u16 timeout_cmd_id; | ||
149 | u16 timeout_cmd_act; | ||
150 | u16 last_cmd_id[DBG_CMD_NUM]; | ||
151 | u16 last_cmd_act[DBG_CMD_NUM]; | ||
152 | u16 last_cmd_index; | ||
153 | u16 last_cmd_resp_id[DBG_CMD_NUM]; | ||
154 | u16 last_cmd_resp_index; | ||
155 | u16 last_event[DBG_CMD_NUM]; | ||
156 | u16 last_event_index; | ||
157 | }; | ||
158 | |||
159 | enum MWIFIEX_HARDWARE_STATUS { | ||
160 | MWIFIEX_HW_STATUS_READY, | ||
161 | MWIFIEX_HW_STATUS_INITIALIZING, | ||
162 | MWIFIEX_HW_STATUS_FW_READY, | ||
163 | MWIFIEX_HW_STATUS_INIT_DONE, | ||
164 | MWIFIEX_HW_STATUS_RESET, | ||
165 | MWIFIEX_HW_STATUS_CLOSING, | ||
166 | MWIFIEX_HW_STATUS_NOT_READY | ||
167 | }; | ||
168 | |||
169 | enum MWIFIEX_802_11_POWER_MODE { | ||
170 | MWIFIEX_802_11_POWER_MODE_CAM, | ||
171 | MWIFIEX_802_11_POWER_MODE_PSP | ||
172 | }; | ||
173 | |||
174 | struct mwifiex_tx_param { | ||
175 | u32 next_pkt_len; | ||
176 | }; | ||
177 | |||
178 | enum MWIFIEX_PS_STATE { | ||
179 | PS_STATE_AWAKE, | ||
180 | PS_STATE_PRE_SLEEP, | ||
181 | PS_STATE_SLEEP_CFM, | ||
182 | PS_STATE_SLEEP | ||
183 | }; | ||
184 | |||
185 | struct mwifiex_add_ba_param { | ||
186 | u32 tx_win_size; | ||
187 | u32 rx_win_size; | ||
188 | u32 timeout; | ||
189 | }; | ||
190 | |||
191 | struct mwifiex_tx_aggr { | ||
192 | u8 ampdu_user; | ||
193 | u8 ampdu_ap; | ||
194 | u8 amsdu; | ||
195 | }; | ||
196 | |||
197 | struct mwifiex_ra_list_tbl { | ||
198 | struct list_head list; | ||
199 | struct sk_buff_head skb_head; | ||
200 | u8 ra[ETH_ALEN]; | ||
201 | u32 total_pkts_size; | ||
202 | u32 is_11n_enabled; | ||
203 | }; | ||
204 | |||
205 | struct mwifiex_tid_tbl { | ||
206 | struct list_head ra_list; | ||
207 | /* spin lock for tid table */ | ||
208 | spinlock_t tid_tbl_lock; | ||
209 | struct mwifiex_ra_list_tbl *ra_list_curr; | ||
210 | }; | ||
211 | |||
212 | #define WMM_HIGHEST_PRIORITY 7 | ||
213 | #define HIGH_PRIO_TID 7 | ||
214 | #define LOW_PRIO_TID 0 | ||
215 | |||
216 | struct mwifiex_wmm_desc { | ||
217 | struct mwifiex_tid_tbl tid_tbl_ptr[MAX_NUM_TID]; | ||
218 | u32 packets_out[MAX_NUM_TID]; | ||
219 | /* spin lock to protect ra_list */ | ||
220 | spinlock_t ra_list_spinlock; | ||
221 | struct mwifiex_wmm_ac_status ac_status[IEEE80211_MAX_QUEUES]; | ||
222 | enum mwifiex_wmm_ac_e ac_down_graded_vals[IEEE80211_MAX_QUEUES]; | ||
223 | u32 drv_pkt_delay_max; | ||
224 | u8 queue_priority[IEEE80211_MAX_QUEUES]; | ||
225 | u32 user_pri_pkt_tx_ctrl[WMM_HIGHEST_PRIORITY + 1]; /* UP: 0 to 7 */ | ||
226 | |||
227 | }; | ||
228 | |||
229 | struct mwifiex_802_11_security { | ||
230 | u8 wpa_enabled; | ||
231 | u8 wpa2_enabled; | ||
232 | u8 wapi_enabled; | ||
233 | u8 wapi_key_on; | ||
234 | enum MWIFIEX_802_11_WEP_STATUS wep_status; | ||
235 | u32 authentication_mode; | ||
236 | u32 encryption_mode; | ||
237 | }; | ||
238 | |||
239 | struct ieee_types_header { | ||
240 | u8 element_id; | ||
241 | u8 len; | ||
242 | } __packed; | ||
243 | |||
244 | struct ieee_obss_scan_param { | ||
245 | u16 obss_scan_passive_dwell; | ||
246 | u16 obss_scan_active_dwell; | ||
247 | u16 bss_chan_width_trigger_scan_int; | ||
248 | u16 obss_scan_passive_total; | ||
249 | u16 obss_scan_active_total; | ||
250 | u16 bss_width_chan_trans_delay; | ||
251 | u16 obss_scan_active_threshold; | ||
252 | } __packed; | ||
253 | |||
254 | struct ieee_types_obss_scan_param { | ||
255 | struct ieee_types_header ieee_hdr; | ||
256 | struct ieee_obss_scan_param obss_scan; | ||
257 | } __packed; | ||
258 | |||
259 | #define MWIFIEX_SUPPORTED_RATES 14 | ||
260 | |||
261 | #define MWIFIEX_SUPPORTED_RATES_EXT 32 | ||
262 | |||
263 | #define IEEE_MAX_IE_SIZE 256 | ||
264 | |||
265 | struct ieee_types_vendor_specific { | ||
266 | struct ieee_types_vendor_header vend_hdr; | ||
267 | u8 data[IEEE_MAX_IE_SIZE - sizeof(struct ieee_types_vendor_header)]; | ||
268 | } __packed; | ||
269 | |||
270 | struct ieee_types_generic { | ||
271 | struct ieee_types_header ieee_hdr; | ||
272 | u8 data[IEEE_MAX_IE_SIZE - sizeof(struct ieee_types_header)]; | ||
273 | } __packed; | ||
274 | |||
275 | struct mwifiex_bssdescriptor { | ||
276 | u8 mac_address[ETH_ALEN]; | ||
277 | struct mwifiex_802_11_ssid ssid; | ||
278 | u32 privacy; | ||
279 | s32 rssi; | ||
280 | u32 channel; | ||
281 | u32 freq; | ||
282 | u16 beacon_period; | ||
283 | u8 erp_flags; | ||
284 | u32 bss_mode; | ||
285 | u8 supported_rates[MWIFIEX_SUPPORTED_RATES]; | ||
286 | u8 data_rates[MWIFIEX_SUPPORTED_RATES]; | ||
287 | /* Network band. | ||
288 | * BAND_B(0x01): 'b' band | ||
289 | * BAND_G(0x02): 'g' band | ||
290 | * BAND_A(0X04): 'a' band | ||
291 | */ | ||
292 | u16 bss_band; | ||
293 | long long network_tsf; | ||
294 | u8 time_stamp[8]; | ||
295 | union ieee_types_phy_param_set phy_param_set; | ||
296 | union ieee_types_ss_param_set ss_param_set; | ||
297 | u16 cap_info_bitmap; | ||
298 | struct ieee_types_wmm_parameter wmm_ie; | ||
299 | u8 disable_11n; | ||
300 | struct ieee80211_ht_cap *bcn_ht_cap; | ||
301 | u16 ht_cap_offset; | ||
302 | struct ieee80211_ht_info *bcn_ht_info; | ||
303 | u16 ht_info_offset; | ||
304 | u8 *bcn_bss_co_2040; | ||
305 | u16 bss_co_2040_offset; | ||
306 | u8 *bcn_ext_cap; | ||
307 | u16 ext_cap_offset; | ||
308 | struct ieee_types_obss_scan_param *bcn_obss_scan; | ||
309 | u16 overlap_bss_offset; | ||
310 | struct ieee_types_vendor_specific *bcn_wpa_ie; | ||
311 | u16 wpa_offset; | ||
312 | struct ieee_types_generic *bcn_rsn_ie; | ||
313 | u16 rsn_offset; | ||
314 | struct ieee_types_generic *bcn_wapi_ie; | ||
315 | u16 wapi_offset; | ||
316 | u8 *beacon_buf; | ||
317 | u32 beacon_buf_size; | ||
318 | u32 beacon_buf_size_max; | ||
319 | |||
320 | }; | ||
321 | |||
322 | struct mwifiex_current_bss_params { | ||
323 | struct mwifiex_bssdescriptor bss_descriptor; | ||
324 | u8 wmm_enabled; | ||
325 | u8 wmm_uapsd_enabled; | ||
326 | u8 band; | ||
327 | u32 num_of_rates; | ||
328 | u8 data_rates[MWIFIEX_SUPPORTED_RATES]; | ||
329 | }; | ||
330 | |||
331 | struct mwifiex_sleep_params { | ||
332 | u16 sp_error; | ||
333 | u16 sp_offset; | ||
334 | u16 sp_stable_time; | ||
335 | u8 sp_cal_control; | ||
336 | u8 sp_ext_sleep_clk; | ||
337 | u16 sp_reserved; | ||
338 | }; | ||
339 | |||
340 | struct mwifiex_sleep_period { | ||
341 | u16 period; | ||
342 | u16 reserved; | ||
343 | }; | ||
344 | |||
345 | struct mwifiex_wep_key { | ||
346 | u32 length; | ||
347 | u32 key_index; | ||
348 | u32 key_length; | ||
349 | u8 key_material[MWIFIEX_KEY_BUFFER_SIZE]; | ||
350 | }; | ||
351 | |||
352 | #define MAX_REGION_CHANNEL_NUM 2 | ||
353 | |||
354 | struct mwifiex_chan_freq_power { | ||
355 | u16 channel; | ||
356 | u32 freq; | ||
357 | u16 max_tx_power; | ||
358 | u8 unsupported; | ||
359 | }; | ||
360 | |||
361 | enum state_11d_t { | ||
362 | DISABLE_11D = 0, | ||
363 | ENABLE_11D = 1, | ||
364 | }; | ||
365 | |||
366 | #define MWIFIEX_MAX_TRIPLET_802_11D 83 | ||
367 | |||
368 | struct mwifiex_802_11d_domain_reg { | ||
369 | u8 country_code[IEEE80211_COUNTRY_STRING_LEN]; | ||
370 | u8 no_of_triplet; | ||
371 | struct ieee80211_country_ie_triplet | ||
372 | triplet[MWIFIEX_MAX_TRIPLET_802_11D]; | ||
373 | }; | ||
374 | |||
375 | struct mwifiex_vendor_spec_cfg_ie { | ||
376 | u16 mask; | ||
377 | u16 flag; | ||
378 | u8 ie[MWIFIEX_MAX_VSIE_LEN]; | ||
379 | }; | ||
380 | |||
381 | struct wps { | ||
382 | u8 session_enable; | ||
383 | }; | ||
384 | |||
385 | struct mwifiex_adapter; | ||
386 | struct mwifiex_private; | ||
387 | |||
388 | struct mwifiex_private { | ||
389 | struct mwifiex_adapter *adapter; | ||
390 | u8 bss_index; | ||
391 | u8 bss_type; | ||
392 | u8 bss_role; | ||
393 | u8 bss_priority; | ||
394 | u8 bss_num; | ||
395 | u8 frame_type; | ||
396 | u8 curr_addr[ETH_ALEN]; | ||
397 | u8 media_connected; | ||
398 | u32 num_tx_timeout; | ||
399 | struct net_device *netdev; | ||
400 | struct net_device_stats stats; | ||
401 | u16 curr_pkt_filter; | ||
402 | u32 bss_mode; | ||
403 | u32 pkt_tx_ctrl; | ||
404 | u16 tx_power_level; | ||
405 | u8 max_tx_power_level; | ||
406 | u8 min_tx_power_level; | ||
407 | u8 tx_rate; | ||
408 | u8 tx_htinfo; | ||
409 | u8 rxpd_htinfo; | ||
410 | u8 rxpd_rate; | ||
411 | u16 rate_bitmap; | ||
412 | u16 bitmap_rates[MAX_BITMAP_RATES_SIZE]; | ||
413 | u32 data_rate; | ||
414 | u8 is_data_rate_auto; | ||
415 | u16 bcn_avg_factor; | ||
416 | u16 data_avg_factor; | ||
417 | s16 data_rssi_last; | ||
418 | s16 data_nf_last; | ||
419 | s16 data_rssi_avg; | ||
420 | s16 data_nf_avg; | ||
421 | s16 bcn_rssi_last; | ||
422 | s16 bcn_nf_last; | ||
423 | s16 bcn_rssi_avg; | ||
424 | s16 bcn_nf_avg; | ||
425 | struct mwifiex_bssdescriptor *attempted_bss_desc; | ||
426 | struct mwifiex_802_11_ssid prev_ssid; | ||
427 | u8 prev_bssid[ETH_ALEN]; | ||
428 | struct mwifiex_current_bss_params curr_bss_params; | ||
429 | u16 beacon_period; | ||
430 | u16 listen_interval; | ||
431 | u16 atim_window; | ||
432 | u8 adhoc_channel; | ||
433 | u8 adhoc_is_link_sensed; | ||
434 | u8 adhoc_state; | ||
435 | struct mwifiex_802_11_security sec_info; | ||
436 | struct mwifiex_wep_key wep_key[NUM_WEP_KEYS]; | ||
437 | u16 wep_key_curr_index; | ||
438 | u8 wpa_ie[256]; | ||
439 | u8 wpa_ie_len; | ||
440 | u8 wpa_is_gtk_set; | ||
441 | struct host_cmd_ds_802_11_key_material aes_key; | ||
442 | u8 wapi_ie[256]; | ||
443 | u8 wapi_ie_len; | ||
444 | u8 wmm_required; | ||
445 | u8 wmm_enabled; | ||
446 | u8 wmm_qosinfo; | ||
447 | struct mwifiex_wmm_desc wmm; | ||
448 | struct list_head tx_ba_stream_tbl_ptr; | ||
449 | /* spin lock for tx_ba_stream_tbl_ptr queue */ | ||
450 | spinlock_t tx_ba_stream_tbl_lock; | ||
451 | struct mwifiex_tx_aggr aggr_prio_tbl[MAX_NUM_TID]; | ||
452 | struct mwifiex_add_ba_param add_ba_param; | ||
453 | u16 rx_seq[MAX_NUM_TID]; | ||
454 | struct list_head rx_reorder_tbl_ptr; | ||
455 | /* spin lock for rx_reorder_tbl_ptr queue */ | ||
456 | spinlock_t rx_reorder_tbl_lock; | ||
457 | /* spin lock for Rx packets */ | ||
458 | spinlock_t rx_pkt_lock; | ||
459 | |||
460 | #define MWIFIEX_ASSOC_RSP_BUF_SIZE 500 | ||
461 | u8 assoc_rsp_buf[MWIFIEX_ASSOC_RSP_BUF_SIZE]; | ||
462 | u32 assoc_rsp_size; | ||
463 | |||
464 | #define MWIFIEX_GENIE_BUF_SIZE 256 | ||
465 | u8 gen_ie_buf[MWIFIEX_GENIE_BUF_SIZE]; | ||
466 | u8 gen_ie_buf_len; | ||
467 | |||
468 | struct mwifiex_vendor_spec_cfg_ie vs_ie[MWIFIEX_MAX_VSIE_NUM]; | ||
469 | |||
470 | #define MWIFIEX_ASSOC_TLV_BUF_SIZE 256 | ||
471 | u8 assoc_tlv_buf[MWIFIEX_ASSOC_TLV_BUF_SIZE]; | ||
472 | u8 assoc_tlv_buf_len; | ||
473 | |||
474 | u8 *curr_bcn_buf; | ||
475 | u32 curr_bcn_size; | ||
476 | /* spin lock for beacon buffer */ | ||
477 | spinlock_t curr_bcn_buf_lock; | ||
478 | u16 ioctl_wait_q_woken; | ||
479 | wait_queue_head_t ioctl_wait_q; | ||
480 | u16 cmd_wait_q_woken; | ||
481 | wait_queue_head_t cmd_wait_q; | ||
482 | struct wireless_dev *wdev; | ||
483 | struct mwifiex_chan_freq_power cfp; | ||
484 | char version_str[128]; | ||
485 | #ifdef CONFIG_DEBUG_FS | ||
486 | struct dentry *dfs_dev_dir; | ||
487 | #endif | ||
488 | u8 nick_name[16]; | ||
489 | struct iw_statistics w_stats; | ||
490 | u16 w_stats_wait_q_woken; | ||
491 | wait_queue_head_t w_stats_wait_q; | ||
492 | u16 current_key_index; | ||
493 | struct semaphore async_sem; | ||
494 | u8 scan_pending_on_block; | ||
495 | u8 report_scan_result; | ||
496 | struct cfg80211_scan_request *scan_request; | ||
497 | int scan_result_status; | ||
498 | bool assoc_request; | ||
499 | u16 assoc_result; | ||
500 | bool ibss_join_request; | ||
501 | u16 ibss_join_result; | ||
502 | bool disconnect; | ||
503 | u8 cfg_bssid[6]; | ||
504 | struct workqueue_struct *workqueue; | ||
505 | struct work_struct cfg_workqueue; | ||
506 | u8 country_code[IEEE80211_COUNTRY_STRING_LEN]; | ||
507 | struct wps wps; | ||
508 | u8 scan_block; | ||
509 | }; | ||
510 | |||
511 | enum mwifiex_ba_status { | ||
512 | BA_STREAM_NOT_SETUP = 0, | ||
513 | BA_STREAM_SETUP_INPROGRESS, | ||
514 | BA_STREAM_SETUP_COMPLETE | ||
515 | }; | ||
516 | |||
517 | struct mwifiex_tx_ba_stream_tbl { | ||
518 | struct list_head list; | ||
519 | int tid; | ||
520 | u8 ra[ETH_ALEN]; | ||
521 | enum mwifiex_ba_status ba_status; | ||
522 | }; | ||
523 | |||
524 | struct mwifiex_rx_reorder_tbl; | ||
525 | |||
526 | struct reorder_tmr_cnxt { | ||
527 | struct timer_list timer; | ||
528 | struct mwifiex_rx_reorder_tbl *ptr; | ||
529 | struct mwifiex_private *priv; | ||
530 | }; | ||
531 | |||
532 | struct mwifiex_rx_reorder_tbl { | ||
533 | struct list_head list; | ||
534 | int tid; | ||
535 | u8 ta[ETH_ALEN]; | ||
536 | int start_win; | ||
537 | int win_size; | ||
538 | void **rx_reorder_ptr; | ||
539 | struct reorder_tmr_cnxt timer_context; | ||
540 | }; | ||
541 | |||
542 | struct mwifiex_bss_prio_node { | ||
543 | struct list_head list; | ||
544 | struct mwifiex_private *priv; | ||
545 | }; | ||
546 | |||
547 | struct mwifiex_bss_prio_tbl { | ||
548 | struct list_head bss_prio_head; | ||
549 | /* spin lock for bss priority */ | ||
550 | spinlock_t bss_prio_lock; | ||
551 | struct mwifiex_bss_prio_node *bss_prio_cur; | ||
552 | }; | ||
553 | |||
554 | struct cmd_ctrl_node { | ||
555 | struct list_head list; | ||
556 | struct mwifiex_private *priv; | ||
557 | u32 cmd_oid; | ||
558 | u32 cmd_flag; | ||
559 | struct sk_buff *cmd_skb; | ||
560 | struct sk_buff *resp_skb; | ||
561 | void *data_buf; | ||
562 | void *wq_buf; | ||
563 | struct sk_buff *skb; | ||
564 | }; | ||
565 | |||
566 | struct mwifiex_if_ops { | ||
567 | int (*init_if) (struct mwifiex_adapter *); | ||
568 | void (*cleanup_if) (struct mwifiex_adapter *); | ||
569 | int (*check_fw_status) (struct mwifiex_adapter *, u32, int *); | ||
570 | int (*prog_fw) (struct mwifiex_adapter *, struct mwifiex_fw_image *); | ||
571 | int (*register_dev) (struct mwifiex_adapter *); | ||
572 | void (*unregister_dev) (struct mwifiex_adapter *); | ||
573 | int (*enable_int) (struct mwifiex_adapter *); | ||
574 | int (*process_int_status) (struct mwifiex_adapter *); | ||
575 | int (*host_to_card) (struct mwifiex_adapter *, u8, | ||
576 | u8 *payload, u32 pkt_len, | ||
577 | struct mwifiex_tx_param *); | ||
578 | int (*wakeup) (struct mwifiex_adapter *); | ||
579 | int (*wakeup_complete) (struct mwifiex_adapter *); | ||
580 | |||
581 | void (*update_mp_end_port) (struct mwifiex_adapter *, u16); | ||
582 | void (*cleanup_mpa_buf) (struct mwifiex_adapter *); | ||
583 | }; | ||
584 | |||
585 | struct mwifiex_adapter { | ||
586 | struct mwifiex_private *priv[MWIFIEX_MAX_BSS_NUM]; | ||
587 | u8 priv_num; | ||
588 | struct mwifiex_drv_mode *drv_mode; | ||
589 | const struct firmware *firmware; | ||
590 | struct device *dev; | ||
591 | bool surprise_removed; | ||
592 | u32 fw_release_number; | ||
593 | u32 revision_id; | ||
594 | u16 init_wait_q_woken; | ||
595 | wait_queue_head_t init_wait_q; | ||
596 | void *card; | ||
597 | struct mwifiex_if_ops if_ops; | ||
598 | atomic_t rx_pending; | ||
599 | atomic_t tx_pending; | ||
600 | atomic_t ioctl_pending; | ||
601 | struct workqueue_struct *workqueue; | ||
602 | struct work_struct main_work; | ||
603 | struct mwifiex_bss_prio_tbl bss_prio_tbl[MWIFIEX_MAX_BSS_NUM]; | ||
604 | /* spin lock for init/shutdown */ | ||
605 | spinlock_t mwifiex_lock; | ||
606 | /* spin lock for main process */ | ||
607 | spinlock_t main_proc_lock; | ||
608 | u32 mwifiex_processing; | ||
609 | u16 max_tx_buf_size; | ||
610 | u16 tx_buf_size; | ||
611 | u16 curr_tx_buf_size; | ||
612 | u32 ioport; | ||
613 | enum MWIFIEX_HARDWARE_STATUS hw_status; | ||
614 | u16 radio_on; | ||
615 | u16 number_of_antenna; | ||
616 | u32 fw_cap_info; | ||
617 | /* spin lock for interrupt handling */ | ||
618 | spinlock_t int_lock; | ||
619 | u8 int_status; | ||
620 | u32 event_cause; | ||
621 | struct sk_buff *event_skb; | ||
622 | u8 upld_buf[MWIFIEX_UPLD_SIZE]; | ||
623 | u8 data_sent; | ||
624 | u8 cmd_sent; | ||
625 | u8 cmd_resp_received; | ||
626 | u8 event_received; | ||
627 | u8 data_received; | ||
628 | u16 seq_num; | ||
629 | struct cmd_ctrl_node *cmd_pool; | ||
630 | struct cmd_ctrl_node *curr_cmd; | ||
631 | /* spin lock for command */ | ||
632 | spinlock_t mwifiex_cmd_lock; | ||
633 | u32 num_cmd_timeout; | ||
634 | u16 last_init_cmd; | ||
635 | struct timer_list cmd_timer; | ||
636 | struct list_head cmd_free_q; | ||
637 | /* spin lock for cmd_free_q */ | ||
638 | spinlock_t cmd_free_q_lock; | ||
639 | struct list_head cmd_pending_q; | ||
640 | /* spin lock for cmd_pending_q */ | ||
641 | spinlock_t cmd_pending_q_lock; | ||
642 | struct list_head scan_pending_q; | ||
643 | /* spin lock for scan_pending_q */ | ||
644 | spinlock_t scan_pending_q_lock; | ||
645 | u32 scan_processing; | ||
646 | u16 region_code; | ||
647 | struct mwifiex_802_11d_domain_reg domain_reg; | ||
648 | struct mwifiex_bssdescriptor *scan_table; | ||
649 | u32 num_in_scan_table; | ||
650 | u16 scan_probes; | ||
651 | u32 scan_mode; | ||
652 | u16 specific_scan_time; | ||
653 | u16 active_scan_time; | ||
654 | u16 passive_scan_time; | ||
655 | u8 bcn_buf[MAX_SCAN_BEACON_BUFFER]; | ||
656 | u8 *bcn_buf_end; | ||
657 | u8 fw_bands; | ||
658 | u8 adhoc_start_band; | ||
659 | u8 config_bands; | ||
660 | struct mwifiex_chan_scan_param_set *scan_channels; | ||
661 | u8 tx_lock_flag; | ||
662 | struct mwifiex_sleep_params sleep_params; | ||
663 | struct mwifiex_sleep_period sleep_period; | ||
664 | u16 ps_mode; | ||
665 | u32 ps_state; | ||
666 | u8 need_to_wakeup; | ||
667 | u16 multiple_dtim; | ||
668 | u16 local_listen_interval; | ||
669 | u16 null_pkt_interval; | ||
670 | struct sk_buff *sleep_cfm; | ||
671 | u16 bcn_miss_time_out; | ||
672 | u16 adhoc_awake_period; | ||
673 | u8 is_deep_sleep; | ||
674 | u8 delay_null_pkt; | ||
675 | u16 delay_to_ps; | ||
676 | u16 enhanced_ps_mode; | ||
677 | u8 pm_wakeup_card_req; | ||
678 | u16 gen_null_pkt; | ||
679 | u16 pps_uapsd_mode; | ||
680 | u32 pm_wakeup_fw_try; | ||
681 | u8 is_hs_configured; | ||
682 | struct mwifiex_hs_config_param hs_cfg; | ||
683 | u8 hs_activated; | ||
684 | u16 hs_activate_wait_q_woken; | ||
685 | wait_queue_head_t hs_activate_wait_q; | ||
686 | bool is_suspended; | ||
687 | u8 event_body[MAX_EVENT_SIZE]; | ||
688 | u32 hw_dot_11n_dev_cap; | ||
689 | u8 hw_dev_mcs_support; | ||
690 | u32 usr_dot_11n_dev_cap; | ||
691 | u8 usr_dev_mcs_support; | ||
692 | u8 adhoc_11n_enabled; | ||
693 | u8 chan_offset; | ||
694 | struct mwifiex_dbg dbg; | ||
695 | u8 arp_filter[ARP_FILTER_MAX_BUF_SIZE]; | ||
696 | u32 arp_filter_size; | ||
697 | }; | ||
698 | |||
699 | int mwifiex_init_lock_list(struct mwifiex_adapter *adapter); | ||
700 | void mwifiex_free_lock_list(struct mwifiex_adapter *adapter); | ||
701 | |||
702 | int mwifiex_init_fw(struct mwifiex_adapter *adapter); | ||
703 | |||
704 | int mwifiex_init_fw_complete(struct mwifiex_adapter *adapter); | ||
705 | |||
706 | int mwifiex_shutdown_drv(struct mwifiex_adapter *adapter); | ||
707 | |||
708 | int mwifiex_shutdown_fw_complete(struct mwifiex_adapter *adapter); | ||
709 | |||
710 | int mwifiex_dnld_fw(struct mwifiex_adapter *, struct mwifiex_fw_image *); | ||
711 | |||
712 | int mwifiex_recv_complete(struct mwifiex_adapter *, | ||
713 | struct sk_buff *skb, | ||
714 | int status); | ||
715 | |||
716 | int mwifiex_recv_packet(struct mwifiex_adapter *, struct sk_buff *skb); | ||
717 | |||
718 | int mwifiex_process_event(struct mwifiex_adapter *adapter); | ||
719 | |||
720 | int mwifiex_ioctl_complete(struct mwifiex_adapter *adapter, | ||
721 | struct mwifiex_wait_queue *ioctl_wq, | ||
722 | int status); | ||
723 | |||
724 | int mwifiex_prepare_cmd(struct mwifiex_private *priv, | ||
725 | uint16_t cmd_no, | ||
726 | u16 cmd_action, | ||
727 | u32 cmd_oid, | ||
728 | void *wait_queue, void *data_buf); | ||
729 | |||
730 | void mwifiex_cmd_timeout_func(unsigned long function_context); | ||
731 | |||
732 | int mwifiex_misc_ioctl_init_shutdown(struct mwifiex_adapter *adapter, | ||
733 | struct mwifiex_wait_queue *wait_queue, | ||
734 | u32 func_init_shutdown); | ||
735 | int mwifiex_get_debug_info(struct mwifiex_private *, | ||
736 | struct mwifiex_debug_info *); | ||
737 | |||
738 | int mwifiex_alloc_cmd_buffer(struct mwifiex_adapter *adapter); | ||
739 | int mwifiex_free_cmd_buffer(struct mwifiex_adapter *adapter); | ||
740 | void mwifiex_cancel_all_pending_cmd(struct mwifiex_adapter *adapter); | ||
741 | void mwifiex_cancel_pending_ioctl(struct mwifiex_adapter *adapter, | ||
742 | struct mwifiex_wait_queue *ioctl_wq); | ||
743 | |||
744 | void mwifiex_insert_cmd_to_free_q(struct mwifiex_adapter *adapter, | ||
745 | struct cmd_ctrl_node *cmd_node); | ||
746 | |||
747 | void mwifiex_insert_cmd_to_pending_q(struct mwifiex_adapter *adapter, | ||
748 | struct cmd_ctrl_node *cmd_node, | ||
749 | u32 addtail); | ||
750 | |||
751 | int mwifiex_exec_next_cmd(struct mwifiex_adapter *adapter); | ||
752 | int mwifiex_process_cmdresp(struct mwifiex_adapter *adapter); | ||
753 | int mwifiex_handle_rx_packet(struct mwifiex_adapter *adapter, | ||
754 | struct sk_buff *skb); | ||
755 | int mwifiex_process_tx(struct mwifiex_private *priv, struct sk_buff *skb, | ||
756 | struct mwifiex_tx_param *tx_param); | ||
757 | int mwifiex_send_null_packet(struct mwifiex_private *priv, u8 flags); | ||
758 | int mwifiex_write_data_complete(struct mwifiex_adapter *adapter, | ||
759 | struct sk_buff *skb, int status); | ||
760 | int mwifiex_recv_packet_complete(struct mwifiex_adapter *, | ||
761 | struct sk_buff *skb, int status); | ||
762 | void mwifiex_clean_txrx(struct mwifiex_private *priv); | ||
763 | u8 mwifiex_check_last_packet_indication(struct mwifiex_private *priv); | ||
764 | void mwifiex_check_ps_cond(struct mwifiex_adapter *adapter); | ||
765 | void mwifiex_process_sleep_confirm_resp(struct mwifiex_adapter *, u8 *, | ||
766 | u32); | ||
767 | int mwifiex_cmd_enh_power_mode(struct mwifiex_private *priv, | ||
768 | struct host_cmd_ds_command *cmd, | ||
769 | u16 cmd_action, uint16_t ps_bitmap, | ||
770 | void *data_buf); | ||
771 | int mwifiex_ret_enh_power_mode(struct mwifiex_private *priv, | ||
772 | struct host_cmd_ds_command *resp, | ||
773 | void *data_buf); | ||
774 | void mwifiex_process_hs_config(struct mwifiex_adapter *adapter); | ||
775 | void mwifiex_hs_activated_event(struct mwifiex_private *priv, | ||
776 | u8 activated); | ||
777 | int mwifiex_ret_802_11_hs_cfg(struct mwifiex_private *priv, | ||
778 | struct host_cmd_ds_command *resp); | ||
779 | int mwifiex_process_rx_packet(struct mwifiex_adapter *adapter, | ||
780 | struct sk_buff *skb); | ||
781 | int mwifiex_sta_prepare_cmd(struct mwifiex_private *, uint16_t cmd_no, | ||
782 | u16 cmd_action, u32 cmd_oid, | ||
783 | void *data_buf, void *cmd_buf); | ||
784 | int mwifiex_process_sta_cmdresp(struct mwifiex_private *, u16 cmdresp_no, | ||
785 | void *cmd_buf, void *ioctl); | ||
786 | int mwifiex_process_sta_rx_packet(struct mwifiex_adapter *, | ||
787 | struct sk_buff *skb); | ||
788 | int mwifiex_process_sta_event(struct mwifiex_private *); | ||
789 | void *mwifiex_process_sta_txpd(struct mwifiex_private *, struct sk_buff *skb); | ||
790 | int mwifiex_sta_init_cmd(struct mwifiex_private *, u8 first_sta); | ||
791 | int mwifiex_scan_networks(struct mwifiex_private *priv, void *wait_queue, | ||
792 | u16 action, | ||
793 | const struct mwifiex_user_scan_cfg | ||
794 | *user_scan_in, struct mwifiex_scan_resp *); | ||
795 | int mwifiex_cmd_802_11_scan(struct mwifiex_private *priv, | ||
796 | struct host_cmd_ds_command *cmd, | ||
797 | void *data_buf); | ||
798 | void mwifiex_queue_scan_cmd(struct mwifiex_private *priv, | ||
799 | struct cmd_ctrl_node *cmd_node); | ||
800 | int mwifiex_ret_802_11_scan(struct mwifiex_private *priv, | ||
801 | struct host_cmd_ds_command *resp, | ||
802 | void *wait_queue); | ||
803 | s32 mwifiex_find_ssid_in_list(struct mwifiex_private *priv, | ||
804 | struct mwifiex_802_11_ssid *ssid, u8 *bssid, | ||
805 | u32 mode); | ||
806 | s32 mwifiex_find_bssid_in_list(struct mwifiex_private *priv, u8 *bssid, | ||
807 | u32 mode); | ||
808 | int mwifiex_find_best_network(struct mwifiex_private *priv, | ||
809 | struct mwifiex_ssid_bssid *req_ssid_bssid); | ||
810 | s32 mwifiex_ssid_cmp(struct mwifiex_802_11_ssid *ssid1, | ||
811 | struct mwifiex_802_11_ssid *ssid2); | ||
812 | int mwifiex_associate(struct mwifiex_private *priv, void *wait_queue, | ||
813 | struct mwifiex_bssdescriptor *bss_desc); | ||
814 | int mwifiex_cmd_802_11_associate(struct mwifiex_private *priv, | ||
815 | struct host_cmd_ds_command | ||
816 | *cmd, void *data_buf); | ||
817 | int mwifiex_ret_802_11_associate(struct mwifiex_private *priv, | ||
818 | struct host_cmd_ds_command *resp, | ||
819 | void *wait_queue); | ||
820 | void mwifiex_reset_connect_state(struct mwifiex_private *priv); | ||
821 | void mwifiex_2040_coex_event(struct mwifiex_private *priv); | ||
822 | u8 mwifiex_band_to_radio_type(u8 band); | ||
823 | int mwifiex_deauthenticate(struct mwifiex_private *priv, | ||
824 | struct mwifiex_wait_queue *wait_queue, | ||
825 | u8 *mac); | ||
826 | int mwifiex_adhoc_start(struct mwifiex_private *priv, void *wait_queue, | ||
827 | struct mwifiex_802_11_ssid *adhoc_ssid); | ||
828 | int mwifiex_adhoc_join(struct mwifiex_private *priv, void *wait_queue, | ||
829 | struct mwifiex_bssdescriptor *bss_desc); | ||
830 | int mwifiex_cmd_802_11_ad_hoc_start(struct mwifiex_private *priv, | ||
831 | struct host_cmd_ds_command *cmd, | ||
832 | void *data_buf); | ||
833 | int mwifiex_cmd_802_11_ad_hoc_join(struct mwifiex_private *priv, | ||
834 | struct host_cmd_ds_command *cmd, | ||
835 | void *data_buf); | ||
836 | int mwifiex_ret_802_11_ad_hoc(struct mwifiex_private *priv, | ||
837 | struct host_cmd_ds_command *resp, | ||
838 | void *wait_queue); | ||
839 | int mwifiex_cmd_802_11_bg_scan_query(struct mwifiex_private *priv, | ||
840 | struct host_cmd_ds_command *cmd, | ||
841 | void *data_buf); | ||
842 | struct mwifiex_chan_freq_power * | ||
843 | mwifiex_get_cfp_by_band_and_channel_from_cfg80211( | ||
844 | struct mwifiex_private *priv, | ||
845 | u8 band, u16 channel); | ||
846 | struct mwifiex_chan_freq_power *mwifiex_get_cfp_by_band_and_freq_from_cfg80211( | ||
847 | struct mwifiex_private *priv, | ||
848 | u8 band, u32 freq); | ||
849 | u32 mwifiex_index_to_data_rate(struct mwifiex_adapter *adapter, u8 index, | ||
850 | u8 ht_info); | ||
851 | u32 mwifiex_find_freq_from_band_chan(u8, u8); | ||
852 | int mwifiex_cmd_append_vsie_tlv(struct mwifiex_private *priv, u16 vsie_mask, | ||
853 | u8 **buffer); | ||
854 | u32 mwifiex_index_to_data_rate(struct mwifiex_adapter *adapter, u8 index, | ||
855 | u8 ht_info); | ||
856 | u32 mwifiex_get_active_data_rates(struct mwifiex_private *priv, | ||
857 | u8 *rates); | ||
858 | u32 mwifiex_get_supported_rates(struct mwifiex_private *priv, u8 *rates); | ||
859 | u8 mwifiex_data_rate_to_index(struct mwifiex_adapter *adapter, u32 rate); | ||
860 | u8 mwifiex_is_rate_auto(struct mwifiex_private *priv); | ||
861 | int mwifiex_get_rate_index(struct mwifiex_adapter *adapter, | ||
862 | u16 *rateBitmap, int size); | ||
863 | extern u16 region_code_index[MWIFIEX_MAX_REGION_CODE]; | ||
864 | void mwifiex_save_curr_bcn(struct mwifiex_private *priv); | ||
865 | void mwifiex_free_curr_bcn(struct mwifiex_private *priv); | ||
866 | int mwifiex_cmd_get_hw_spec(struct mwifiex_private *priv, | ||
867 | struct host_cmd_ds_command *cmd); | ||
868 | int mwifiex_ret_get_hw_spec(struct mwifiex_private *priv, | ||
869 | struct host_cmd_ds_command *resp); | ||
870 | int is_command_pending(struct mwifiex_adapter *adapter); | ||
871 | |||
872 | /* | ||
873 | * This function checks if the queuing is RA based or not. | ||
874 | */ | ||
875 | static inline u8 | ||
876 | mwifiex_queuing_ra_based(struct mwifiex_private *priv) | ||
877 | { | ||
878 | /* | ||
879 | * Currently we assume if we are in Infra, then DA=RA. This might not be | ||
880 | * true in the future | ||
881 | */ | ||
882 | if ((priv->bss_mode == MWIFIEX_BSS_MODE_INFRA) && | ||
883 | (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA)) | ||
884 | return false; | ||
885 | |||
886 | return true; | ||
887 | } | ||
888 | |||
889 | /* | ||
890 | * This function copies rates. | ||
891 | */ | ||
892 | static inline u32 | ||
893 | mwifiex_copy_rates(u8 *dest, u32 pos, u8 *src, int len) | ||
894 | { | ||
895 | int i; | ||
896 | |||
897 | for (i = 0; i < len && src[i]; i++, pos++) { | ||
898 | if (pos >= MWIFIEX_SUPPORTED_RATES) | ||
899 | break; | ||
900 | dest[pos] = src[i]; | ||
901 | } | ||
902 | |||
903 | return pos; | ||
904 | } | ||
905 | |||
906 | /* | ||
907 | * This function returns the correct private structure pointer based | ||
908 | * upon the BSS type and BSS number. | ||
909 | */ | ||
910 | static inline struct mwifiex_private * | ||
911 | mwifiex_get_priv_by_id(struct mwifiex_adapter *adapter, | ||
912 | u32 bss_num, u32 bss_type) | ||
913 | { | ||
914 | int i; | ||
915 | |||
916 | for (i = 0; i < adapter->priv_num; i++) { | ||
917 | if (adapter->priv[i]) { | ||
918 | if ((adapter->priv[i]->bss_num == bss_num) | ||
919 | && (adapter->priv[i]->bss_type == bss_type)) | ||
920 | break; | ||
921 | } | ||
922 | } | ||
923 | return ((i < adapter->priv_num) ? adapter->priv[i] : NULL); | ||
924 | } | ||
925 | |||
926 | /* | ||
927 | * This function returns the first available private structure pointer | ||
928 | * based upon the BSS role. | ||
929 | */ | ||
930 | static inline struct mwifiex_private * | ||
931 | mwifiex_get_priv(struct mwifiex_adapter *adapter, | ||
932 | enum mwifiex_bss_role bss_role) | ||
933 | { | ||
934 | int i; | ||
935 | |||
936 | for (i = 0; i < adapter->priv_num; i++) { | ||
937 | if (adapter->priv[i]) { | ||
938 | if (bss_role == MWIFIEX_BSS_ROLE_ANY || | ||
939 | GET_BSS_ROLE(adapter->priv[i]) == bss_role) | ||
940 | break; | ||
941 | } | ||
942 | } | ||
943 | |||
944 | return ((i < adapter->priv_num) ? adapter->priv[i] : NULL); | ||
945 | } | ||
946 | |||
947 | /* | ||
948 | * This function returns the driver private structure of a network device. | ||
949 | */ | ||
950 | static inline struct mwifiex_private * | ||
951 | mwifiex_netdev_get_priv(struct net_device *dev) | ||
952 | { | ||
953 | return (struct mwifiex_private *) (*(unsigned long *) netdev_priv(dev)); | ||
954 | } | ||
955 | |||
956 | struct mwifiex_wait_queue *mwifiex_alloc_fill_wait_queue( | ||
957 | struct mwifiex_private *, | ||
958 | u8 wait_option); | ||
959 | struct mwifiex_private *mwifiex_bss_index_to_priv(struct mwifiex_adapter | ||
960 | *adapter, u8 bss_index); | ||
961 | int mwifiex_shutdown_fw(struct mwifiex_private *, u8); | ||
962 | |||
963 | int mwifiex_add_card(void *, struct semaphore *, struct mwifiex_if_ops *); | ||
964 | int mwifiex_remove_card(struct mwifiex_adapter *, struct semaphore *); | ||
965 | |||
966 | void mwifiex_get_version(struct mwifiex_adapter *adapter, char *version, | ||
967 | int maxlen); | ||
968 | int mwifiex_request_set_mac_address(struct mwifiex_private *priv); | ||
969 | void mwifiex_request_set_multicast_list(struct mwifiex_private *priv, | ||
970 | struct net_device *dev); | ||
971 | int mwifiex_request_ioctl(struct mwifiex_private *priv, | ||
972 | struct mwifiex_wait_queue *req, | ||
973 | int, u8 wait_option); | ||
974 | int mwifiex_disconnect(struct mwifiex_private *, u8, u8 *); | ||
975 | int mwifiex_bss_start(struct mwifiex_private *priv, | ||
976 | u8 wait_option, | ||
977 | struct mwifiex_ssid_bssid *ssid_bssid); | ||
978 | int mwifiex_set_hs_params(struct mwifiex_private *priv, | ||
979 | u16 action, u8 wait_option, | ||
980 | struct mwifiex_ds_hs_cfg *hscfg); | ||
981 | int mwifiex_cancel_hs(struct mwifiex_private *priv, u8 wait_option); | ||
982 | int mwifiex_enable_hs(struct mwifiex_adapter *adapter); | ||
983 | void mwifiex_process_ioctl_resp(struct mwifiex_private *priv, | ||
984 | struct mwifiex_wait_queue *req); | ||
985 | u32 mwifiex_get_mode(struct mwifiex_private *priv, u8 wait_option); | ||
986 | int mwifiex_get_signal_info(struct mwifiex_private *priv, | ||
987 | u8 wait_option, | ||
988 | struct mwifiex_ds_get_signal *signal); | ||
989 | int mwifiex_drv_get_data_rate(struct mwifiex_private *priv, | ||
990 | struct mwifiex_rate_cfg *rate); | ||
991 | int mwifiex_get_channel_list(struct mwifiex_private *priv, | ||
992 | u8 wait_option, | ||
993 | struct mwifiex_chan_list *chanlist); | ||
994 | int mwifiex_get_scan_table(struct mwifiex_private *priv, | ||
995 | u8 wait_option, | ||
996 | struct mwifiex_scan_resp *scanresp); | ||
997 | int mwifiex_get_auth_mode(struct mwifiex_private *priv, | ||
998 | u8 wait_option, u32 *auth_mode); | ||
999 | int mwifiex_get_encrypt_mode(struct mwifiex_private *priv, | ||
1000 | u8 wait_option, | ||
1001 | u32 *encrypt_mode); | ||
1002 | int mwifiex_enable_wep_key(struct mwifiex_private *priv, u8 wait_option); | ||
1003 | int mwifiex_find_best_bss(struct mwifiex_private *priv, u8 wait_option, | ||
1004 | struct mwifiex_ssid_bssid *ssid_bssid); | ||
1005 | int mwifiex_request_scan(struct mwifiex_private *priv, | ||
1006 | u8 wait_option, | ||
1007 | struct mwifiex_802_11_ssid *req_ssid); | ||
1008 | int mwifiex_set_user_scan_ioctl(struct mwifiex_private *priv, | ||
1009 | struct mwifiex_user_scan_cfg *scan_req); | ||
1010 | int mwifiex_change_adhoc_chan(struct mwifiex_private *priv, int channel); | ||
1011 | int mwifiex_set_radio(struct mwifiex_private *priv, u8 option); | ||
1012 | |||
1013 | int mwifiex_drv_get_mode(struct mwifiex_private *priv, u8 wait_option); | ||
1014 | |||
1015 | int mwifiex_drv_change_adhoc_chan(struct mwifiex_private *priv, int channel); | ||
1016 | |||
1017 | int mwifiex_set_auth(struct mwifiex_private *priv, int encrypt_mode, | ||
1018 | int auth_mode, int wpa_enabled); | ||
1019 | |||
1020 | int mwifiex_set_encode(struct mwifiex_private *priv, const u8 *key, | ||
1021 | int key_len, u8 key_index, int disable); | ||
1022 | |||
1023 | int mwifiex_set_gen_ie(struct mwifiex_private *priv, u8 *ie, int ie_len); | ||
1024 | |||
1025 | int mwifiex_get_ver_ext(struct mwifiex_private *priv); | ||
1026 | |||
1027 | int mwifiex_get_stats_info(struct mwifiex_private *priv, | ||
1028 | struct mwifiex_ds_get_stats *log); | ||
1029 | |||
1030 | int mwifiex_reg_write(struct mwifiex_private *priv, u32 reg_type, | ||
1031 | u32 reg_offset, u32 reg_value); | ||
1032 | |||
1033 | int mwifiex_reg_read(struct mwifiex_private *priv, u32 reg_type, | ||
1034 | u32 reg_offset, u32 *value); | ||
1035 | |||
1036 | int mwifiex_eeprom_read(struct mwifiex_private *priv, u16 offset, u16 bytes, | ||
1037 | u8 *value); | ||
1038 | |||
1039 | int mwifiex_set_11n_httx_cfg(struct mwifiex_private *priv, int data); | ||
1040 | |||
1041 | int mwifiex_get_11n_httx_cfg(struct mwifiex_private *priv, int *data); | ||
1042 | |||
1043 | int mwifiex_set_tx_rate_cfg(struct mwifiex_private *priv, int tx_rate_index); | ||
1044 | |||
1045 | int mwifiex_get_tx_rate_cfg(struct mwifiex_private *priv, int *tx_rate_index); | ||
1046 | |||
1047 | int mwifiex_drv_set_power(struct mwifiex_private *priv, bool power_on); | ||
1048 | |||
1049 | int mwifiex_drv_get_driver_version(struct mwifiex_adapter *adapter, | ||
1050 | char *version, int max_len); | ||
1051 | |||
1052 | int mwifiex_set_tx_power(struct mwifiex_private *priv, int type, int dbm); | ||
1053 | |||
1054 | int mwifiex_main_process(struct mwifiex_adapter *); | ||
1055 | |||
1056 | int mwifiex_bss_ioctl_mode(struct mwifiex_private *, | ||
1057 | struct mwifiex_wait_queue *, | ||
1058 | u16 action, int *mode); | ||
1059 | int mwifiex_bss_ioctl_channel(struct mwifiex_private *, | ||
1060 | u16 action, | ||
1061 | struct mwifiex_chan_freq_power *cfp); | ||
1062 | int mwifiex_bss_ioctl_find_bss(struct mwifiex_private *, | ||
1063 | struct mwifiex_wait_queue *, | ||
1064 | struct mwifiex_ssid_bssid *); | ||
1065 | int mwifiex_radio_ioctl_band_cfg(struct mwifiex_private *, | ||
1066 | u16 action, | ||
1067 | struct mwifiex_ds_band_cfg *); | ||
1068 | int mwifiex_snmp_mib_ioctl(struct mwifiex_private *, | ||
1069 | struct mwifiex_wait_queue *, | ||
1070 | u32 cmd_oid, u16 action, u32 *value); | ||
1071 | int mwifiex_get_bss_info(struct mwifiex_private *, | ||
1072 | struct mwifiex_bss_info *); | ||
1073 | |||
1074 | #ifdef CONFIG_DEBUG_FS | ||
1075 | void mwifiex_debugfs_init(void); | ||
1076 | void mwifiex_debugfs_remove(void); | ||
1077 | |||
1078 | void mwifiex_dev_debugfs_init(struct mwifiex_private *priv); | ||
1079 | void mwifiex_dev_debugfs_remove(struct mwifiex_private *priv); | ||
1080 | #endif | ||
1081 | #endif /* !_MWIFIEX_MAIN_H_ */ | ||
diff --git a/drivers/net/wireless/mwifiex/scan.c b/drivers/net/wireless/mwifiex/scan.c new file mode 100644 index 000000000000..1152beb930ab --- /dev/null +++ b/drivers/net/wireless/mwifiex/scan.c | |||
@@ -0,0 +1,3098 @@ | |||
1 | /* | ||
2 | * Marvell Wireless LAN device driver: scan ioctl and command handling | ||
3 | * | ||
4 | * Copyright (C) 2011, Marvell International Ltd. | ||
5 | * | ||
6 | * This software file (the "File") is distributed by Marvell International | ||
7 | * Ltd. under the terms of the GNU General Public License Version 2, June 1991 | ||
8 | * (the "License"). You may use, redistribute and/or modify this File in | ||
9 | * accordance with the terms and conditions of the License, a copy of which | ||
10 | * is available by writing to the Free Software Foundation, Inc., | ||
11 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the | ||
12 | * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. | ||
13 | * | ||
14 | * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE | ||
15 | * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE | ||
16 | * ARE EXPRESSLY DISCLAIMED. The License provides additional details about | ||
17 | * this warranty disclaimer. | ||
18 | */ | ||
19 | |||
20 | #include "decl.h" | ||
21 | #include "ioctl.h" | ||
22 | #include "util.h" | ||
23 | #include "fw.h" | ||
24 | #include "main.h" | ||
25 | #include "11n.h" | ||
26 | #include "cfg80211.h" | ||
27 | |||
28 | /* The maximum number of channels the firmware can scan per command */ | ||
29 | #define MWIFIEX_MAX_CHANNELS_PER_SPECIFIC_SCAN 14 | ||
30 | |||
31 | #define MWIFIEX_CHANNELS_PER_SCAN_CMD 4 | ||
32 | |||
33 | /* Memory needed to store a max sized Channel List TLV for a firmware scan */ | ||
34 | #define CHAN_TLV_MAX_SIZE (sizeof(struct mwifiex_ie_types_header) \ | ||
35 | + (MWIFIEX_MAX_CHANNELS_PER_SPECIFIC_SCAN \ | ||
36 | *sizeof(struct mwifiex_chan_scan_param_set))) | ||
37 | |||
38 | /* Memory needed to store supported rate */ | ||
39 | #define RATE_TLV_MAX_SIZE (sizeof(struct mwifiex_ie_types_rates_param_set) \ | ||
40 | + HOSTCMD_SUPPORTED_RATES) | ||
41 | |||
42 | /* Memory needed to store a max number/size WildCard SSID TLV for a firmware | ||
43 | scan */ | ||
44 | #define WILDCARD_SSID_TLV_MAX_SIZE \ | ||
45 | (MWIFIEX_MAX_SSID_LIST_LENGTH * \ | ||
46 | (sizeof(struct mwifiex_ie_types_wildcard_ssid_params) \ | ||
47 | + IEEE80211_MAX_SSID_LEN)) | ||
48 | |||
49 | /* Maximum memory needed for a mwifiex_scan_cmd_config with all TLVs at max */ | ||
50 | #define MAX_SCAN_CFG_ALLOC (sizeof(struct mwifiex_scan_cmd_config) \ | ||
51 | + sizeof(struct mwifiex_ie_types_num_probes) \ | ||
52 | + sizeof(struct mwifiex_ie_types_htcap) \ | ||
53 | + CHAN_TLV_MAX_SIZE \ | ||
54 | + RATE_TLV_MAX_SIZE \ | ||
55 | + WILDCARD_SSID_TLV_MAX_SIZE) | ||
56 | |||
57 | |||
58 | union mwifiex_scan_cmd_config_tlv { | ||
59 | /* Scan configuration (variable length) */ | ||
60 | struct mwifiex_scan_cmd_config config; | ||
61 | /* Max allocated block */ | ||
62 | u8 config_alloc_buf[MAX_SCAN_CFG_ALLOC]; | ||
63 | }; | ||
64 | |||
65 | enum cipher_suite { | ||
66 | CIPHER_SUITE_TKIP, | ||
67 | CIPHER_SUITE_CCMP, | ||
68 | CIPHER_SUITE_MAX | ||
69 | }; | ||
70 | static u8 mwifiex_wpa_oui[CIPHER_SUITE_MAX][4] = { | ||
71 | { 0x00, 0x50, 0xf2, 0x02 }, /* TKIP */ | ||
72 | { 0x00, 0x50, 0xf2, 0x04 }, /* AES */ | ||
73 | }; | ||
74 | static u8 mwifiex_rsn_oui[CIPHER_SUITE_MAX][4] = { | ||
75 | { 0x00, 0x0f, 0xac, 0x02 }, /* TKIP */ | ||
76 | { 0x00, 0x0f, 0xac, 0x04 }, /* AES */ | ||
77 | }; | ||
78 | |||
79 | /* | ||
80 | * This function parses a given IE for a given OUI. | ||
81 | * | ||
82 | * This is used to parse a WPA/RSN IE to find if it has | ||
83 | * a given oui in PTK. | ||
84 | */ | ||
85 | static u8 | ||
86 | mwifiex_search_oui_in_ie(struct ie_body *iebody, u8 *oui) | ||
87 | { | ||
88 | u8 count; | ||
89 | |||
90 | count = iebody->ptk_cnt[0]; | ||
91 | |||
92 | /* There could be multiple OUIs for PTK hence | ||
93 | 1) Take the length. | ||
94 | 2) Check all the OUIs for AES. | ||
95 | 3) If one of them is AES then pass success. */ | ||
96 | while (count) { | ||
97 | if (!memcmp(iebody->ptk_body, oui, sizeof(iebody->ptk_body))) | ||
98 | return MWIFIEX_OUI_PRESENT; | ||
99 | |||
100 | --count; | ||
101 | if (count) | ||
102 | iebody = (struct ie_body *) ((u8 *) iebody + | ||
103 | sizeof(iebody->ptk_body)); | ||
104 | } | ||
105 | |||
106 | pr_debug("info: %s: OUI is not found in PTK\n", __func__); | ||
107 | return MWIFIEX_OUI_NOT_PRESENT; | ||
108 | } | ||
109 | |||
110 | /* | ||
111 | * This function checks if a given OUI is present in a RSN IE. | ||
112 | * | ||
113 | * The function first checks if a RSN IE is present or not in the | ||
114 | * BSS descriptor. It tries to locate the OUI only if such an IE is | ||
115 | * present. | ||
116 | */ | ||
117 | static u8 | ||
118 | mwifiex_is_rsn_oui_present(struct mwifiex_bssdescriptor *bss_desc, u32 cipher) | ||
119 | { | ||
120 | u8 *oui = NULL; | ||
121 | struct ie_body *iebody = NULL; | ||
122 | u8 ret = MWIFIEX_OUI_NOT_PRESENT; | ||
123 | |||
124 | if (((bss_desc->bcn_rsn_ie) && ((*(bss_desc->bcn_rsn_ie)). | ||
125 | ieee_hdr.element_id == WLAN_EID_RSN))) { | ||
126 | iebody = (struct ie_body *) | ||
127 | (((u8 *) bss_desc->bcn_rsn_ie->data) + | ||
128 | RSN_GTK_OUI_OFFSET); | ||
129 | oui = &mwifiex_rsn_oui[cipher][0]; | ||
130 | ret = mwifiex_search_oui_in_ie(iebody, oui); | ||
131 | if (ret) | ||
132 | return ret; | ||
133 | } | ||
134 | return ret; | ||
135 | } | ||
136 | |||
137 | /* | ||
138 | * This function checks if a given OUI is present in a WPA IE. | ||
139 | * | ||
140 | * The function first checks if a WPA IE is present or not in the | ||
141 | * BSS descriptor. It tries to locate the OUI only if such an IE is | ||
142 | * present. | ||
143 | */ | ||
144 | static u8 | ||
145 | mwifiex_is_wpa_oui_present(struct mwifiex_bssdescriptor *bss_desc, u32 cipher) | ||
146 | { | ||
147 | u8 *oui = NULL; | ||
148 | struct ie_body *iebody = NULL; | ||
149 | u8 ret = MWIFIEX_OUI_NOT_PRESENT; | ||
150 | |||
151 | if (((bss_desc->bcn_wpa_ie) && ((*(bss_desc->bcn_wpa_ie)). | ||
152 | vend_hdr.element_id == WLAN_EID_WPA))) { | ||
153 | iebody = (struct ie_body *) bss_desc->bcn_wpa_ie->data; | ||
154 | oui = &mwifiex_wpa_oui[cipher][0]; | ||
155 | ret = mwifiex_search_oui_in_ie(iebody, oui); | ||
156 | if (ret) | ||
157 | return ret; | ||
158 | } | ||
159 | return ret; | ||
160 | } | ||
161 | |||
162 | /* | ||
163 | * This function compares two SSIDs and checks if they match. | ||
164 | */ | ||
165 | s32 | ||
166 | mwifiex_ssid_cmp(struct mwifiex_802_11_ssid *ssid1, | ||
167 | struct mwifiex_802_11_ssid *ssid2) | ||
168 | { | ||
169 | if (!ssid1 || !ssid2 || (ssid1->ssid_len != ssid2->ssid_len)) | ||
170 | return -1; | ||
171 | return memcmp(ssid1->ssid, ssid2->ssid, ssid1->ssid_len); | ||
172 | } | ||
173 | |||
174 | /* | ||
175 | * Sends IOCTL request to get the best BSS. | ||
176 | * | ||
177 | * This function allocates the IOCTL request buffer, fills it | ||
178 | * with requisite parameters and calls the IOCTL handler. | ||
179 | */ | ||
180 | int mwifiex_find_best_bss(struct mwifiex_private *priv, | ||
181 | u8 wait_option, struct mwifiex_ssid_bssid *ssid_bssid) | ||
182 | { | ||
183 | struct mwifiex_wait_queue *wait = NULL; | ||
184 | struct mwifiex_ssid_bssid tmp_ssid_bssid; | ||
185 | int ret = 0; | ||
186 | u8 *mac = NULL; | ||
187 | |||
188 | if (!ssid_bssid) | ||
189 | return -1; | ||
190 | |||
191 | /* Allocate wait request buffer */ | ||
192 | wait = mwifiex_alloc_fill_wait_queue(priv, wait_option); | ||
193 | if (!wait) | ||
194 | return -ENOMEM; | ||
195 | |||
196 | memcpy(&tmp_ssid_bssid, ssid_bssid, | ||
197 | sizeof(struct mwifiex_ssid_bssid)); | ||
198 | ret = mwifiex_bss_ioctl_find_bss(priv, wait, &tmp_ssid_bssid); | ||
199 | |||
200 | if (!ret) { | ||
201 | memcpy(ssid_bssid, &tmp_ssid_bssid, | ||
202 | sizeof(struct mwifiex_ssid_bssid)); | ||
203 | mac = (u8 *) &ssid_bssid->bssid; | ||
204 | dev_dbg(priv->adapter->dev, "cmd: found network: ssid=%s," | ||
205 | " %pM\n", ssid_bssid->ssid.ssid, mac); | ||
206 | } | ||
207 | |||
208 | kfree(wait); | ||
209 | return ret; | ||
210 | } | ||
211 | |||
212 | /* | ||
213 | * Sends IOCTL request to start a scan with user configurations. | ||
214 | * | ||
215 | * This function allocates the IOCTL request buffer, fills it | ||
216 | * with requisite parameters and calls the IOCTL handler. | ||
217 | * | ||
218 | * Upon completion, it also generates a wireless event to notify | ||
219 | * applications. | ||
220 | */ | ||
221 | int mwifiex_set_user_scan_ioctl(struct mwifiex_private *priv, | ||
222 | struct mwifiex_user_scan_cfg *scan_req) | ||
223 | { | ||
224 | struct mwifiex_wait_queue *wait = NULL; | ||
225 | int status = 0; | ||
226 | u8 wait_option = MWIFIEX_IOCTL_WAIT; | ||
227 | |||
228 | /* Allocate an IOCTL request buffer */ | ||
229 | wait = mwifiex_alloc_fill_wait_queue(priv, wait_option); | ||
230 | if (!wait) | ||
231 | return -ENOMEM; | ||
232 | |||
233 | status = mwifiex_scan_networks(priv, wait, HostCmd_ACT_GEN_SET, | ||
234 | scan_req, NULL); | ||
235 | |||
236 | status = mwifiex_request_ioctl(priv, wait, status, wait_option); | ||
237 | |||
238 | if (wait && (status != -EINPROGRESS)) | ||
239 | kfree(wait); | ||
240 | return status; | ||
241 | } | ||
242 | |||
243 | /* | ||
244 | * This function checks if wapi is enabled in driver and scanned network is | ||
245 | * compatible with it. | ||
246 | */ | ||
247 | static bool | ||
248 | mwifiex_is_network_compatible_for_wapi(struct mwifiex_private *priv, | ||
249 | struct mwifiex_bssdescriptor *bss_desc) | ||
250 | { | ||
251 | if (priv->sec_info.wapi_enabled && | ||
252 | (bss_desc->bcn_wapi_ie && | ||
253 | ((*(bss_desc->bcn_wapi_ie)).ieee_hdr.element_id == | ||
254 | WLAN_EID_BSS_AC_ACCESS_DELAY))) { | ||
255 | return true; | ||
256 | } | ||
257 | return false; | ||
258 | } | ||
259 | |||
260 | /* | ||
261 | * This function checks if driver is configured with no security mode and | ||
262 | * scanned network is compatible with it. | ||
263 | */ | ||
264 | static bool | ||
265 | mwifiex_is_network_compatible_for_no_sec(struct mwifiex_private *priv, | ||
266 | struct mwifiex_bssdescriptor *bss_desc) | ||
267 | { | ||
268 | if (priv->sec_info.wep_status == MWIFIEX_802_11_WEP_DISABLED | ||
269 | && !priv->sec_info.wpa_enabled && !priv->sec_info.wpa2_enabled | ||
270 | && ((!bss_desc->bcn_wpa_ie) || | ||
271 | ((*(bss_desc->bcn_wpa_ie)).vend_hdr.element_id != | ||
272 | WLAN_EID_WPA)) | ||
273 | && ((!bss_desc->bcn_rsn_ie) || | ||
274 | ((*(bss_desc->bcn_rsn_ie)).ieee_hdr.element_id != | ||
275 | WLAN_EID_RSN)) | ||
276 | && priv->sec_info.encryption_mode == | ||
277 | MWIFIEX_ENCRYPTION_MODE_NONE && !bss_desc->privacy) { | ||
278 | return true; | ||
279 | } | ||
280 | return false; | ||
281 | } | ||
282 | |||
283 | /* | ||
284 | * This function checks if static WEP is enabled in driver and scanned network | ||
285 | * is compatible with it. | ||
286 | */ | ||
287 | static bool | ||
288 | mwifiex_is_network_compatible_for_static_wep(struct mwifiex_private *priv, | ||
289 | struct mwifiex_bssdescriptor *bss_desc) | ||
290 | { | ||
291 | if (priv->sec_info.wep_status == MWIFIEX_802_11_WEP_ENABLED | ||
292 | && !priv->sec_info.wpa_enabled && !priv->sec_info.wpa2_enabled | ||
293 | && bss_desc->privacy) { | ||
294 | return true; | ||
295 | } | ||
296 | return false; | ||
297 | } | ||
298 | |||
299 | /* | ||
300 | * This function checks if wpa is enabled in driver and scanned network is | ||
301 | * compatible with it. | ||
302 | */ | ||
303 | static bool | ||
304 | mwifiex_is_network_compatible_for_wpa(struct mwifiex_private *priv, | ||
305 | struct mwifiex_bssdescriptor *bss_desc, | ||
306 | int index) | ||
307 | { | ||
308 | if (priv->sec_info.wep_status == MWIFIEX_802_11_WEP_DISABLED | ||
309 | && priv->sec_info.wpa_enabled && !priv->sec_info.wpa2_enabled | ||
310 | && ((bss_desc->bcn_wpa_ie) && ((*(bss_desc->bcn_wpa_ie)).vend_hdr. | ||
311 | element_id == WLAN_EID_WPA)) | ||
312 | /* | ||
313 | * Privacy bit may NOT be set in some APs like | ||
314 | * LinkSys WRT54G && bss_desc->privacy | ||
315 | */ | ||
316 | ) { | ||
317 | dev_dbg(priv->adapter->dev, "info: %s: WPA: index=%d" | ||
318 | " wpa_ie=%#x wpa2_ie=%#x WEP=%s WPA=%s WPA2=%s " | ||
319 | "EncMode=%#x privacy=%#x\n", __func__, index, | ||
320 | (bss_desc->bcn_wpa_ie) ? | ||
321 | (*(bss_desc->bcn_wpa_ie)). | ||
322 | vend_hdr.element_id : 0, | ||
323 | (bss_desc->bcn_rsn_ie) ? | ||
324 | (*(bss_desc->bcn_rsn_ie)). | ||
325 | ieee_hdr.element_id : 0, | ||
326 | (priv->sec_info.wep_status == | ||
327 | MWIFIEX_802_11_WEP_ENABLED) ? "e" : "d", | ||
328 | (priv->sec_info.wpa_enabled) ? "e" : "d", | ||
329 | (priv->sec_info.wpa2_enabled) ? "e" : "d", | ||
330 | priv->sec_info.encryption_mode, | ||
331 | bss_desc->privacy); | ||
332 | return true; | ||
333 | } | ||
334 | return false; | ||
335 | } | ||
336 | |||
337 | /* | ||
338 | * This function checks if wpa2 is enabled in driver and scanned network is | ||
339 | * compatible with it. | ||
340 | */ | ||
341 | static bool | ||
342 | mwifiex_is_network_compatible_for_wpa2(struct mwifiex_private *priv, | ||
343 | struct mwifiex_bssdescriptor *bss_desc, | ||
344 | int index) | ||
345 | { | ||
346 | if (priv->sec_info.wep_status == MWIFIEX_802_11_WEP_DISABLED | ||
347 | && !priv->sec_info.wpa_enabled && priv->sec_info.wpa2_enabled | ||
348 | && ((bss_desc->bcn_rsn_ie) && ((*(bss_desc->bcn_rsn_ie)).ieee_hdr. | ||
349 | element_id == WLAN_EID_RSN)) | ||
350 | /* | ||
351 | * Privacy bit may NOT be set in some APs like | ||
352 | * LinkSys WRT54G && bss_desc->privacy | ||
353 | */ | ||
354 | ) { | ||
355 | dev_dbg(priv->adapter->dev, "info: %s: WPA2: index=%d" | ||
356 | " wpa_ie=%#x wpa2_ie=%#x WEP=%s WPA=%s WPA2=%s " | ||
357 | "EncMode=%#x privacy=%#x\n", __func__, index, | ||
358 | (bss_desc->bcn_wpa_ie) ? | ||
359 | (*(bss_desc->bcn_wpa_ie)). | ||
360 | vend_hdr.element_id : 0, | ||
361 | (bss_desc->bcn_rsn_ie) ? | ||
362 | (*(bss_desc->bcn_rsn_ie)). | ||
363 | ieee_hdr.element_id : 0, | ||
364 | (priv->sec_info.wep_status == | ||
365 | MWIFIEX_802_11_WEP_ENABLED) ? "e" : "d", | ||
366 | (priv->sec_info.wpa_enabled) ? "e" : "d", | ||
367 | (priv->sec_info.wpa2_enabled) ? "e" : "d", | ||
368 | priv->sec_info.encryption_mode, | ||
369 | bss_desc->privacy); | ||
370 | return true; | ||
371 | } | ||
372 | return false; | ||
373 | } | ||
374 | |||
375 | /* | ||
376 | * This function checks if adhoc AES is enabled in driver and scanned network is | ||
377 | * compatible with it. | ||
378 | */ | ||
379 | static bool | ||
380 | mwifiex_is_network_compatible_for_adhoc_aes(struct mwifiex_private *priv, | ||
381 | struct mwifiex_bssdescriptor *bss_desc) | ||
382 | { | ||
383 | if (priv->sec_info.wep_status == MWIFIEX_802_11_WEP_DISABLED | ||
384 | && !priv->sec_info.wpa_enabled && !priv->sec_info.wpa2_enabled | ||
385 | && ((!bss_desc->bcn_wpa_ie) || ((*(bss_desc->bcn_wpa_ie)).vend_hdr. | ||
386 | element_id != WLAN_EID_WPA)) | ||
387 | && ((!bss_desc->bcn_rsn_ie) || ((*(bss_desc->bcn_rsn_ie)).ieee_hdr. | ||
388 | element_id != WLAN_EID_RSN)) | ||
389 | && priv->sec_info.encryption_mode == | ||
390 | MWIFIEX_ENCRYPTION_MODE_NONE && bss_desc->privacy) { | ||
391 | return true; | ||
392 | } | ||
393 | return false; | ||
394 | } | ||
395 | |||
396 | /* | ||
397 | * This function checks if dynamic WEP is enabled in driver and scanned network | ||
398 | * is compatible with it. | ||
399 | */ | ||
400 | static bool | ||
401 | mwifiex_is_network_compatible_for_dynamic_wep(struct mwifiex_private *priv, | ||
402 | struct mwifiex_bssdescriptor *bss_desc, | ||
403 | int index) | ||
404 | { | ||
405 | if (priv->sec_info.wep_status == MWIFIEX_802_11_WEP_DISABLED | ||
406 | && !priv->sec_info.wpa_enabled && !priv->sec_info.wpa2_enabled | ||
407 | && ((!bss_desc->bcn_wpa_ie) || ((*(bss_desc->bcn_wpa_ie)).vend_hdr. | ||
408 | element_id != WLAN_EID_WPA)) | ||
409 | && ((!bss_desc->bcn_rsn_ie) || ((*(bss_desc->bcn_rsn_ie)).ieee_hdr. | ||
410 | element_id != WLAN_EID_RSN)) | ||
411 | && priv->sec_info.encryption_mode != | ||
412 | MWIFIEX_ENCRYPTION_MODE_NONE && bss_desc->privacy) { | ||
413 | dev_dbg(priv->adapter->dev, "info: %s: dynamic " | ||
414 | "WEP: index=%d wpa_ie=%#x wpa2_ie=%#x " | ||
415 | "EncMode=%#x privacy=%#x\n", | ||
416 | __func__, index, | ||
417 | (bss_desc->bcn_wpa_ie) ? | ||
418 | (*(bss_desc->bcn_wpa_ie)). | ||
419 | vend_hdr.element_id : 0, | ||
420 | (bss_desc->bcn_rsn_ie) ? | ||
421 | (*(bss_desc->bcn_rsn_ie)). | ||
422 | ieee_hdr.element_id : 0, | ||
423 | priv->sec_info.encryption_mode, | ||
424 | bss_desc->privacy); | ||
425 | return true; | ||
426 | } | ||
427 | return false; | ||
428 | } | ||
429 | |||
430 | /* | ||
431 | * This function checks if a scanned network is compatible with the driver | ||
432 | * settings. | ||
433 | * | ||
434 | * WEP WPA WPA2 ad-hoc encrypt Network | ||
435 | * enabled enabled enabled AES mode Privacy WPA WPA2 Compatible | ||
436 | * 0 0 0 0 NONE 0 0 0 yes No security | ||
437 | * 0 1 0 0 x 1x 1 x yes WPA (disable | ||
438 | * HT if no AES) | ||
439 | * 0 0 1 0 x 1x x 1 yes WPA2 (disable | ||
440 | * HT if no AES) | ||
441 | * 0 0 0 1 NONE 1 0 0 yes Ad-hoc AES | ||
442 | * 1 0 0 0 NONE 1 0 0 yes Static WEP | ||
443 | * (disable HT) | ||
444 | * 0 0 0 0 !=NONE 1 0 0 yes Dynamic WEP | ||
445 | * | ||
446 | * Compatibility is not matched while roaming, except for mode. | ||
447 | */ | ||
448 | static s32 | ||
449 | mwifiex_is_network_compatible(struct mwifiex_private *priv, u32 index, u32 mode) | ||
450 | { | ||
451 | struct mwifiex_adapter *adapter = priv->adapter; | ||
452 | struct mwifiex_bssdescriptor *bss_desc; | ||
453 | |||
454 | bss_desc = &adapter->scan_table[index]; | ||
455 | bss_desc->disable_11n = false; | ||
456 | |||
457 | /* Don't check for compatibility if roaming */ | ||
458 | if (priv->media_connected && (priv->bss_mode == MWIFIEX_BSS_MODE_INFRA) | ||
459 | && (bss_desc->bss_mode == MWIFIEX_BSS_MODE_INFRA)) | ||
460 | return index; | ||
461 | |||
462 | if (priv->wps.session_enable) { | ||
463 | dev_dbg(adapter->dev, | ||
464 | "info: return success directly in WPS period\n"); | ||
465 | return index; | ||
466 | } | ||
467 | |||
468 | if (mwifiex_is_network_compatible_for_wapi(priv, bss_desc)) { | ||
469 | dev_dbg(adapter->dev, "info: return success for WAPI AP\n"); | ||
470 | return index; | ||
471 | } | ||
472 | |||
473 | if (bss_desc->bss_mode == mode) { | ||
474 | if (mwifiex_is_network_compatible_for_no_sec(priv, bss_desc)) { | ||
475 | /* No security */ | ||
476 | return index; | ||
477 | } else if (mwifiex_is_network_compatible_for_static_wep(priv, | ||
478 | bss_desc)) { | ||
479 | /* Static WEP enabled */ | ||
480 | dev_dbg(adapter->dev, "info: Disable 11n in WEP mode.\n"); | ||
481 | bss_desc->disable_11n = true; | ||
482 | return index; | ||
483 | } else if (mwifiex_is_network_compatible_for_wpa(priv, bss_desc, | ||
484 | index)) { | ||
485 | /* WPA enabled */ | ||
486 | if (((priv->adapter->config_bands & BAND_GN | ||
487 | || priv->adapter->config_bands & BAND_AN) | ||
488 | && bss_desc->bcn_ht_cap) | ||
489 | && !mwifiex_is_wpa_oui_present(bss_desc, | ||
490 | CIPHER_SUITE_CCMP)) { | ||
491 | |||
492 | if (mwifiex_is_wpa_oui_present(bss_desc, | ||
493 | CIPHER_SUITE_TKIP)) { | ||
494 | dev_dbg(adapter->dev, | ||
495 | "info: Disable 11n if AES " | ||
496 | "is not supported by AP\n"); | ||
497 | bss_desc->disable_11n = true; | ||
498 | } else { | ||
499 | return -1; | ||
500 | } | ||
501 | } | ||
502 | return index; | ||
503 | } else if (mwifiex_is_network_compatible_for_wpa2(priv, | ||
504 | bss_desc, index)) { | ||
505 | /* WPA2 enabled */ | ||
506 | if (((priv->adapter->config_bands & BAND_GN | ||
507 | || priv->adapter->config_bands & BAND_AN) | ||
508 | && bss_desc->bcn_ht_cap) | ||
509 | && !mwifiex_is_rsn_oui_present(bss_desc, | ||
510 | CIPHER_SUITE_CCMP)) { | ||
511 | |||
512 | if (mwifiex_is_rsn_oui_present(bss_desc, | ||
513 | CIPHER_SUITE_TKIP)) { | ||
514 | dev_dbg(adapter->dev, | ||
515 | "info: Disable 11n if AES " | ||
516 | "is not supported by AP\n"); | ||
517 | bss_desc->disable_11n = true; | ||
518 | } else { | ||
519 | return -1; | ||
520 | } | ||
521 | } | ||
522 | return index; | ||
523 | } else if (mwifiex_is_network_compatible_for_adhoc_aes(priv, | ||
524 | bss_desc)) { | ||
525 | /* Ad-hoc AES enabled */ | ||
526 | return index; | ||
527 | } else if (mwifiex_is_network_compatible_for_dynamic_wep(priv, | ||
528 | bss_desc, index)) { | ||
529 | /* Dynamic WEP enabled */ | ||
530 | return index; | ||
531 | } | ||
532 | |||
533 | /* Security doesn't match */ | ||
534 | dev_dbg(adapter->dev, "info: %s: failed: index=%d " | ||
535 | "wpa_ie=%#x wpa2_ie=%#x WEP=%s WPA=%s WPA2=%s EncMode" | ||
536 | "=%#x privacy=%#x\n", | ||
537 | __func__, index, | ||
538 | (bss_desc->bcn_wpa_ie) ? | ||
539 | (*(bss_desc->bcn_wpa_ie)).vend_hdr. | ||
540 | element_id : 0, | ||
541 | (bss_desc->bcn_rsn_ie) ? | ||
542 | (*(bss_desc->bcn_rsn_ie)).ieee_hdr. | ||
543 | element_id : 0, | ||
544 | (priv->sec_info.wep_status == | ||
545 | MWIFIEX_802_11_WEP_ENABLED) ? "e" : "d", | ||
546 | (priv->sec_info.wpa_enabled) ? "e" : "d", | ||
547 | (priv->sec_info.wpa2_enabled) ? "e" : "d", | ||
548 | priv->sec_info.encryption_mode, bss_desc->privacy); | ||
549 | return -1; | ||
550 | } | ||
551 | |||
552 | /* Mode doesn't match */ | ||
553 | return -1; | ||
554 | } | ||
555 | |||
556 | /* | ||
557 | * This function finds the best SSID in the scan list. | ||
558 | * | ||
559 | * It searches the scan table for the best SSID that also matches the current | ||
560 | * adapter network preference (mode, security etc.). | ||
561 | */ | ||
562 | static s32 | ||
563 | mwifiex_find_best_network_in_list(struct mwifiex_private *priv) | ||
564 | { | ||
565 | struct mwifiex_adapter *adapter = priv->adapter; | ||
566 | u32 mode = priv->bss_mode; | ||
567 | s32 best_net = -1; | ||
568 | s32 best_rssi = 0; | ||
569 | u32 i; | ||
570 | |||
571 | dev_dbg(adapter->dev, "info: num of BSSIDs = %d\n", | ||
572 | adapter->num_in_scan_table); | ||
573 | |||
574 | for (i = 0; i < adapter->num_in_scan_table; i++) { | ||
575 | switch (mode) { | ||
576 | case MWIFIEX_BSS_MODE_INFRA: | ||
577 | case MWIFIEX_BSS_MODE_IBSS: | ||
578 | if (mwifiex_is_network_compatible(priv, i, mode) >= 0) { | ||
579 | if (SCAN_RSSI(adapter->scan_table[i].rssi) > | ||
580 | best_rssi) { | ||
581 | best_rssi = SCAN_RSSI(adapter-> | ||
582 | scan_table[i].rssi); | ||
583 | best_net = i; | ||
584 | } | ||
585 | } | ||
586 | break; | ||
587 | case MWIFIEX_BSS_MODE_AUTO: | ||
588 | default: | ||
589 | if (SCAN_RSSI(adapter->scan_table[i].rssi) > | ||
590 | best_rssi) { | ||
591 | best_rssi = SCAN_RSSI(adapter->scan_table[i]. | ||
592 | rssi); | ||
593 | best_net = i; | ||
594 | } | ||
595 | break; | ||
596 | } | ||
597 | } | ||
598 | |||
599 | return best_net; | ||
600 | } | ||
601 | |||
602 | /* | ||
603 | * This function creates a channel list for the driver to scan, based | ||
604 | * on region/band information. | ||
605 | * | ||
606 | * This routine is used for any scan that is not provided with a | ||
607 | * specific channel list to scan. | ||
608 | */ | ||
609 | static void | ||
610 | mwifiex_scan_create_channel_list(struct mwifiex_private *priv, | ||
611 | const struct mwifiex_user_scan_cfg | ||
612 | *user_scan_in, | ||
613 | struct mwifiex_chan_scan_param_set | ||
614 | *scan_chan_list, | ||
615 | u8 filtered_scan) | ||
616 | { | ||
617 | enum ieee80211_band band; | ||
618 | struct ieee80211_supported_band *sband; | ||
619 | struct ieee80211_channel *ch; | ||
620 | struct mwifiex_adapter *adapter = priv->adapter; | ||
621 | int chan_idx = 0, i; | ||
622 | u8 scan_type; | ||
623 | |||
624 | for (band = 0; (band < IEEE80211_NUM_BANDS) ; band++) { | ||
625 | |||
626 | if (!priv->wdev->wiphy->bands[band]) | ||
627 | continue; | ||
628 | |||
629 | sband = priv->wdev->wiphy->bands[band]; | ||
630 | |||
631 | for (i = 0; (i < sband->n_channels) ; i++, chan_idx++) { | ||
632 | ch = &sband->channels[i]; | ||
633 | if (ch->flags & IEEE80211_CHAN_DISABLED) | ||
634 | continue; | ||
635 | scan_chan_list[chan_idx].radio_type = band; | ||
636 | scan_type = ch->flags & IEEE80211_CHAN_PASSIVE_SCAN; | ||
637 | if (user_scan_in && | ||
638 | user_scan_in->chan_list[0].scan_time) | ||
639 | scan_chan_list[chan_idx].max_scan_time = | ||
640 | cpu_to_le16((u16) user_scan_in-> | ||
641 | chan_list[0].scan_time); | ||
642 | else if (scan_type == MWIFIEX_SCAN_TYPE_PASSIVE) | ||
643 | scan_chan_list[chan_idx].max_scan_time = | ||
644 | cpu_to_le16(adapter->passive_scan_time); | ||
645 | else | ||
646 | scan_chan_list[chan_idx].max_scan_time = | ||
647 | cpu_to_le16(adapter->active_scan_time); | ||
648 | if (scan_type == MWIFIEX_SCAN_TYPE_PASSIVE) | ||
649 | scan_chan_list[chan_idx].chan_scan_mode_bitmap | ||
650 | |= MWIFIEX_PASSIVE_SCAN; | ||
651 | else | ||
652 | scan_chan_list[chan_idx].chan_scan_mode_bitmap | ||
653 | &= ~MWIFIEX_PASSIVE_SCAN; | ||
654 | scan_chan_list[chan_idx].chan_number = | ||
655 | (u32) ch->hw_value; | ||
656 | if (filtered_scan) { | ||
657 | scan_chan_list[chan_idx].max_scan_time = | ||
658 | cpu_to_le16(adapter->specific_scan_time); | ||
659 | scan_chan_list[chan_idx].chan_scan_mode_bitmap | ||
660 | |= MWIFIEX_DISABLE_CHAN_FILT; | ||
661 | } | ||
662 | } | ||
663 | |||
664 | } | ||
665 | } | ||
666 | |||
667 | /* | ||
668 | * This function constructs and sends multiple scan config commands to | ||
669 | * the firmware. | ||
670 | * | ||
671 | * Previous routines in the code flow have created a scan command configuration | ||
672 | * with any requested TLVs. This function splits the channel TLV into maximum | ||
673 | * channels supported per scan lists and sends the portion of the channel TLV, | ||
674 | * along with the other TLVs, to the firmware. | ||
675 | */ | ||
676 | static int | ||
677 | mwifiex_scan_channel_list(struct mwifiex_private *priv, void *wait_buf, | ||
678 | u32 max_chan_per_scan, u8 filtered_scan, | ||
679 | struct mwifiex_scan_cmd_config *scan_cfg_out, | ||
680 | struct mwifiex_ie_types_chan_list_param_set | ||
681 | *chan_tlv_out, | ||
682 | struct mwifiex_chan_scan_param_set *scan_chan_list) | ||
683 | { | ||
684 | int ret = 0; | ||
685 | struct mwifiex_chan_scan_param_set *tmp_chan_list; | ||
686 | struct mwifiex_chan_scan_param_set *start_chan; | ||
687 | |||
688 | u32 tlv_idx; | ||
689 | u32 total_scan_time; | ||
690 | u32 done_early; | ||
691 | |||
692 | if (!scan_cfg_out || !chan_tlv_out || !scan_chan_list) { | ||
693 | dev_dbg(priv->adapter->dev, | ||
694 | "info: Scan: Null detect: %p, %p, %p\n", | ||
695 | scan_cfg_out, chan_tlv_out, scan_chan_list); | ||
696 | return -1; | ||
697 | } | ||
698 | |||
699 | chan_tlv_out->header.type = cpu_to_le16(TLV_TYPE_CHANLIST); | ||
700 | |||
701 | /* Set the temp channel struct pointer to the start of the desired | ||
702 | list */ | ||
703 | tmp_chan_list = scan_chan_list; | ||
704 | |||
705 | /* Loop through the desired channel list, sending a new firmware scan | ||
706 | commands for each max_chan_per_scan channels (or for 1,6,11 | ||
707 | individually if configured accordingly) */ | ||
708 | while (tmp_chan_list->chan_number) { | ||
709 | |||
710 | tlv_idx = 0; | ||
711 | total_scan_time = 0; | ||
712 | chan_tlv_out->header.len = 0; | ||
713 | start_chan = tmp_chan_list; | ||
714 | done_early = false; | ||
715 | |||
716 | /* | ||
717 | * Construct the Channel TLV for the scan command. Continue to | ||
718 | * insert channel TLVs until: | ||
719 | * - the tlv_idx hits the maximum configured per scan command | ||
720 | * - the next channel to insert is 0 (end of desired channel | ||
721 | * list) | ||
722 | * - done_early is set (controlling individual scanning of | ||
723 | * 1,6,11) | ||
724 | */ | ||
725 | while (tlv_idx < max_chan_per_scan | ||
726 | && tmp_chan_list->chan_number && !done_early) { | ||
727 | |||
728 | dev_dbg(priv->adapter->dev, | ||
729 | "info: Scan: Chan(%3d), Radio(%d)," | ||
730 | " Mode(%d, %d), Dur(%d)\n", | ||
731 | tmp_chan_list->chan_number, | ||
732 | tmp_chan_list->radio_type, | ||
733 | tmp_chan_list->chan_scan_mode_bitmap | ||
734 | & MWIFIEX_PASSIVE_SCAN, | ||
735 | (tmp_chan_list->chan_scan_mode_bitmap | ||
736 | & MWIFIEX_DISABLE_CHAN_FILT) >> 1, | ||
737 | le16_to_cpu(tmp_chan_list->max_scan_time)); | ||
738 | |||
739 | /* Copy the current channel TLV to the command being | ||
740 | prepared */ | ||
741 | memcpy(chan_tlv_out->chan_scan_param + tlv_idx, | ||
742 | tmp_chan_list, | ||
743 | sizeof(chan_tlv_out->chan_scan_param)); | ||
744 | |||
745 | /* Increment the TLV header length by the size | ||
746 | appended */ | ||
747 | chan_tlv_out->header.len = | ||
748 | cpu_to_le16(le16_to_cpu(chan_tlv_out->header.len) + | ||
749 | (sizeof(chan_tlv_out->chan_scan_param))); | ||
750 | |||
751 | /* | ||
752 | * The tlv buffer length is set to the number of bytes | ||
753 | * of the between the channel tlv pointer and the start | ||
754 | * of the tlv buffer. This compensates for any TLVs | ||
755 | * that were appended before the channel list. | ||
756 | */ | ||
757 | scan_cfg_out->tlv_buf_len = (u32) ((u8 *) chan_tlv_out - | ||
758 | scan_cfg_out->tlv_buf); | ||
759 | |||
760 | /* Add the size of the channel tlv header and the data | ||
761 | length */ | ||
762 | scan_cfg_out->tlv_buf_len += | ||
763 | (sizeof(chan_tlv_out->header) | ||
764 | + le16_to_cpu(chan_tlv_out->header.len)); | ||
765 | |||
766 | /* Increment the index to the channel tlv we are | ||
767 | constructing */ | ||
768 | tlv_idx++; | ||
769 | |||
770 | /* Count the total scan time per command */ | ||
771 | total_scan_time += | ||
772 | le16_to_cpu(tmp_chan_list->max_scan_time); | ||
773 | |||
774 | done_early = false; | ||
775 | |||
776 | /* Stop the loop if the *current* channel is in the | ||
777 | 1,6,11 set and we are not filtering on a BSSID | ||
778 | or SSID. */ | ||
779 | if (!filtered_scan && (tmp_chan_list->chan_number == 1 | ||
780 | || tmp_chan_list->chan_number == 6 | ||
781 | || tmp_chan_list->chan_number == 11)) | ||
782 | done_early = true; | ||
783 | |||
784 | /* Increment the tmp pointer to the next channel to | ||
785 | be scanned */ | ||
786 | tmp_chan_list++; | ||
787 | |||
788 | /* Stop the loop if the *next* channel is in the 1,6,11 | ||
789 | set. This will cause it to be the only channel | ||
790 | scanned on the next interation */ | ||
791 | if (!filtered_scan && (tmp_chan_list->chan_number == 1 | ||
792 | || tmp_chan_list->chan_number == 6 | ||
793 | || tmp_chan_list->chan_number == 11)) | ||
794 | done_early = true; | ||
795 | } | ||
796 | |||
797 | /* The total scan time should be less than scan command timeout | ||
798 | value */ | ||
799 | if (total_scan_time > MWIFIEX_MAX_TOTAL_SCAN_TIME) { | ||
800 | dev_err(priv->adapter->dev, "total scan time %dms" | ||
801 | " is over limit (%dms), scan skipped\n", | ||
802 | total_scan_time, MWIFIEX_MAX_TOTAL_SCAN_TIME); | ||
803 | ret = -1; | ||
804 | break; | ||
805 | } | ||
806 | |||
807 | priv->adapter->scan_channels = start_chan; | ||
808 | |||
809 | /* Send the scan command to the firmware with the specified | ||
810 | cfg */ | ||
811 | ret = mwifiex_prepare_cmd(priv, HostCmd_CMD_802_11_SCAN, | ||
812 | HostCmd_ACT_GEN_SET, | ||
813 | 0, wait_buf, scan_cfg_out); | ||
814 | if (ret) | ||
815 | break; | ||
816 | } | ||
817 | |||
818 | if (ret) | ||
819 | return -1; | ||
820 | |||
821 | return 0; | ||
822 | } | ||
823 | |||
824 | /* | ||
825 | * This function constructs a scan command configuration structure to use | ||
826 | * in scan commands. | ||
827 | * | ||
828 | * Application layer or other functions can invoke network scanning | ||
829 | * with a scan configuration supplied in a user scan configuration structure. | ||
830 | * This structure is used as the basis of one or many scan command configuration | ||
831 | * commands that are sent to the command processing module and eventually to the | ||
832 | * firmware. | ||
833 | * | ||
834 | * This function creates a scan command configuration structure based on the | ||
835 | * following user supplied parameters (if present): | ||
836 | * - SSID filter | ||
837 | * - BSSID filter | ||
838 | * - Number of Probes to be sent | ||
839 | * - Channel list | ||
840 | * | ||
841 | * If the SSID or BSSID filter is not present, the filter is disabled/cleared. | ||
842 | * If the number of probes is not set, adapter default setting is used. | ||
843 | */ | ||
844 | static void | ||
845 | mwifiex_scan_setup_scan_config(struct mwifiex_private *priv, | ||
846 | const struct mwifiex_user_scan_cfg *user_scan_in, | ||
847 | struct mwifiex_scan_cmd_config *scan_cfg_out, | ||
848 | struct mwifiex_ie_types_chan_list_param_set | ||
849 | **chan_list_out, | ||
850 | struct mwifiex_chan_scan_param_set | ||
851 | *scan_chan_list, | ||
852 | u8 *max_chan_per_scan, u8 *filtered_scan, | ||
853 | u8 *scan_current_only) | ||
854 | { | ||
855 | struct mwifiex_adapter *adapter = priv->adapter; | ||
856 | struct mwifiex_ie_types_num_probes *num_probes_tlv; | ||
857 | struct mwifiex_ie_types_wildcard_ssid_params *wildcard_ssid_tlv; | ||
858 | struct mwifiex_ie_types_rates_param_set *rates_tlv; | ||
859 | const u8 zero_mac[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 }; | ||
860 | u8 *tlv_pos; | ||
861 | u32 num_probes; | ||
862 | u32 ssid_len; | ||
863 | u32 chan_idx; | ||
864 | u32 scan_type; | ||
865 | u16 scan_dur; | ||
866 | u8 channel; | ||
867 | u8 radio_type; | ||
868 | u32 ssid_idx; | ||
869 | u8 ssid_filter; | ||
870 | u8 rates[MWIFIEX_SUPPORTED_RATES]; | ||
871 | u32 rates_size; | ||
872 | struct mwifiex_ie_types_htcap *ht_cap; | ||
873 | |||
874 | /* The tlv_buf_len is calculated for each scan command. The TLVs added | ||
875 | in this routine will be preserved since the routine that sends the | ||
876 | command will append channelTLVs at *chan_list_out. The difference | ||
877 | between the *chan_list_out and the tlv_buf start will be used to | ||
878 | calculate the size of anything we add in this routine. */ | ||
879 | scan_cfg_out->tlv_buf_len = 0; | ||
880 | |||
881 | /* Running tlv pointer. Assigned to chan_list_out at end of function | ||
882 | so later routines know where channels can be added to the command | ||
883 | buf */ | ||
884 | tlv_pos = scan_cfg_out->tlv_buf; | ||
885 | |||
886 | /* Initialize the scan as un-filtered; the flag is later set to TRUE | ||
887 | below if a SSID or BSSID filter is sent in the command */ | ||
888 | *filtered_scan = false; | ||
889 | |||
890 | /* Initialize the scan as not being only on the current channel. If | ||
891 | the channel list is customized, only contains one channel, and is | ||
892 | the active channel, this is set true and data flow is not halted. */ | ||
893 | *scan_current_only = false; | ||
894 | |||
895 | if (user_scan_in) { | ||
896 | |||
897 | /* Default the ssid_filter flag to TRUE, set false under | ||
898 | certain wildcard conditions and qualified by the existence | ||
899 | of an SSID list before marking the scan as filtered */ | ||
900 | ssid_filter = true; | ||
901 | |||
902 | /* Set the BSS type scan filter, use Adapter setting if | ||
903 | unset */ | ||
904 | scan_cfg_out->bss_mode = | ||
905 | (user_scan_in->bss_mode ? (u8) user_scan_in-> | ||
906 | bss_mode : (u8) adapter->scan_mode); | ||
907 | |||
908 | /* Set the number of probes to send, use Adapter setting | ||
909 | if unset */ | ||
910 | num_probes = | ||
911 | (user_scan_in->num_probes ? user_scan_in-> | ||
912 | num_probes : adapter->scan_probes); | ||
913 | |||
914 | /* | ||
915 | * Set the BSSID filter to the incoming configuration, | ||
916 | * if non-zero. If not set, it will remain disabled | ||
917 | * (all zeros). | ||
918 | */ | ||
919 | memcpy(scan_cfg_out->specific_bssid, | ||
920 | user_scan_in->specific_bssid, | ||
921 | sizeof(scan_cfg_out->specific_bssid)); | ||
922 | |||
923 | for (ssid_idx = 0; | ||
924 | ((ssid_idx < ARRAY_SIZE(user_scan_in->ssid_list)) | ||
925 | && (*user_scan_in->ssid_list[ssid_idx].ssid | ||
926 | || user_scan_in->ssid_list[ssid_idx].max_len)); | ||
927 | ssid_idx++) { | ||
928 | |||
929 | ssid_len = strlen(user_scan_in->ssid_list[ssid_idx]. | ||
930 | ssid) + 1; | ||
931 | |||
932 | wildcard_ssid_tlv = | ||
933 | (struct mwifiex_ie_types_wildcard_ssid_params *) | ||
934 | tlv_pos; | ||
935 | wildcard_ssid_tlv->header.type = | ||
936 | cpu_to_le16(TLV_TYPE_WILDCARDSSID); | ||
937 | wildcard_ssid_tlv->header.len = cpu_to_le16( | ||
938 | (u16) (ssid_len + sizeof(wildcard_ssid_tlv-> | ||
939 | max_ssid_length))); | ||
940 | wildcard_ssid_tlv->max_ssid_length = | ||
941 | user_scan_in->ssid_list[ssid_idx].max_len; | ||
942 | |||
943 | memcpy(wildcard_ssid_tlv->ssid, | ||
944 | user_scan_in->ssid_list[ssid_idx].ssid, | ||
945 | ssid_len); | ||
946 | |||
947 | tlv_pos += (sizeof(wildcard_ssid_tlv->header) | ||
948 | + le16_to_cpu(wildcard_ssid_tlv->header.len)); | ||
949 | |||
950 | dev_dbg(adapter->dev, "info: scan: ssid_list[%d]: %s, %d\n", | ||
951 | ssid_idx, wildcard_ssid_tlv->ssid, | ||
952 | wildcard_ssid_tlv->max_ssid_length); | ||
953 | |||
954 | /* Empty wildcard ssid with a maxlen will match many or | ||
955 | potentially all SSIDs (maxlen == 32), therefore do | ||
956 | not treat the scan as | ||
957 | filtered. */ | ||
958 | if (!ssid_len && wildcard_ssid_tlv->max_ssid_length) | ||
959 | ssid_filter = false; | ||
960 | |||
961 | } | ||
962 | |||
963 | /* | ||
964 | * The default number of channels sent in the command is low to | ||
965 | * ensure the response buffer from the firmware does not | ||
966 | * truncate scan results. That is not an issue with an SSID | ||
967 | * or BSSID filter applied to the scan results in the firmware. | ||
968 | */ | ||
969 | if ((ssid_idx && ssid_filter) | ||
970 | || memcmp(scan_cfg_out->specific_bssid, &zero_mac, | ||
971 | sizeof(zero_mac))) | ||
972 | *filtered_scan = true; | ||
973 | } else { | ||
974 | scan_cfg_out->bss_mode = (u8) adapter->scan_mode; | ||
975 | num_probes = adapter->scan_probes; | ||
976 | } | ||
977 | |||
978 | /* | ||
979 | * If a specific BSSID or SSID is used, the number of channels in the | ||
980 | * scan command will be increased to the absolute maximum. | ||
981 | */ | ||
982 | if (*filtered_scan) | ||
983 | *max_chan_per_scan = MWIFIEX_MAX_CHANNELS_PER_SPECIFIC_SCAN; | ||
984 | else | ||
985 | *max_chan_per_scan = MWIFIEX_CHANNELS_PER_SCAN_CMD; | ||
986 | |||
987 | /* If the input config or adapter has the number of Probes set, | ||
988 | add tlv */ | ||
989 | if (num_probes) { | ||
990 | |||
991 | dev_dbg(adapter->dev, "info: scan: num_probes = %d\n", | ||
992 | num_probes); | ||
993 | |||
994 | num_probes_tlv = (struct mwifiex_ie_types_num_probes *) tlv_pos; | ||
995 | num_probes_tlv->header.type = cpu_to_le16(TLV_TYPE_NUMPROBES); | ||
996 | num_probes_tlv->header.len = | ||
997 | cpu_to_le16(sizeof(num_probes_tlv->num_probes)); | ||
998 | num_probes_tlv->num_probes = cpu_to_le16((u16) num_probes); | ||
999 | |||
1000 | tlv_pos += sizeof(num_probes_tlv->header) + | ||
1001 | le16_to_cpu(num_probes_tlv->header.len); | ||
1002 | |||
1003 | } | ||
1004 | |||
1005 | /* Append rates tlv */ | ||
1006 | memset(rates, 0, sizeof(rates)); | ||
1007 | |||
1008 | rates_size = mwifiex_get_supported_rates(priv, rates); | ||
1009 | |||
1010 | rates_tlv = (struct mwifiex_ie_types_rates_param_set *) tlv_pos; | ||
1011 | rates_tlv->header.type = cpu_to_le16(WLAN_EID_SUPP_RATES); | ||
1012 | rates_tlv->header.len = cpu_to_le16((u16) rates_size); | ||
1013 | memcpy(rates_tlv->rates, rates, rates_size); | ||
1014 | tlv_pos += sizeof(rates_tlv->header) + rates_size; | ||
1015 | |||
1016 | dev_dbg(adapter->dev, "info: SCAN_CMD: Rates size = %d\n", rates_size); | ||
1017 | |||
1018 | if (ISSUPP_11NENABLED(priv->adapter->fw_cap_info) | ||
1019 | && (priv->adapter->config_bands & BAND_GN | ||
1020 | || priv->adapter->config_bands & BAND_AN)) { | ||
1021 | ht_cap = (struct mwifiex_ie_types_htcap *) tlv_pos; | ||
1022 | memset(ht_cap, 0, sizeof(struct mwifiex_ie_types_htcap)); | ||
1023 | ht_cap->header.type = cpu_to_le16(WLAN_EID_HT_CAPABILITY); | ||
1024 | ht_cap->header.len = | ||
1025 | cpu_to_le16(sizeof(struct ieee80211_ht_cap)); | ||
1026 | mwifiex_fill_cap_info(priv, ht_cap); | ||
1027 | tlv_pos += sizeof(struct mwifiex_ie_types_htcap); | ||
1028 | } | ||
1029 | |||
1030 | /* Append vendor specific IE TLV */ | ||
1031 | mwifiex_cmd_append_vsie_tlv(priv, MWIFIEX_VSIE_MASK_SCAN, &tlv_pos); | ||
1032 | |||
1033 | /* | ||
1034 | * Set the output for the channel TLV to the address in the tlv buffer | ||
1035 | * past any TLVs that were added in this function (SSID, num_probes). | ||
1036 | * Channel TLVs will be added past this for each scan command, | ||
1037 | * preserving the TLVs that were previously added. | ||
1038 | */ | ||
1039 | *chan_list_out = | ||
1040 | (struct mwifiex_ie_types_chan_list_param_set *) tlv_pos; | ||
1041 | |||
1042 | if (user_scan_in && user_scan_in->chan_list[0].chan_number) { | ||
1043 | |||
1044 | dev_dbg(adapter->dev, "info: Scan: Using supplied channel list\n"); | ||
1045 | |||
1046 | for (chan_idx = 0; | ||
1047 | chan_idx < MWIFIEX_USER_SCAN_CHAN_MAX | ||
1048 | && user_scan_in->chan_list[chan_idx].chan_number; | ||
1049 | chan_idx++) { | ||
1050 | |||
1051 | channel = user_scan_in->chan_list[chan_idx].chan_number; | ||
1052 | (scan_chan_list + chan_idx)->chan_number = channel; | ||
1053 | |||
1054 | radio_type = | ||
1055 | user_scan_in->chan_list[chan_idx].radio_type; | ||
1056 | (scan_chan_list + chan_idx)->radio_type = radio_type; | ||
1057 | |||
1058 | scan_type = user_scan_in->chan_list[chan_idx].scan_type; | ||
1059 | |||
1060 | if (scan_type == MWIFIEX_SCAN_TYPE_PASSIVE) | ||
1061 | (scan_chan_list + | ||
1062 | chan_idx)->chan_scan_mode_bitmap | ||
1063 | |= MWIFIEX_PASSIVE_SCAN; | ||
1064 | else | ||
1065 | (scan_chan_list + | ||
1066 | chan_idx)->chan_scan_mode_bitmap | ||
1067 | &= ~MWIFIEX_PASSIVE_SCAN; | ||
1068 | |||
1069 | if (user_scan_in->chan_list[chan_idx].scan_time) { | ||
1070 | scan_dur = (u16) user_scan_in-> | ||
1071 | chan_list[chan_idx].scan_time; | ||
1072 | } else { | ||
1073 | if (scan_type == MWIFIEX_SCAN_TYPE_PASSIVE) | ||
1074 | scan_dur = adapter->passive_scan_time; | ||
1075 | else if (*filtered_scan) | ||
1076 | scan_dur = adapter->specific_scan_time; | ||
1077 | else | ||
1078 | scan_dur = adapter->active_scan_time; | ||
1079 | } | ||
1080 | |||
1081 | (scan_chan_list + chan_idx)->min_scan_time = | ||
1082 | cpu_to_le16(scan_dur); | ||
1083 | (scan_chan_list + chan_idx)->max_scan_time = | ||
1084 | cpu_to_le16(scan_dur); | ||
1085 | } | ||
1086 | |||
1087 | /* Check if we are only scanning the current channel */ | ||
1088 | if ((chan_idx == 1) | ||
1089 | && (user_scan_in->chan_list[0].chan_number | ||
1090 | == priv->curr_bss_params.bss_descriptor.channel)) { | ||
1091 | *scan_current_only = true; | ||
1092 | dev_dbg(adapter->dev, | ||
1093 | "info: Scan: Scanning current channel only\n"); | ||
1094 | } | ||
1095 | |||
1096 | } else { | ||
1097 | dev_dbg(adapter->dev, | ||
1098 | "info: Scan: Creating full region channel list\n"); | ||
1099 | mwifiex_scan_create_channel_list(priv, user_scan_in, | ||
1100 | scan_chan_list, | ||
1101 | *filtered_scan); | ||
1102 | } | ||
1103 | } | ||
1104 | |||
1105 | /* | ||
1106 | * This function inspects the scan response buffer for pointers to | ||
1107 | * expected TLVs. | ||
1108 | * | ||
1109 | * TLVs can be included at the end of the scan response BSS information. | ||
1110 | * | ||
1111 | * Data in the buffer is parsed pointers to TLVs that can potentially | ||
1112 | * be passed back in the response. | ||
1113 | */ | ||
1114 | static void | ||
1115 | mwifiex_ret_802_11_scan_get_tlv_ptrs(struct mwifiex_adapter *adapter, | ||
1116 | struct mwifiex_ie_types_data *tlv, | ||
1117 | u32 tlv_buf_size, u32 req_tlv_type, | ||
1118 | struct mwifiex_ie_types_data **tlv_data) | ||
1119 | { | ||
1120 | struct mwifiex_ie_types_data *current_tlv; | ||
1121 | u32 tlv_buf_left; | ||
1122 | u32 tlv_type; | ||
1123 | u32 tlv_len; | ||
1124 | |||
1125 | current_tlv = tlv; | ||
1126 | tlv_buf_left = tlv_buf_size; | ||
1127 | *tlv_data = NULL; | ||
1128 | |||
1129 | dev_dbg(adapter->dev, "info: SCAN_RESP: tlv_buf_size = %d\n", | ||
1130 | tlv_buf_size); | ||
1131 | |||
1132 | while (tlv_buf_left >= sizeof(struct mwifiex_ie_types_header)) { | ||
1133 | |||
1134 | tlv_type = le16_to_cpu(current_tlv->header.type); | ||
1135 | tlv_len = le16_to_cpu(current_tlv->header.len); | ||
1136 | |||
1137 | if (sizeof(tlv->header) + tlv_len > tlv_buf_left) { | ||
1138 | dev_err(adapter->dev, "SCAN_RESP: TLV buffer corrupt\n"); | ||
1139 | break; | ||
1140 | } | ||
1141 | |||
1142 | if (req_tlv_type == tlv_type) { | ||
1143 | switch (tlv_type) { | ||
1144 | case TLV_TYPE_TSFTIMESTAMP: | ||
1145 | dev_dbg(adapter->dev, "info: SCAN_RESP: TSF " | ||
1146 | "timestamp TLV, len = %d\n", tlv_len); | ||
1147 | *tlv_data = (struct mwifiex_ie_types_data *) | ||
1148 | current_tlv; | ||
1149 | break; | ||
1150 | case TLV_TYPE_CHANNELBANDLIST: | ||
1151 | dev_dbg(adapter->dev, "info: SCAN_RESP: channel" | ||
1152 | " band list TLV, len = %d\n", tlv_len); | ||
1153 | *tlv_data = (struct mwifiex_ie_types_data *) | ||
1154 | current_tlv; | ||
1155 | break; | ||
1156 | default: | ||
1157 | dev_err(adapter->dev, | ||
1158 | "SCAN_RESP: unhandled TLV = %d\n", | ||
1159 | tlv_type); | ||
1160 | /* Give up, this seems corrupted */ | ||
1161 | return; | ||
1162 | } | ||
1163 | } | ||
1164 | |||
1165 | if (*tlv_data) | ||
1166 | break; | ||
1167 | |||
1168 | |||
1169 | tlv_buf_left -= (sizeof(tlv->header) + tlv_len); | ||
1170 | current_tlv = | ||
1171 | (struct mwifiex_ie_types_data *) (current_tlv->data + | ||
1172 | tlv_len); | ||
1173 | |||
1174 | } /* while */ | ||
1175 | } | ||
1176 | |||
1177 | /* | ||
1178 | * This function interprets a BSS scan response returned from the firmware. | ||
1179 | * | ||
1180 | * The various fixed fields and IEs are parsed and passed back for a BSS | ||
1181 | * probe response or beacon from scan command. Information is recorded as | ||
1182 | * needed in the scan table for that entry. | ||
1183 | * | ||
1184 | * The following IE types are recognized and parsed - | ||
1185 | * - SSID | ||
1186 | * - Supported rates | ||
1187 | * - FH parameters set | ||
1188 | * - DS parameters set | ||
1189 | * - CF parameters set | ||
1190 | * - IBSS parameters set | ||
1191 | * - ERP information | ||
1192 | * - Extended supported rates | ||
1193 | * - Vendor specific (221) | ||
1194 | * - RSN IE | ||
1195 | * - WAPI IE | ||
1196 | * - HT capability | ||
1197 | * - HT operation | ||
1198 | * - BSS Coexistence 20/40 | ||
1199 | * - Extended capability | ||
1200 | * - Overlapping BSS scan parameters | ||
1201 | */ | ||
1202 | static int | ||
1203 | mwifiex_interpret_bss_desc_with_ie(struct mwifiex_adapter *adapter, | ||
1204 | struct mwifiex_bssdescriptor *bss_entry, | ||
1205 | u8 **beacon_info, u32 *bytes_left) | ||
1206 | { | ||
1207 | int ret = 0; | ||
1208 | u8 element_id; | ||
1209 | struct ieee_types_fh_param_set *fh_param_set; | ||
1210 | struct ieee_types_ds_param_set *ds_param_set; | ||
1211 | struct ieee_types_cf_param_set *cf_param_set; | ||
1212 | struct ieee_types_ibss_param_set *ibss_param_set; | ||
1213 | struct mwifiex_802_11_fixed_ies fixed_ie; | ||
1214 | u8 *current_ptr; | ||
1215 | u8 *rate; | ||
1216 | u8 element_len; | ||
1217 | u16 total_ie_len; | ||
1218 | u8 bytes_to_copy; | ||
1219 | u8 rate_size; | ||
1220 | u16 beacon_size; | ||
1221 | u8 found_data_rate_ie; | ||
1222 | u32 bytes_left_for_current_beacon; | ||
1223 | struct ieee_types_vendor_specific *vendor_ie; | ||
1224 | const u8 wpa_oui[4] = { 0x00, 0x50, 0xf2, 0x01 }; | ||
1225 | const u8 wmm_oui[4] = { 0x00, 0x50, 0xf2, 0x02 }; | ||
1226 | |||
1227 | found_data_rate_ie = false; | ||
1228 | rate_size = 0; | ||
1229 | beacon_size = 0; | ||
1230 | |||
1231 | if (*bytes_left >= sizeof(beacon_size)) { | ||
1232 | /* Extract & convert beacon size from the command buffer */ | ||
1233 | memcpy(&beacon_size, *beacon_info, sizeof(beacon_size)); | ||
1234 | *bytes_left -= sizeof(beacon_size); | ||
1235 | *beacon_info += sizeof(beacon_size); | ||
1236 | } | ||
1237 | |||
1238 | if (!beacon_size || beacon_size > *bytes_left) { | ||
1239 | *beacon_info += *bytes_left; | ||
1240 | *bytes_left = 0; | ||
1241 | return -1; | ||
1242 | } | ||
1243 | |||
1244 | /* Initialize the current working beacon pointer for this BSS | ||
1245 | iteration */ | ||
1246 | current_ptr = *beacon_info; | ||
1247 | |||
1248 | /* Advance the return beacon pointer past the current beacon */ | ||
1249 | *beacon_info += beacon_size; | ||
1250 | *bytes_left -= beacon_size; | ||
1251 | |||
1252 | bytes_left_for_current_beacon = beacon_size; | ||
1253 | |||
1254 | memcpy(bss_entry->mac_address, current_ptr, ETH_ALEN); | ||
1255 | dev_dbg(adapter->dev, "info: InterpretIE: AP MAC Addr: %pM\n", | ||
1256 | bss_entry->mac_address); | ||
1257 | |||
1258 | current_ptr += ETH_ALEN; | ||
1259 | bytes_left_for_current_beacon -= ETH_ALEN; | ||
1260 | |||
1261 | if (bytes_left_for_current_beacon < 12) { | ||
1262 | dev_err(adapter->dev, "InterpretIE: not enough bytes left\n"); | ||
1263 | return -1; | ||
1264 | } | ||
1265 | |||
1266 | /* | ||
1267 | * Next 4 fields are RSSI, time stamp, beacon interval, | ||
1268 | * and capability information | ||
1269 | */ | ||
1270 | |||
1271 | /* RSSI is 1 byte long */ | ||
1272 | bss_entry->rssi = (s32) (*current_ptr); | ||
1273 | dev_dbg(adapter->dev, "info: InterpretIE: RSSI=%02X\n", *current_ptr); | ||
1274 | current_ptr += 1; | ||
1275 | bytes_left_for_current_beacon -= 1; | ||
1276 | |||
1277 | /* | ||
1278 | * The RSSI is not part of the beacon/probe response. After we have | ||
1279 | * advanced current_ptr past the RSSI field, save the remaining | ||
1280 | * data for use at the application layer | ||
1281 | */ | ||
1282 | bss_entry->beacon_buf = current_ptr; | ||
1283 | bss_entry->beacon_buf_size = bytes_left_for_current_beacon; | ||
1284 | |||
1285 | /* Time stamp is 8 bytes long */ | ||
1286 | memcpy(fixed_ie.time_stamp, current_ptr, 8); | ||
1287 | memcpy(bss_entry->time_stamp, current_ptr, 8); | ||
1288 | current_ptr += 8; | ||
1289 | bytes_left_for_current_beacon -= 8; | ||
1290 | |||
1291 | /* Beacon interval is 2 bytes long */ | ||
1292 | memcpy(&fixed_ie.beacon_interval, current_ptr, 2); | ||
1293 | bss_entry->beacon_period = le16_to_cpu(fixed_ie.beacon_interval); | ||
1294 | current_ptr += 2; | ||
1295 | bytes_left_for_current_beacon -= 2; | ||
1296 | |||
1297 | /* Capability information is 2 bytes long */ | ||
1298 | memcpy(&fixed_ie.capabilities, current_ptr, 2); | ||
1299 | dev_dbg(adapter->dev, "info: InterpretIE: fixed_ie.capabilities=0x%X\n", | ||
1300 | fixed_ie.capabilities); | ||
1301 | bss_entry->cap_info_bitmap = le16_to_cpu(fixed_ie.capabilities); | ||
1302 | current_ptr += 2; | ||
1303 | bytes_left_for_current_beacon -= 2; | ||
1304 | |||
1305 | /* Rest of the current buffer are IE's */ | ||
1306 | dev_dbg(adapter->dev, "info: InterpretIE: IELength for this AP = %d\n", | ||
1307 | bytes_left_for_current_beacon); | ||
1308 | |||
1309 | if (bss_entry->cap_info_bitmap & WLAN_CAPABILITY_PRIVACY) { | ||
1310 | dev_dbg(adapter->dev, "info: InterpretIE: AP WEP enabled\n"); | ||
1311 | bss_entry->privacy = MWIFIEX_802_11_PRIV_FILTER_8021X_WEP; | ||
1312 | } else { | ||
1313 | bss_entry->privacy = MWIFIEX_802_11_PRIV_FILTER_ACCEPT_ALL; | ||
1314 | } | ||
1315 | |||
1316 | if (bss_entry->cap_info_bitmap & WLAN_CAPABILITY_IBSS) | ||
1317 | bss_entry->bss_mode = MWIFIEX_BSS_MODE_IBSS; | ||
1318 | else | ||
1319 | bss_entry->bss_mode = MWIFIEX_BSS_MODE_INFRA; | ||
1320 | |||
1321 | |||
1322 | /* Process variable IE */ | ||
1323 | while (bytes_left_for_current_beacon >= 2) { | ||
1324 | element_id = *current_ptr; | ||
1325 | element_len = *(current_ptr + 1); | ||
1326 | total_ie_len = element_len + sizeof(struct ieee_types_header); | ||
1327 | |||
1328 | if (bytes_left_for_current_beacon < total_ie_len) { | ||
1329 | dev_err(adapter->dev, "err: InterpretIE: in processing" | ||
1330 | " IE, bytes left < IE length\n"); | ||
1331 | bytes_left_for_current_beacon = 0; | ||
1332 | ret = -1; | ||
1333 | continue; | ||
1334 | } | ||
1335 | switch (element_id) { | ||
1336 | case WLAN_EID_SSID: | ||
1337 | bss_entry->ssid.ssid_len = element_len; | ||
1338 | memcpy(bss_entry->ssid.ssid, (current_ptr + 2), | ||
1339 | element_len); | ||
1340 | dev_dbg(adapter->dev, "info: InterpretIE: ssid: %-32s\n", | ||
1341 | bss_entry->ssid.ssid); | ||
1342 | break; | ||
1343 | |||
1344 | case WLAN_EID_SUPP_RATES: | ||
1345 | memcpy(bss_entry->data_rates, current_ptr + 2, | ||
1346 | element_len); | ||
1347 | memcpy(bss_entry->supported_rates, current_ptr + 2, | ||
1348 | element_len); | ||
1349 | rate_size = element_len; | ||
1350 | found_data_rate_ie = true; | ||
1351 | break; | ||
1352 | |||
1353 | case WLAN_EID_FH_PARAMS: | ||
1354 | fh_param_set = | ||
1355 | (struct ieee_types_fh_param_set *) current_ptr; | ||
1356 | memcpy(&bss_entry->phy_param_set.fh_param_set, | ||
1357 | fh_param_set, | ||
1358 | sizeof(struct ieee_types_fh_param_set)); | ||
1359 | break; | ||
1360 | |||
1361 | case WLAN_EID_DS_PARAMS: | ||
1362 | ds_param_set = | ||
1363 | (struct ieee_types_ds_param_set *) current_ptr; | ||
1364 | |||
1365 | bss_entry->channel = ds_param_set->current_chan; | ||
1366 | |||
1367 | memcpy(&bss_entry->phy_param_set.ds_param_set, | ||
1368 | ds_param_set, | ||
1369 | sizeof(struct ieee_types_ds_param_set)); | ||
1370 | break; | ||
1371 | |||
1372 | case WLAN_EID_CF_PARAMS: | ||
1373 | cf_param_set = | ||
1374 | (struct ieee_types_cf_param_set *) current_ptr; | ||
1375 | memcpy(&bss_entry->ss_param_set.cf_param_set, | ||
1376 | cf_param_set, | ||
1377 | sizeof(struct ieee_types_cf_param_set)); | ||
1378 | break; | ||
1379 | |||
1380 | case WLAN_EID_IBSS_PARAMS: | ||
1381 | ibss_param_set = | ||
1382 | (struct ieee_types_ibss_param_set *) | ||
1383 | current_ptr; | ||
1384 | memcpy(&bss_entry->ss_param_set.ibss_param_set, | ||
1385 | ibss_param_set, | ||
1386 | sizeof(struct ieee_types_ibss_param_set)); | ||
1387 | break; | ||
1388 | |||
1389 | case WLAN_EID_ERP_INFO: | ||
1390 | bss_entry->erp_flags = *(current_ptr + 2); | ||
1391 | break; | ||
1392 | |||
1393 | case WLAN_EID_EXT_SUPP_RATES: | ||
1394 | /* | ||
1395 | * Only process extended supported rate | ||
1396 | * if data rate is already found. | ||
1397 | * Data rate IE should come before | ||
1398 | * extended supported rate IE | ||
1399 | */ | ||
1400 | if (found_data_rate_ie) { | ||
1401 | if ((element_len + rate_size) > | ||
1402 | MWIFIEX_SUPPORTED_RATES) | ||
1403 | bytes_to_copy = | ||
1404 | (MWIFIEX_SUPPORTED_RATES - | ||
1405 | rate_size); | ||
1406 | else | ||
1407 | bytes_to_copy = element_len; | ||
1408 | |||
1409 | rate = (u8 *) bss_entry->data_rates; | ||
1410 | rate += rate_size; | ||
1411 | memcpy(rate, current_ptr + 2, bytes_to_copy); | ||
1412 | |||
1413 | rate = (u8 *) bss_entry->supported_rates; | ||
1414 | rate += rate_size; | ||
1415 | memcpy(rate, current_ptr + 2, bytes_to_copy); | ||
1416 | } | ||
1417 | break; | ||
1418 | |||
1419 | case WLAN_EID_VENDOR_SPECIFIC: | ||
1420 | vendor_ie = (struct ieee_types_vendor_specific *) | ||
1421 | current_ptr; | ||
1422 | |||
1423 | if (!memcmp | ||
1424 | (vendor_ie->vend_hdr.oui, wpa_oui, | ||
1425 | sizeof(wpa_oui))) { | ||
1426 | bss_entry->bcn_wpa_ie = | ||
1427 | (struct ieee_types_vendor_specific *) | ||
1428 | current_ptr; | ||
1429 | bss_entry->wpa_offset = (u16) (current_ptr - | ||
1430 | bss_entry->beacon_buf); | ||
1431 | } else if (!memcmp(vendor_ie->vend_hdr.oui, wmm_oui, | ||
1432 | sizeof(wmm_oui))) { | ||
1433 | if (total_ie_len == | ||
1434 | sizeof(struct ieee_types_wmm_parameter) | ||
1435 | || total_ie_len == | ||
1436 | sizeof(struct ieee_types_wmm_info)) | ||
1437 | /* | ||
1438 | * Only accept and copy the WMM IE if | ||
1439 | * it matches the size expected for the | ||
1440 | * WMM Info IE or the WMM Parameter IE. | ||
1441 | */ | ||
1442 | memcpy((u8 *) &bss_entry->wmm_ie, | ||
1443 | current_ptr, total_ie_len); | ||
1444 | } | ||
1445 | break; | ||
1446 | case WLAN_EID_RSN: | ||
1447 | bss_entry->bcn_rsn_ie = | ||
1448 | (struct ieee_types_generic *) current_ptr; | ||
1449 | bss_entry->rsn_offset = (u16) (current_ptr - | ||
1450 | bss_entry->beacon_buf); | ||
1451 | break; | ||
1452 | case WLAN_EID_BSS_AC_ACCESS_DELAY: | ||
1453 | bss_entry->bcn_wapi_ie = | ||
1454 | (struct ieee_types_generic *) current_ptr; | ||
1455 | bss_entry->wapi_offset = (u16) (current_ptr - | ||
1456 | bss_entry->beacon_buf); | ||
1457 | break; | ||
1458 | case WLAN_EID_HT_CAPABILITY: | ||
1459 | bss_entry->bcn_ht_cap = (struct ieee80211_ht_cap *) | ||
1460 | (current_ptr + | ||
1461 | sizeof(struct ieee_types_header)); | ||
1462 | bss_entry->ht_cap_offset = (u16) (current_ptr + | ||
1463 | sizeof(struct ieee_types_header) - | ||
1464 | bss_entry->beacon_buf); | ||
1465 | break; | ||
1466 | case WLAN_EID_HT_INFORMATION: | ||
1467 | bss_entry->bcn_ht_info = (struct ieee80211_ht_info *) | ||
1468 | (current_ptr + | ||
1469 | sizeof(struct ieee_types_header)); | ||
1470 | bss_entry->ht_info_offset = (u16) (current_ptr + | ||
1471 | sizeof(struct ieee_types_header) - | ||
1472 | bss_entry->beacon_buf); | ||
1473 | break; | ||
1474 | case WLAN_EID_BSS_COEX_2040: | ||
1475 | bss_entry->bcn_bss_co_2040 = (u8 *) (current_ptr + | ||
1476 | sizeof(struct ieee_types_header)); | ||
1477 | bss_entry->bss_co_2040_offset = (u16) (current_ptr + | ||
1478 | sizeof(struct ieee_types_header) - | ||
1479 | bss_entry->beacon_buf); | ||
1480 | break; | ||
1481 | case WLAN_EID_EXT_CAPABILITY: | ||
1482 | bss_entry->bcn_ext_cap = (u8 *) (current_ptr + | ||
1483 | sizeof(struct ieee_types_header)); | ||
1484 | bss_entry->ext_cap_offset = (u16) (current_ptr + | ||
1485 | sizeof(struct ieee_types_header) - | ||
1486 | bss_entry->beacon_buf); | ||
1487 | break; | ||
1488 | case WLAN_EID_OVERLAP_BSS_SCAN_PARAM: | ||
1489 | bss_entry->bcn_obss_scan = | ||
1490 | (struct ieee_types_obss_scan_param *) | ||
1491 | current_ptr; | ||
1492 | bss_entry->overlap_bss_offset = (u16) (current_ptr - | ||
1493 | bss_entry->beacon_buf); | ||
1494 | break; | ||
1495 | default: | ||
1496 | break; | ||
1497 | } | ||
1498 | |||
1499 | current_ptr += element_len + 2; | ||
1500 | |||
1501 | /* Need to account for IE ID and IE Len */ | ||
1502 | bytes_left_for_current_beacon -= (element_len + 2); | ||
1503 | |||
1504 | } /* while (bytes_left_for_current_beacon > 2) */ | ||
1505 | return ret; | ||
1506 | } | ||
1507 | |||
1508 | /* | ||
1509 | * This function adjusts the pointers used in beacon buffers to reflect | ||
1510 | * shifts. | ||
1511 | * | ||
1512 | * The memory allocated for beacon buffers is of fixed sizes where all the | ||
1513 | * saved beacons must be stored. New beacons are added in the free portion | ||
1514 | * of this memory, space permitting; while duplicate beacon buffers are | ||
1515 | * placed at the same start location. However, since duplicate beacon | ||
1516 | * buffers may not match the size of the old one, all the following buffers | ||
1517 | * in the memory must be shifted to either make space, or to fill up freed | ||
1518 | * up space. | ||
1519 | * | ||
1520 | * This function is used to update the beacon buffer pointers that are past | ||
1521 | * an existing beacon buffer that is updated with a new one of different | ||
1522 | * size. The pointers are shifted by a fixed amount, either forward or | ||
1523 | * backward. | ||
1524 | * | ||
1525 | * the following pointers in every affected beacon buffers are changed, if | ||
1526 | * present - | ||
1527 | * - WPA IE pointer | ||
1528 | * - RSN IE pointer | ||
1529 | * - WAPI IE pointer | ||
1530 | * - HT capability IE pointer | ||
1531 | * - HT information IE pointer | ||
1532 | * - BSS coexistence 20/40 IE pointer | ||
1533 | * - Extended capability IE pointer | ||
1534 | * - Overlapping BSS scan parameter IE pointer | ||
1535 | */ | ||
1536 | static void | ||
1537 | mwifiex_adjust_beacon_buffer_ptrs(struct mwifiex_private *priv, u8 advance, | ||
1538 | u8 *bcn_store, u32 rem_bcn_size, | ||
1539 | u32 num_of_ent) | ||
1540 | { | ||
1541 | struct mwifiex_adapter *adapter = priv->adapter; | ||
1542 | u32 adj_idx; | ||
1543 | for (adj_idx = 0; adj_idx < num_of_ent; adj_idx++) { | ||
1544 | if (adapter->scan_table[adj_idx].beacon_buf > bcn_store) { | ||
1545 | |||
1546 | if (advance) | ||
1547 | adapter->scan_table[adj_idx].beacon_buf += | ||
1548 | rem_bcn_size; | ||
1549 | else | ||
1550 | adapter->scan_table[adj_idx].beacon_buf -= | ||
1551 | rem_bcn_size; | ||
1552 | |||
1553 | if (adapter->scan_table[adj_idx].bcn_wpa_ie) | ||
1554 | adapter->scan_table[adj_idx].bcn_wpa_ie = | ||
1555 | (struct ieee_types_vendor_specific *) | ||
1556 | (adapter->scan_table[adj_idx].beacon_buf + | ||
1557 | adapter->scan_table[adj_idx].wpa_offset); | ||
1558 | if (adapter->scan_table[adj_idx].bcn_rsn_ie) | ||
1559 | adapter->scan_table[adj_idx].bcn_rsn_ie = | ||
1560 | (struct ieee_types_generic *) | ||
1561 | (adapter->scan_table[adj_idx].beacon_buf + | ||
1562 | adapter->scan_table[adj_idx].rsn_offset); | ||
1563 | if (adapter->scan_table[adj_idx].bcn_wapi_ie) | ||
1564 | adapter->scan_table[adj_idx].bcn_wapi_ie = | ||
1565 | (struct ieee_types_generic *) | ||
1566 | (adapter->scan_table[adj_idx].beacon_buf + | ||
1567 | adapter->scan_table[adj_idx].wapi_offset); | ||
1568 | if (adapter->scan_table[adj_idx].bcn_ht_cap) | ||
1569 | adapter->scan_table[adj_idx].bcn_ht_cap = | ||
1570 | (struct ieee80211_ht_cap *) | ||
1571 | (adapter->scan_table[adj_idx].beacon_buf + | ||
1572 | adapter->scan_table[adj_idx].ht_cap_offset); | ||
1573 | |||
1574 | if (adapter->scan_table[adj_idx].bcn_ht_info) | ||
1575 | adapter->scan_table[adj_idx].bcn_ht_info = | ||
1576 | (struct ieee80211_ht_info *) | ||
1577 | (adapter->scan_table[adj_idx].beacon_buf + | ||
1578 | adapter->scan_table[adj_idx].ht_info_offset); | ||
1579 | if (adapter->scan_table[adj_idx].bcn_bss_co_2040) | ||
1580 | adapter->scan_table[adj_idx].bcn_bss_co_2040 = | ||
1581 | (u8 *) | ||
1582 | (adapter->scan_table[adj_idx].beacon_buf + | ||
1583 | adapter->scan_table[adj_idx].bss_co_2040_offset); | ||
1584 | if (adapter->scan_table[adj_idx].bcn_ext_cap) | ||
1585 | adapter->scan_table[adj_idx].bcn_ext_cap = | ||
1586 | (u8 *) | ||
1587 | (adapter->scan_table[adj_idx].beacon_buf + | ||
1588 | adapter->scan_table[adj_idx].ext_cap_offset); | ||
1589 | if (adapter->scan_table[adj_idx].bcn_obss_scan) | ||
1590 | adapter->scan_table[adj_idx].bcn_obss_scan = | ||
1591 | (struct ieee_types_obss_scan_param *) | ||
1592 | (adapter->scan_table[adj_idx].beacon_buf + | ||
1593 | adapter->scan_table[adj_idx].overlap_bss_offset); | ||
1594 | } | ||
1595 | } | ||
1596 | } | ||
1597 | |||
1598 | /* | ||
1599 | * This function updates the pointers used in beacon buffer for given bss | ||
1600 | * descriptor to reflect shifts | ||
1601 | * | ||
1602 | * Following pointers are updated | ||
1603 | * - WPA IE pointer | ||
1604 | * - RSN IE pointer | ||
1605 | * - WAPI IE pointer | ||
1606 | * - HT capability IE pointer | ||
1607 | * - HT information IE pointer | ||
1608 | * - BSS coexistence 20/40 IE pointer | ||
1609 | * - Extended capability IE pointer | ||
1610 | * - Overlapping BSS scan parameter IE pointer | ||
1611 | */ | ||
1612 | static void | ||
1613 | mwifiex_update_beacon_buffer_ptrs(struct mwifiex_bssdescriptor *beacon) | ||
1614 | { | ||
1615 | if (beacon->bcn_wpa_ie) | ||
1616 | beacon->bcn_wpa_ie = (struct ieee_types_vendor_specific *) | ||
1617 | (beacon->beacon_buf + beacon->wpa_offset); | ||
1618 | if (beacon->bcn_rsn_ie) | ||
1619 | beacon->bcn_rsn_ie = (struct ieee_types_generic *) | ||
1620 | (beacon->beacon_buf + beacon->rsn_offset); | ||
1621 | if (beacon->bcn_wapi_ie) | ||
1622 | beacon->bcn_wapi_ie = (struct ieee_types_generic *) | ||
1623 | (beacon->beacon_buf + beacon->wapi_offset); | ||
1624 | if (beacon->bcn_ht_cap) | ||
1625 | beacon->bcn_ht_cap = (struct ieee80211_ht_cap *) | ||
1626 | (beacon->beacon_buf + beacon->ht_cap_offset); | ||
1627 | if (beacon->bcn_ht_info) | ||
1628 | beacon->bcn_ht_info = (struct ieee80211_ht_info *) | ||
1629 | (beacon->beacon_buf + beacon->ht_info_offset); | ||
1630 | if (beacon->bcn_bss_co_2040) | ||
1631 | beacon->bcn_bss_co_2040 = (u8 *) (beacon->beacon_buf + | ||
1632 | beacon->bss_co_2040_offset); | ||
1633 | if (beacon->bcn_ext_cap) | ||
1634 | beacon->bcn_ext_cap = (u8 *) (beacon->beacon_buf + | ||
1635 | beacon->ext_cap_offset); | ||
1636 | if (beacon->bcn_obss_scan) | ||
1637 | beacon->bcn_obss_scan = (struct ieee_types_obss_scan_param *) | ||
1638 | (beacon->beacon_buf + beacon->overlap_bss_offset); | ||
1639 | } | ||
1640 | |||
1641 | /* | ||
1642 | * This function stores a beacon or probe response for a BSS returned | ||
1643 | * in the scan. | ||
1644 | * | ||
1645 | * This stores a new scan response or an update for a previous scan response. | ||
1646 | * New entries need to verify that they do not exceed the total amount of | ||
1647 | * memory allocated for the table. | ||
1648 | * | ||
1649 | * Replacement entries need to take into consideration the amount of space | ||
1650 | * currently allocated for the beacon/probe response and adjust the entry | ||
1651 | * as needed. | ||
1652 | * | ||
1653 | * A small amount of extra pad (SCAN_BEACON_ENTRY_PAD) is generally reserved | ||
1654 | * for an entry in case it is a beacon since a probe response for the | ||
1655 | * network will by larger per the standard. This helps to reduce the | ||
1656 | * amount of memory copying to fit a new probe response into an entry | ||
1657 | * already occupied by a network's previously stored beacon. | ||
1658 | */ | ||
1659 | static void | ||
1660 | mwifiex_ret_802_11_scan_store_beacon(struct mwifiex_private *priv, | ||
1661 | u32 beacon_idx, u32 num_of_ent, | ||
1662 | struct mwifiex_bssdescriptor *new_beacon) | ||
1663 | { | ||
1664 | struct mwifiex_adapter *adapter = priv->adapter; | ||
1665 | u8 *bcn_store; | ||
1666 | u32 new_bcn_size; | ||
1667 | u32 old_bcn_size; | ||
1668 | u32 bcn_space; | ||
1669 | |||
1670 | if (adapter->scan_table[beacon_idx].beacon_buf) { | ||
1671 | |||
1672 | new_bcn_size = new_beacon->beacon_buf_size; | ||
1673 | old_bcn_size = adapter->scan_table[beacon_idx].beacon_buf_size; | ||
1674 | bcn_space = adapter->scan_table[beacon_idx].beacon_buf_size_max; | ||
1675 | bcn_store = adapter->scan_table[beacon_idx].beacon_buf; | ||
1676 | |||
1677 | /* Set the max to be the same as current entry unless changed | ||
1678 | below */ | ||
1679 | new_beacon->beacon_buf_size_max = bcn_space; | ||
1680 | if (new_bcn_size == old_bcn_size) { | ||
1681 | /* | ||
1682 | * Beacon is the same size as the previous entry. | ||
1683 | * Replace the previous contents with the scan result | ||
1684 | */ | ||
1685 | memcpy(bcn_store, new_beacon->beacon_buf, | ||
1686 | new_beacon->beacon_buf_size); | ||
1687 | |||
1688 | } else if (new_bcn_size <= bcn_space) { | ||
1689 | /* | ||
1690 | * New beacon size will fit in the amount of space | ||
1691 | * we have previously allocated for it | ||
1692 | */ | ||
1693 | |||
1694 | /* Copy the new beacon buffer entry over the old one */ | ||
1695 | memcpy(bcn_store, new_beacon->beacon_buf, new_bcn_size); | ||
1696 | |||
1697 | /* | ||
1698 | * If the old beacon size was less than the maximum | ||
1699 | * we had alloted for the entry, and the new entry | ||
1700 | * is even smaller, reset the max size to the old | ||
1701 | * beacon entry and compress the storage space | ||
1702 | * (leaving a new pad space of (old_bcn_size - | ||
1703 | * new_bcn_size). | ||
1704 | */ | ||
1705 | if (old_bcn_size < bcn_space | ||
1706 | && new_bcn_size <= old_bcn_size) { | ||
1707 | /* | ||
1708 | * Old Beacon size is smaller than the alloted | ||
1709 | * storage size. Shrink the alloted storage | ||
1710 | * space. | ||
1711 | */ | ||
1712 | dev_dbg(adapter->dev, "info: AppControl:" | ||
1713 | " smaller duplicate beacon " | ||
1714 | "(%d), old = %d, new = %d, space = %d," | ||
1715 | "left = %d\n", | ||
1716 | beacon_idx, old_bcn_size, new_bcn_size, | ||
1717 | bcn_space, | ||
1718 | (int)(sizeof(adapter->bcn_buf) - | ||
1719 | (adapter->bcn_buf_end - | ||
1720 | adapter->bcn_buf))); | ||
1721 | |||
1722 | /* | ||
1723 | * memmove (since the memory overlaps) the | ||
1724 | * data after the beacon we just stored to the | ||
1725 | * end of the current beacon. This cleans up | ||
1726 | * any unused space the old larger beacon was | ||
1727 | * using in the buffer | ||
1728 | */ | ||
1729 | memmove(bcn_store + old_bcn_size, | ||
1730 | bcn_store + bcn_space, | ||
1731 | adapter->bcn_buf_end - (bcn_store + | ||
1732 | bcn_space)); | ||
1733 | |||
1734 | /* | ||
1735 | * Decrement the end pointer by the difference | ||
1736 | * between the old larger size and the new | ||
1737 | * smaller size since we are using less space | ||
1738 | * due to the new beacon being smaller | ||
1739 | */ | ||
1740 | adapter->bcn_buf_end -= | ||
1741 | (bcn_space - old_bcn_size); | ||
1742 | |||
1743 | /* Set the maximum storage size to the old | ||
1744 | beacon size */ | ||
1745 | new_beacon->beacon_buf_size_max = old_bcn_size; | ||
1746 | |||
1747 | /* Adjust beacon buffer pointers that are past | ||
1748 | the current */ | ||
1749 | mwifiex_adjust_beacon_buffer_ptrs(priv, 0, | ||
1750 | bcn_store, (bcn_space - old_bcn_size), | ||
1751 | num_of_ent); | ||
1752 | } | ||
1753 | } else if (adapter->bcn_buf_end + (new_bcn_size - bcn_space) | ||
1754 | < (adapter->bcn_buf + sizeof(adapter->bcn_buf))) { | ||
1755 | /* | ||
1756 | * Beacon is larger than space previously allocated | ||
1757 | * (bcn_space) and there is enough space left in the | ||
1758 | * beaconBuffer to store the additional data | ||
1759 | */ | ||
1760 | dev_dbg(adapter->dev, "info: AppControl:" | ||
1761 | " larger duplicate beacon (%d), " | ||
1762 | "old = %d, new = %d, space = %d, left = %d\n", | ||
1763 | beacon_idx, old_bcn_size, new_bcn_size, | ||
1764 | bcn_space, | ||
1765 | (int)(sizeof(adapter->bcn_buf) - | ||
1766 | (adapter->bcn_buf_end - | ||
1767 | adapter->bcn_buf))); | ||
1768 | |||
1769 | /* | ||
1770 | * memmove (since the memory overlaps) the data | ||
1771 | * after the beacon we just stored to the end of | ||
1772 | * the current beacon. This moves the data for | ||
1773 | * the beacons after this further in memory to | ||
1774 | * make space for the new larger beacon we are | ||
1775 | * about to copy in. | ||
1776 | */ | ||
1777 | memmove(bcn_store + new_bcn_size, | ||
1778 | bcn_store + bcn_space, | ||
1779 | adapter->bcn_buf_end - (bcn_store + bcn_space)); | ||
1780 | |||
1781 | /* Copy the new beacon buffer entry over the old one */ | ||
1782 | memcpy(bcn_store, new_beacon->beacon_buf, new_bcn_size); | ||
1783 | |||
1784 | /* Move the beacon end pointer by the amount of new | ||
1785 | beacon data we are adding */ | ||
1786 | adapter->bcn_buf_end += (new_bcn_size - bcn_space); | ||
1787 | |||
1788 | /* | ||
1789 | * This entry is bigger than the alloted max space | ||
1790 | * previously reserved. Increase the max space to | ||
1791 | * be equal to the new beacon size | ||
1792 | */ | ||
1793 | new_beacon->beacon_buf_size_max = new_bcn_size; | ||
1794 | |||
1795 | /* Adjust beacon buffer pointers that are past the | ||
1796 | current */ | ||
1797 | mwifiex_adjust_beacon_buffer_ptrs(priv, 1, bcn_store, | ||
1798 | (new_bcn_size - bcn_space), | ||
1799 | num_of_ent); | ||
1800 | } else { | ||
1801 | /* | ||
1802 | * Beacon is larger than the previously allocated space, | ||
1803 | * but there is not enough free space to store the | ||
1804 | * additional data. | ||
1805 | */ | ||
1806 | dev_err(adapter->dev, "AppControl: larger duplicate " | ||
1807 | " beacon (%d), old = %d new = %d, space = %d," | ||
1808 | " left = %d\n", beacon_idx, old_bcn_size, | ||
1809 | new_bcn_size, bcn_space, | ||
1810 | (int)(sizeof(adapter->bcn_buf) - | ||
1811 | (adapter->bcn_buf_end - adapter->bcn_buf))); | ||
1812 | |||
1813 | /* Storage failure, keep old beacon intact */ | ||
1814 | new_beacon->beacon_buf_size = old_bcn_size; | ||
1815 | if (new_beacon->bcn_wpa_ie) | ||
1816 | new_beacon->wpa_offset = | ||
1817 | adapter->scan_table[beacon_idx]. | ||
1818 | wpa_offset; | ||
1819 | if (new_beacon->bcn_rsn_ie) | ||
1820 | new_beacon->rsn_offset = | ||
1821 | adapter->scan_table[beacon_idx]. | ||
1822 | rsn_offset; | ||
1823 | if (new_beacon->bcn_wapi_ie) | ||
1824 | new_beacon->wapi_offset = | ||
1825 | adapter->scan_table[beacon_idx]. | ||
1826 | wapi_offset; | ||
1827 | if (new_beacon->bcn_ht_cap) | ||
1828 | new_beacon->ht_cap_offset = | ||
1829 | adapter->scan_table[beacon_idx]. | ||
1830 | ht_cap_offset; | ||
1831 | if (new_beacon->bcn_ht_info) | ||
1832 | new_beacon->ht_info_offset = | ||
1833 | adapter->scan_table[beacon_idx]. | ||
1834 | ht_info_offset; | ||
1835 | if (new_beacon->bcn_bss_co_2040) | ||
1836 | new_beacon->bss_co_2040_offset = | ||
1837 | adapter->scan_table[beacon_idx]. | ||
1838 | bss_co_2040_offset; | ||
1839 | if (new_beacon->bcn_ext_cap) | ||
1840 | new_beacon->ext_cap_offset = | ||
1841 | adapter->scan_table[beacon_idx]. | ||
1842 | ext_cap_offset; | ||
1843 | if (new_beacon->bcn_obss_scan) | ||
1844 | new_beacon->overlap_bss_offset = | ||
1845 | adapter->scan_table[beacon_idx]. | ||
1846 | overlap_bss_offset; | ||
1847 | } | ||
1848 | /* Point the new entry to its permanent storage space */ | ||
1849 | new_beacon->beacon_buf = bcn_store; | ||
1850 | mwifiex_update_beacon_buffer_ptrs(new_beacon); | ||
1851 | } else { | ||
1852 | /* | ||
1853 | * No existing beacon data exists for this entry, check to see | ||
1854 | * if we can fit it in the remaining space | ||
1855 | */ | ||
1856 | if (adapter->bcn_buf_end + new_beacon->beacon_buf_size + | ||
1857 | SCAN_BEACON_ENTRY_PAD < (adapter->bcn_buf + | ||
1858 | sizeof(adapter->bcn_buf))) { | ||
1859 | |||
1860 | /* | ||
1861 | * Copy the beacon buffer data from the local entry to | ||
1862 | * the adapter dev struct buffer space used to store | ||
1863 | * the raw beacon data for each entry in the scan table | ||
1864 | */ | ||
1865 | memcpy(adapter->bcn_buf_end, new_beacon->beacon_buf, | ||
1866 | new_beacon->beacon_buf_size); | ||
1867 | |||
1868 | /* Update the beacon ptr to point to the table save | ||
1869 | area */ | ||
1870 | new_beacon->beacon_buf = adapter->bcn_buf_end; | ||
1871 | new_beacon->beacon_buf_size_max = | ||
1872 | (new_beacon->beacon_buf_size + | ||
1873 | SCAN_BEACON_ENTRY_PAD); | ||
1874 | |||
1875 | mwifiex_update_beacon_buffer_ptrs(new_beacon); | ||
1876 | |||
1877 | /* Increment the end pointer by the size reserved */ | ||
1878 | adapter->bcn_buf_end += new_beacon->beacon_buf_size_max; | ||
1879 | |||
1880 | dev_dbg(adapter->dev, "info: AppControl: beacon[%02d]" | ||
1881 | " sz=%03d, used = %04d, left = %04d\n", | ||
1882 | beacon_idx, | ||
1883 | new_beacon->beacon_buf_size, | ||
1884 | (int)(adapter->bcn_buf_end - adapter->bcn_buf), | ||
1885 | (int)(sizeof(adapter->bcn_buf) - | ||
1886 | (adapter->bcn_buf_end - | ||
1887 | adapter->bcn_buf))); | ||
1888 | } else { | ||
1889 | /* No space for new beacon */ | ||
1890 | dev_dbg(adapter->dev, "info: AppControl: no space for" | ||
1891 | " beacon (%d): %pM sz=%03d, left=%03d\n", | ||
1892 | beacon_idx, new_beacon->mac_address, | ||
1893 | new_beacon->beacon_buf_size, | ||
1894 | (int)(sizeof(adapter->bcn_buf) - | ||
1895 | (adapter->bcn_buf_end - | ||
1896 | adapter->bcn_buf))); | ||
1897 | |||
1898 | /* Storage failure; clear storage records for this | ||
1899 | bcn */ | ||
1900 | new_beacon->beacon_buf = NULL; | ||
1901 | new_beacon->beacon_buf_size = 0; | ||
1902 | new_beacon->beacon_buf_size_max = 0; | ||
1903 | new_beacon->bcn_wpa_ie = NULL; | ||
1904 | new_beacon->wpa_offset = 0; | ||
1905 | new_beacon->bcn_rsn_ie = NULL; | ||
1906 | new_beacon->rsn_offset = 0; | ||
1907 | new_beacon->bcn_wapi_ie = NULL; | ||
1908 | new_beacon->wapi_offset = 0; | ||
1909 | new_beacon->bcn_ht_cap = NULL; | ||
1910 | new_beacon->ht_cap_offset = 0; | ||
1911 | new_beacon->bcn_ht_info = NULL; | ||
1912 | new_beacon->ht_info_offset = 0; | ||
1913 | new_beacon->bcn_bss_co_2040 = NULL; | ||
1914 | new_beacon->bss_co_2040_offset = 0; | ||
1915 | new_beacon->bcn_ext_cap = NULL; | ||
1916 | new_beacon->ext_cap_offset = 0; | ||
1917 | new_beacon->bcn_obss_scan = NULL; | ||
1918 | new_beacon->overlap_bss_offset = 0; | ||
1919 | } | ||
1920 | } | ||
1921 | } | ||
1922 | |||
1923 | /* | ||
1924 | * This function restores a beacon buffer of the current BSS descriptor. | ||
1925 | */ | ||
1926 | static void mwifiex_restore_curr_bcn(struct mwifiex_private *priv) | ||
1927 | { | ||
1928 | struct mwifiex_adapter *adapter = priv->adapter; | ||
1929 | struct mwifiex_bssdescriptor *curr_bss = | ||
1930 | &priv->curr_bss_params.bss_descriptor; | ||
1931 | unsigned long flags; | ||
1932 | |||
1933 | if (priv->curr_bcn_buf && | ||
1934 | ((adapter->bcn_buf_end + priv->curr_bcn_size) < | ||
1935 | (adapter->bcn_buf + sizeof(adapter->bcn_buf)))) { | ||
1936 | spin_lock_irqsave(&priv->curr_bcn_buf_lock, flags); | ||
1937 | |||
1938 | /* restore the current beacon buffer */ | ||
1939 | memcpy(adapter->bcn_buf_end, priv->curr_bcn_buf, | ||
1940 | priv->curr_bcn_size); | ||
1941 | curr_bss->beacon_buf = adapter->bcn_buf_end; | ||
1942 | curr_bss->beacon_buf_size = priv->curr_bcn_size; | ||
1943 | adapter->bcn_buf_end += priv->curr_bcn_size; | ||
1944 | |||
1945 | /* adjust the pointers in the current BSS descriptor */ | ||
1946 | if (curr_bss->bcn_wpa_ie) | ||
1947 | curr_bss->bcn_wpa_ie = | ||
1948 | (struct ieee_types_vendor_specific *) | ||
1949 | (curr_bss->beacon_buf + | ||
1950 | curr_bss->wpa_offset); | ||
1951 | |||
1952 | if (curr_bss->bcn_rsn_ie) | ||
1953 | curr_bss->bcn_rsn_ie = (struct ieee_types_generic *) | ||
1954 | (curr_bss->beacon_buf + | ||
1955 | curr_bss->rsn_offset); | ||
1956 | |||
1957 | if (curr_bss->bcn_ht_cap) | ||
1958 | curr_bss->bcn_ht_cap = (struct ieee80211_ht_cap *) | ||
1959 | (curr_bss->beacon_buf + | ||
1960 | curr_bss->ht_cap_offset); | ||
1961 | |||
1962 | if (curr_bss->bcn_ht_info) | ||
1963 | curr_bss->bcn_ht_info = (struct ieee80211_ht_info *) | ||
1964 | (curr_bss->beacon_buf + | ||
1965 | curr_bss->ht_info_offset); | ||
1966 | |||
1967 | if (curr_bss->bcn_bss_co_2040) | ||
1968 | curr_bss->bcn_bss_co_2040 = | ||
1969 | (u8 *) (curr_bss->beacon_buf + | ||
1970 | curr_bss->bss_co_2040_offset); | ||
1971 | |||
1972 | if (curr_bss->bcn_ext_cap) | ||
1973 | curr_bss->bcn_ext_cap = (u8 *) (curr_bss->beacon_buf + | ||
1974 | curr_bss->ext_cap_offset); | ||
1975 | |||
1976 | if (curr_bss->bcn_obss_scan) | ||
1977 | curr_bss->bcn_obss_scan = | ||
1978 | (struct ieee_types_obss_scan_param *) | ||
1979 | (curr_bss->beacon_buf + | ||
1980 | curr_bss->overlap_bss_offset); | ||
1981 | |||
1982 | spin_unlock_irqrestore(&priv->curr_bcn_buf_lock, flags); | ||
1983 | |||
1984 | dev_dbg(adapter->dev, "info: current beacon restored %d\n", | ||
1985 | priv->curr_bcn_size); | ||
1986 | } else { | ||
1987 | dev_warn(adapter->dev, | ||
1988 | "curr_bcn_buf not saved or bcn_buf has no space\n"); | ||
1989 | } | ||
1990 | } | ||
1991 | |||
1992 | /* | ||
1993 | * This function post processes the scan table after a new scan command has | ||
1994 | * completed. | ||
1995 | * | ||
1996 | * It inspects each entry of the scan table and tries to find an entry that | ||
1997 | * matches with our current associated/joined network from the scan. If | ||
1998 | * one is found, the stored copy of the BSS descriptor of our current network | ||
1999 | * is updated. | ||
2000 | * | ||
2001 | * It also debug dumps the current scan table contents after processing is over. | ||
2002 | */ | ||
2003 | static void | ||
2004 | mwifiex_process_scan_results(struct mwifiex_private *priv) | ||
2005 | { | ||
2006 | struct mwifiex_adapter *adapter = priv->adapter; | ||
2007 | s32 j; | ||
2008 | u32 i; | ||
2009 | unsigned long flags; | ||
2010 | |||
2011 | if (priv->media_connected) { | ||
2012 | |||
2013 | j = mwifiex_find_ssid_in_list(priv, &priv->curr_bss_params. | ||
2014 | bss_descriptor.ssid, | ||
2015 | priv->curr_bss_params. | ||
2016 | bss_descriptor.mac_address, | ||
2017 | priv->bss_mode); | ||
2018 | |||
2019 | if (j >= 0) { | ||
2020 | spin_lock_irqsave(&priv->curr_bcn_buf_lock, flags); | ||
2021 | priv->curr_bss_params.bss_descriptor.bcn_wpa_ie = NULL; | ||
2022 | priv->curr_bss_params.bss_descriptor.wpa_offset = 0; | ||
2023 | priv->curr_bss_params.bss_descriptor.bcn_rsn_ie = NULL; | ||
2024 | priv->curr_bss_params.bss_descriptor.rsn_offset = 0; | ||
2025 | priv->curr_bss_params.bss_descriptor.bcn_wapi_ie = NULL; | ||
2026 | priv->curr_bss_params.bss_descriptor.wapi_offset = 0; | ||
2027 | priv->curr_bss_params.bss_descriptor.bcn_ht_cap = NULL; | ||
2028 | priv->curr_bss_params.bss_descriptor.ht_cap_offset = | ||
2029 | 0; | ||
2030 | priv->curr_bss_params.bss_descriptor.bcn_ht_info = NULL; | ||
2031 | priv->curr_bss_params.bss_descriptor.ht_info_offset = | ||
2032 | 0; | ||
2033 | priv->curr_bss_params.bss_descriptor.bcn_bss_co_2040 = | ||
2034 | NULL; | ||
2035 | priv->curr_bss_params.bss_descriptor. | ||
2036 | bss_co_2040_offset = 0; | ||
2037 | priv->curr_bss_params.bss_descriptor.bcn_ext_cap = NULL; | ||
2038 | priv->curr_bss_params.bss_descriptor.ext_cap_offset = 0; | ||
2039 | priv->curr_bss_params.bss_descriptor. | ||
2040 | bcn_obss_scan = NULL; | ||
2041 | priv->curr_bss_params.bss_descriptor. | ||
2042 | overlap_bss_offset = 0; | ||
2043 | priv->curr_bss_params.bss_descriptor.beacon_buf = NULL; | ||
2044 | priv->curr_bss_params.bss_descriptor.beacon_buf_size = | ||
2045 | 0; | ||
2046 | priv->curr_bss_params.bss_descriptor. | ||
2047 | beacon_buf_size_max = 0; | ||
2048 | |||
2049 | dev_dbg(adapter->dev, "info: Found current ssid/bssid" | ||
2050 | " in list @ index #%d\n", j); | ||
2051 | /* Make a copy of current BSSID descriptor */ | ||
2052 | memcpy(&priv->curr_bss_params.bss_descriptor, | ||
2053 | &adapter->scan_table[j], | ||
2054 | sizeof(priv->curr_bss_params.bss_descriptor)); | ||
2055 | |||
2056 | mwifiex_save_curr_bcn(priv); | ||
2057 | spin_unlock_irqrestore(&priv->curr_bcn_buf_lock, flags); | ||
2058 | |||
2059 | } else { | ||
2060 | mwifiex_restore_curr_bcn(priv); | ||
2061 | } | ||
2062 | } | ||
2063 | |||
2064 | for (i = 0; i < adapter->num_in_scan_table; i++) | ||
2065 | dev_dbg(adapter->dev, "info: scan:(%02d) %pM " | ||
2066 | "RSSI[%03d], SSID[%s]\n", | ||
2067 | i, adapter->scan_table[i].mac_address, | ||
2068 | (s32) adapter->scan_table[i].rssi, | ||
2069 | adapter->scan_table[i].ssid.ssid); | ||
2070 | } | ||
2071 | |||
2072 | /* | ||
2073 | * This function converts radio type scan parameter to a band configuration | ||
2074 | * to be used in join command. | ||
2075 | */ | ||
2076 | static u8 | ||
2077 | mwifiex_radio_type_to_band(u8 radio_type) | ||
2078 | { | ||
2079 | u8 ret_band; | ||
2080 | |||
2081 | switch (radio_type) { | ||
2082 | case HostCmd_SCAN_RADIO_TYPE_A: | ||
2083 | ret_band = BAND_A; | ||
2084 | break; | ||
2085 | case HostCmd_SCAN_RADIO_TYPE_BG: | ||
2086 | default: | ||
2087 | ret_band = BAND_G; | ||
2088 | break; | ||
2089 | } | ||
2090 | |||
2091 | return ret_band; | ||
2092 | } | ||
2093 | |||
2094 | /* | ||
2095 | * This function deletes a specific indexed entry from the scan table. | ||
2096 | * | ||
2097 | * This also compacts the remaining entries and adjusts any buffering | ||
2098 | * of beacon/probe response data if needed. | ||
2099 | */ | ||
2100 | static void | ||
2101 | mwifiex_scan_delete_table_entry(struct mwifiex_private *priv, s32 table_idx) | ||
2102 | { | ||
2103 | struct mwifiex_adapter *adapter = priv->adapter; | ||
2104 | u32 del_idx; | ||
2105 | u32 beacon_buf_adj; | ||
2106 | u8 *beacon_buf; | ||
2107 | |||
2108 | /* | ||
2109 | * Shift the saved beacon buffer data for the scan table back over the | ||
2110 | * entry being removed. Update the end of buffer pointer. Save the | ||
2111 | * deleted buffer allocation size for pointer adjustments for entries | ||
2112 | * compacted after the deleted index. | ||
2113 | */ | ||
2114 | beacon_buf_adj = adapter->scan_table[table_idx].beacon_buf_size_max; | ||
2115 | |||
2116 | dev_dbg(adapter->dev, "info: Scan: Delete Entry %d, beacon buffer " | ||
2117 | "removal = %d bytes\n", table_idx, beacon_buf_adj); | ||
2118 | |||
2119 | /* Check if the table entry had storage allocated for its beacon */ | ||
2120 | if (beacon_buf_adj) { | ||
2121 | beacon_buf = adapter->scan_table[table_idx].beacon_buf; | ||
2122 | |||
2123 | /* | ||
2124 | * Remove the entry's buffer space, decrement the table end | ||
2125 | * pointer by the amount we are removing | ||
2126 | */ | ||
2127 | adapter->bcn_buf_end -= beacon_buf_adj; | ||
2128 | |||
2129 | dev_dbg(adapter->dev, "info: scan: delete entry %d," | ||
2130 | " compact data: %p <- %p (sz = %d)\n", | ||
2131 | table_idx, beacon_buf, | ||
2132 | beacon_buf + beacon_buf_adj, | ||
2133 | (int)(adapter->bcn_buf_end - beacon_buf)); | ||
2134 | |||
2135 | /* | ||
2136 | * Compact data storage. Copy all data after the deleted | ||
2137 | * entry's end address (beacon_buf + beacon_buf_adj) back | ||
2138 | * to the original start address (beacon_buf). | ||
2139 | * | ||
2140 | * Scan table entries affected by the move will have their | ||
2141 | * entry pointer adjusted below. | ||
2142 | * | ||
2143 | * Use memmove since the dest/src memory regions overlap. | ||
2144 | */ | ||
2145 | memmove(beacon_buf, beacon_buf + beacon_buf_adj, | ||
2146 | adapter->bcn_buf_end - beacon_buf); | ||
2147 | } | ||
2148 | |||
2149 | dev_dbg(adapter->dev, | ||
2150 | "info: Scan: Delete Entry %d, num_in_scan_table = %d\n", | ||
2151 | table_idx, adapter->num_in_scan_table); | ||
2152 | |||
2153 | /* Shift all of the entries after the table_idx back by one, compacting | ||
2154 | the table and removing the requested entry */ | ||
2155 | for (del_idx = table_idx; (del_idx + 1) < adapter->num_in_scan_table; | ||
2156 | del_idx++) { | ||
2157 | /* Copy the next entry over this one */ | ||
2158 | memcpy(adapter->scan_table + del_idx, | ||
2159 | adapter->scan_table + del_idx + 1, | ||
2160 | sizeof(struct mwifiex_bssdescriptor)); | ||
2161 | |||
2162 | /* | ||
2163 | * Adjust this entry's pointer to its beacon buffer based on | ||
2164 | * the removed/compacted entry from the deleted index. Don't | ||
2165 | * decrement if the buffer pointer is NULL (no data stored for | ||
2166 | * this entry). | ||
2167 | */ | ||
2168 | if (adapter->scan_table[del_idx].beacon_buf) { | ||
2169 | adapter->scan_table[del_idx].beacon_buf -= | ||
2170 | beacon_buf_adj; | ||
2171 | if (adapter->scan_table[del_idx].bcn_wpa_ie) | ||
2172 | adapter->scan_table[del_idx].bcn_wpa_ie = | ||
2173 | (struct ieee_types_vendor_specific *) | ||
2174 | (adapter->scan_table[del_idx]. | ||
2175 | beacon_buf + | ||
2176 | adapter->scan_table[del_idx]. | ||
2177 | wpa_offset); | ||
2178 | if (adapter->scan_table[del_idx].bcn_rsn_ie) | ||
2179 | adapter->scan_table[del_idx].bcn_rsn_ie = | ||
2180 | (struct ieee_types_generic *) | ||
2181 | (adapter->scan_table[del_idx]. | ||
2182 | beacon_buf + | ||
2183 | adapter->scan_table[del_idx]. | ||
2184 | rsn_offset); | ||
2185 | if (adapter->scan_table[del_idx].bcn_wapi_ie) | ||
2186 | adapter->scan_table[del_idx].bcn_wapi_ie = | ||
2187 | (struct ieee_types_generic *) | ||
2188 | (adapter->scan_table[del_idx].beacon_buf | ||
2189 | + adapter->scan_table[del_idx]. | ||
2190 | wapi_offset); | ||
2191 | if (adapter->scan_table[del_idx].bcn_ht_cap) | ||
2192 | adapter->scan_table[del_idx].bcn_ht_cap = | ||
2193 | (struct ieee80211_ht_cap *) | ||
2194 | (adapter->scan_table[del_idx].beacon_buf | ||
2195 | + adapter->scan_table[del_idx]. | ||
2196 | ht_cap_offset); | ||
2197 | |||
2198 | if (adapter->scan_table[del_idx].bcn_ht_info) | ||
2199 | adapter->scan_table[del_idx].bcn_ht_info = | ||
2200 | (struct ieee80211_ht_info *) | ||
2201 | (adapter->scan_table[del_idx].beacon_buf | ||
2202 | + adapter->scan_table[del_idx]. | ||
2203 | ht_info_offset); | ||
2204 | if (adapter->scan_table[del_idx].bcn_bss_co_2040) | ||
2205 | adapter->scan_table[del_idx].bcn_bss_co_2040 = | ||
2206 | (u8 *) | ||
2207 | (adapter->scan_table[del_idx].beacon_buf | ||
2208 | + adapter->scan_table[del_idx]. | ||
2209 | bss_co_2040_offset); | ||
2210 | if (adapter->scan_table[del_idx].bcn_ext_cap) | ||
2211 | adapter->scan_table[del_idx].bcn_ext_cap = | ||
2212 | (u8 *) | ||
2213 | (adapter->scan_table[del_idx].beacon_buf | ||
2214 | + adapter->scan_table[del_idx]. | ||
2215 | ext_cap_offset); | ||
2216 | if (adapter->scan_table[del_idx].bcn_obss_scan) | ||
2217 | adapter->scan_table[del_idx]. | ||
2218 | bcn_obss_scan = | ||
2219 | (struct ieee_types_obss_scan_param *) | ||
2220 | (adapter->scan_table[del_idx].beacon_buf | ||
2221 | + adapter->scan_table[del_idx]. | ||
2222 | overlap_bss_offset); | ||
2223 | } | ||
2224 | } | ||
2225 | |||
2226 | /* The last entry is invalid now that it has been deleted or moved | ||
2227 | back */ | ||
2228 | memset(adapter->scan_table + adapter->num_in_scan_table - 1, | ||
2229 | 0x00, sizeof(struct mwifiex_bssdescriptor)); | ||
2230 | |||
2231 | adapter->num_in_scan_table--; | ||
2232 | } | ||
2233 | |||
2234 | /* | ||
2235 | * This function deletes all occurrences of a given SSID from the scan table. | ||
2236 | * | ||
2237 | * This iterates through the scan table and deletes all entries that match | ||
2238 | * the given SSID. It also compacts the remaining scan table entries. | ||
2239 | */ | ||
2240 | static int | ||
2241 | mwifiex_scan_delete_ssid_table_entry(struct mwifiex_private *priv, | ||
2242 | struct mwifiex_802_11_ssid *del_ssid) | ||
2243 | { | ||
2244 | int ret = -1; | ||
2245 | s32 table_idx; | ||
2246 | |||
2247 | dev_dbg(priv->adapter->dev, "info: scan: delete ssid entry: %-32s\n", | ||
2248 | del_ssid->ssid); | ||
2249 | |||
2250 | /* If the requested SSID is found in the table, delete it. Then keep | ||
2251 | searching the table for multiple entires for the SSID until no | ||
2252 | more are found */ | ||
2253 | while ((table_idx = mwifiex_find_ssid_in_list(priv, del_ssid, NULL, | ||
2254 | MWIFIEX_BSS_MODE_AUTO)) >= | ||
2255 | 0) { | ||
2256 | dev_dbg(priv->adapter->dev, | ||
2257 | "info: Scan: Delete SSID Entry: Found Idx = %d\n", | ||
2258 | table_idx); | ||
2259 | ret = 0; | ||
2260 | mwifiex_scan_delete_table_entry(priv, table_idx); | ||
2261 | } | ||
2262 | |||
2263 | return ret; | ||
2264 | } | ||
2265 | |||
2266 | /* | ||
2267 | * This is an internal function used to start a scan based on an input | ||
2268 | * configuration. | ||
2269 | * | ||
2270 | * This uses the input user scan configuration information when provided in | ||
2271 | * order to send the appropriate scan commands to firmware to populate or | ||
2272 | * update the internal driver scan table. | ||
2273 | */ | ||
2274 | int mwifiex_scan_networks(struct mwifiex_private *priv, | ||
2275 | void *wait_buf, u16 action, | ||
2276 | const struct mwifiex_user_scan_cfg *user_scan_in, | ||
2277 | struct mwifiex_scan_resp *scan_resp) | ||
2278 | { | ||
2279 | int ret = 0; | ||
2280 | struct mwifiex_adapter *adapter = priv->adapter; | ||
2281 | struct cmd_ctrl_node *cmd_node = NULL; | ||
2282 | union mwifiex_scan_cmd_config_tlv *scan_cfg_out = NULL; | ||
2283 | struct mwifiex_ie_types_chan_list_param_set *chan_list_out; | ||
2284 | u32 buf_size; | ||
2285 | struct mwifiex_chan_scan_param_set *scan_chan_list; | ||
2286 | u8 keep_previous_scan; | ||
2287 | u8 filtered_scan; | ||
2288 | u8 scan_current_chan_only; | ||
2289 | u8 max_chan_per_scan; | ||
2290 | unsigned long flags; | ||
2291 | |||
2292 | if (action == HostCmd_ACT_GEN_GET) { | ||
2293 | if (scan_resp) { | ||
2294 | scan_resp->scan_table = (u8 *) adapter->scan_table; | ||
2295 | scan_resp->num_in_scan_table = | ||
2296 | adapter->num_in_scan_table; | ||
2297 | } else { | ||
2298 | ret = -1; | ||
2299 | } | ||
2300 | return ret; | ||
2301 | } | ||
2302 | |||
2303 | if (adapter->scan_processing && action == HostCmd_ACT_GEN_SET) { | ||
2304 | dev_dbg(adapter->dev, "cmd: Scan already in process...\n"); | ||
2305 | return ret; | ||
2306 | } | ||
2307 | |||
2308 | spin_lock_irqsave(&adapter->mwifiex_cmd_lock, flags); | ||
2309 | adapter->scan_processing = true; | ||
2310 | spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, flags); | ||
2311 | |||
2312 | if (priv->scan_block && action == HostCmd_ACT_GEN_SET) { | ||
2313 | dev_dbg(adapter->dev, | ||
2314 | "cmd: Scan is blocked during association...\n"); | ||
2315 | return ret; | ||
2316 | } | ||
2317 | |||
2318 | scan_cfg_out = kzalloc(sizeof(union mwifiex_scan_cmd_config_tlv), | ||
2319 | GFP_KERNEL); | ||
2320 | if (!scan_cfg_out) { | ||
2321 | dev_err(adapter->dev, "failed to alloc scan_cfg_out\n"); | ||
2322 | return -1; | ||
2323 | } | ||
2324 | |||
2325 | buf_size = sizeof(struct mwifiex_chan_scan_param_set) * | ||
2326 | MWIFIEX_USER_SCAN_CHAN_MAX; | ||
2327 | scan_chan_list = kzalloc(buf_size, GFP_KERNEL); | ||
2328 | if (!scan_chan_list) { | ||
2329 | dev_err(adapter->dev, "failed to alloc scan_chan_list\n"); | ||
2330 | kfree(scan_cfg_out); | ||
2331 | return -1; | ||
2332 | } | ||
2333 | |||
2334 | keep_previous_scan = false; | ||
2335 | |||
2336 | mwifiex_scan_setup_scan_config(priv, user_scan_in, | ||
2337 | &scan_cfg_out->config, &chan_list_out, | ||
2338 | scan_chan_list, &max_chan_per_scan, | ||
2339 | &filtered_scan, &scan_current_chan_only); | ||
2340 | |||
2341 | if (user_scan_in) | ||
2342 | keep_previous_scan = user_scan_in->keep_previous_scan; | ||
2343 | |||
2344 | |||
2345 | if (!keep_previous_scan) { | ||
2346 | memset(adapter->scan_table, 0x00, | ||
2347 | sizeof(struct mwifiex_bssdescriptor) * IW_MAX_AP); | ||
2348 | adapter->num_in_scan_table = 0; | ||
2349 | adapter->bcn_buf_end = adapter->bcn_buf; | ||
2350 | } | ||
2351 | |||
2352 | ret = mwifiex_scan_channel_list(priv, wait_buf, max_chan_per_scan, | ||
2353 | filtered_scan, &scan_cfg_out->config, | ||
2354 | chan_list_out, scan_chan_list); | ||
2355 | |||
2356 | /* Get scan command from scan_pending_q and put to cmd_pending_q */ | ||
2357 | if (!ret) { | ||
2358 | spin_lock_irqsave(&adapter->scan_pending_q_lock, flags); | ||
2359 | if (!list_empty(&adapter->scan_pending_q)) { | ||
2360 | cmd_node = list_first_entry(&adapter->scan_pending_q, | ||
2361 | struct cmd_ctrl_node, list); | ||
2362 | list_del(&cmd_node->list); | ||
2363 | spin_unlock_irqrestore(&adapter->scan_pending_q_lock, | ||
2364 | flags); | ||
2365 | mwifiex_insert_cmd_to_pending_q(adapter, cmd_node, | ||
2366 | true); | ||
2367 | } else { | ||
2368 | spin_unlock_irqrestore(&adapter->scan_pending_q_lock, | ||
2369 | flags); | ||
2370 | } | ||
2371 | ret = -EINPROGRESS; | ||
2372 | } else { | ||
2373 | spin_lock_irqsave(&adapter->mwifiex_cmd_lock, flags); | ||
2374 | adapter->scan_processing = true; | ||
2375 | spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, flags); | ||
2376 | } | ||
2377 | |||
2378 | kfree(scan_cfg_out); | ||
2379 | kfree(scan_chan_list); | ||
2380 | return ret; | ||
2381 | } | ||
2382 | |||
2383 | /* | ||
2384 | * This function prepares a scan command to be sent to the firmware. | ||
2385 | * | ||
2386 | * This uses the scan command configuration sent to the command processing | ||
2387 | * module in command preparation stage to configure a scan command structure | ||
2388 | * to send to firmware. | ||
2389 | * | ||
2390 | * The fixed fields specifying the BSS type and BSSID filters as well as a | ||
2391 | * variable number/length of TLVs are sent in the command to firmware. | ||
2392 | * | ||
2393 | * Preparation also includes - | ||
2394 | * - Setting command ID, and proper size | ||
2395 | * - Ensuring correct endian-ness | ||
2396 | */ | ||
2397 | int mwifiex_cmd_802_11_scan(struct mwifiex_private *priv, | ||
2398 | struct host_cmd_ds_command *cmd, void *data_buf) | ||
2399 | { | ||
2400 | struct host_cmd_ds_802_11_scan *scan_cmd = &cmd->params.scan; | ||
2401 | struct mwifiex_scan_cmd_config *scan_cfg; | ||
2402 | |||
2403 | scan_cfg = (struct mwifiex_scan_cmd_config *) data_buf; | ||
2404 | |||
2405 | /* Set fixed field variables in scan command */ | ||
2406 | scan_cmd->bss_mode = scan_cfg->bss_mode; | ||
2407 | memcpy(scan_cmd->bssid, scan_cfg->specific_bssid, | ||
2408 | sizeof(scan_cmd->bssid)); | ||
2409 | memcpy(scan_cmd->tlv_buffer, scan_cfg->tlv_buf, scan_cfg->tlv_buf_len); | ||
2410 | |||
2411 | cmd->command = cpu_to_le16(HostCmd_CMD_802_11_SCAN); | ||
2412 | |||
2413 | /* Size is equal to the sizeof(fixed portions) + the TLV len + header */ | ||
2414 | cmd->size = cpu_to_le16((u16) (sizeof(scan_cmd->bss_mode) | ||
2415 | + sizeof(scan_cmd->bssid) | ||
2416 | + scan_cfg->tlv_buf_len + S_DS_GEN)); | ||
2417 | |||
2418 | return 0; | ||
2419 | } | ||
2420 | |||
2421 | /* | ||
2422 | * This function handles the command response of scan. | ||
2423 | * | ||
2424 | * The response buffer for the scan command has the following | ||
2425 | * memory layout: | ||
2426 | * | ||
2427 | * .-------------------------------------------------------------. | ||
2428 | * | Header (4 * sizeof(t_u16)): Standard command response hdr | | ||
2429 | * .-------------------------------------------------------------. | ||
2430 | * | BufSize (t_u16) : sizeof the BSS Description data | | ||
2431 | * .-------------------------------------------------------------. | ||
2432 | * | NumOfSet (t_u8) : Number of BSS Descs returned | | ||
2433 | * .-------------------------------------------------------------. | ||
2434 | * | BSSDescription data (variable, size given in BufSize) | | ||
2435 | * .-------------------------------------------------------------. | ||
2436 | * | TLV data (variable, size calculated using Header->Size, | | ||
2437 | * | BufSize and sizeof the fixed fields above) | | ||
2438 | * .-------------------------------------------------------------. | ||
2439 | */ | ||
2440 | int mwifiex_ret_802_11_scan(struct mwifiex_private *priv, | ||
2441 | struct host_cmd_ds_command *resp, void *wq_buf) | ||
2442 | { | ||
2443 | int ret = 0; | ||
2444 | struct mwifiex_adapter *adapter = priv->adapter; | ||
2445 | struct mwifiex_wait_queue *wait_queue = NULL; | ||
2446 | struct cmd_ctrl_node *cmd_node = NULL; | ||
2447 | struct host_cmd_ds_802_11_scan_rsp *scan_rsp = NULL; | ||
2448 | struct mwifiex_bssdescriptor *bss_new_entry = NULL; | ||
2449 | struct mwifiex_ie_types_data *tlv_data; | ||
2450 | struct mwifiex_ie_types_tsf_timestamp *tsf_tlv; | ||
2451 | u8 *bss_info; | ||
2452 | u32 scan_resp_size; | ||
2453 | u32 bytes_left; | ||
2454 | u32 num_in_table; | ||
2455 | u32 bss_idx; | ||
2456 | u32 idx; | ||
2457 | u32 tlv_buf_size; | ||
2458 | long long tsf_val; | ||
2459 | struct mwifiex_chan_freq_power *cfp; | ||
2460 | struct mwifiex_ie_types_chan_band_list_param_set *chan_band_tlv; | ||
2461 | struct chan_band_param_set *chan_band; | ||
2462 | u8 band; | ||
2463 | u8 is_bgscan_resp; | ||
2464 | unsigned long flags; | ||
2465 | |||
2466 | is_bgscan_resp = (le16_to_cpu(resp->command) | ||
2467 | == HostCmd_CMD_802_11_BG_SCAN_QUERY); | ||
2468 | if (is_bgscan_resp) | ||
2469 | scan_rsp = &resp->params.bg_scan_query_resp.scan_resp; | ||
2470 | else | ||
2471 | scan_rsp = &resp->params.scan_resp; | ||
2472 | |||
2473 | |||
2474 | if (scan_rsp->number_of_sets > IW_MAX_AP) { | ||
2475 | dev_err(adapter->dev, "SCAN_RESP: too many AP returned (%d)\n", | ||
2476 | scan_rsp->number_of_sets); | ||
2477 | ret = -1; | ||
2478 | goto done; | ||
2479 | } | ||
2480 | |||
2481 | bytes_left = le16_to_cpu(scan_rsp->bss_descript_size); | ||
2482 | dev_dbg(adapter->dev, "info: SCAN_RESP: bss_descript_size %d\n", | ||
2483 | bytes_left); | ||
2484 | |||
2485 | scan_resp_size = le16_to_cpu(resp->size); | ||
2486 | |||
2487 | dev_dbg(adapter->dev, | ||
2488 | "info: SCAN_RESP: returned %d APs before parsing\n", | ||
2489 | scan_rsp->number_of_sets); | ||
2490 | |||
2491 | num_in_table = adapter->num_in_scan_table; | ||
2492 | bss_info = scan_rsp->bss_desc_and_tlv_buffer; | ||
2493 | |||
2494 | /* | ||
2495 | * The size of the TLV buffer is equal to the entire command response | ||
2496 | * size (scan_resp_size) minus the fixed fields (sizeof()'s), the | ||
2497 | * BSS Descriptions (bss_descript_size as bytesLef) and the command | ||
2498 | * response header (S_DS_GEN) | ||
2499 | */ | ||
2500 | tlv_buf_size = scan_resp_size - (bytes_left | ||
2501 | + sizeof(scan_rsp->bss_descript_size) | ||
2502 | + sizeof(scan_rsp->number_of_sets) | ||
2503 | + S_DS_GEN); | ||
2504 | |||
2505 | tlv_data = (struct mwifiex_ie_types_data *) (scan_rsp-> | ||
2506 | bss_desc_and_tlv_buffer + | ||
2507 | bytes_left); | ||
2508 | |||
2509 | /* Search the TLV buffer space in the scan response for any valid | ||
2510 | TLVs */ | ||
2511 | mwifiex_ret_802_11_scan_get_tlv_ptrs(adapter, tlv_data, tlv_buf_size, | ||
2512 | TLV_TYPE_TSFTIMESTAMP, | ||
2513 | (struct mwifiex_ie_types_data **) | ||
2514 | &tsf_tlv); | ||
2515 | |||
2516 | /* Search the TLV buffer space in the scan response for any valid | ||
2517 | TLVs */ | ||
2518 | mwifiex_ret_802_11_scan_get_tlv_ptrs(adapter, tlv_data, tlv_buf_size, | ||
2519 | TLV_TYPE_CHANNELBANDLIST, | ||
2520 | (struct mwifiex_ie_types_data **) | ||
2521 | &chan_band_tlv); | ||
2522 | |||
2523 | /* | ||
2524 | * Process each scan response returned (scan_rsp->number_of_sets). | ||
2525 | * Save the information in the bss_new_entry and then insert into the | ||
2526 | * driver scan table either as an update to an existing entry | ||
2527 | * or as an addition at the end of the table | ||
2528 | */ | ||
2529 | bss_new_entry = kzalloc(sizeof(struct mwifiex_bssdescriptor), | ||
2530 | GFP_KERNEL); | ||
2531 | if (!bss_new_entry) { | ||
2532 | dev_err(adapter->dev, " failed to alloc bss_new_entry\n"); | ||
2533 | return -1; | ||
2534 | } | ||
2535 | |||
2536 | for (idx = 0; idx < scan_rsp->number_of_sets && bytes_left; idx++) { | ||
2537 | /* Zero out the bss_new_entry we are about to store info in */ | ||
2538 | memset(bss_new_entry, 0x00, | ||
2539 | sizeof(struct mwifiex_bssdescriptor)); | ||
2540 | |||
2541 | if (mwifiex_interpret_bss_desc_with_ie(adapter, bss_new_entry, | ||
2542 | &bss_info, | ||
2543 | &bytes_left)) { | ||
2544 | /* Error parsing/interpreting scan response, skipped */ | ||
2545 | dev_err(adapter->dev, "SCAN_RESP: " | ||
2546 | "mwifiex_interpret_bss_desc_with_ie " | ||
2547 | "returned ERROR\n"); | ||
2548 | continue; | ||
2549 | } | ||
2550 | |||
2551 | /* Process the data fields and IEs returned for this BSS */ | ||
2552 | dev_dbg(adapter->dev, "info: SCAN_RESP: BSSID = %pM\n", | ||
2553 | bss_new_entry->mac_address); | ||
2554 | |||
2555 | /* Search the scan table for the same bssid */ | ||
2556 | for (bss_idx = 0; bss_idx < num_in_table; bss_idx++) { | ||
2557 | if (memcmp(bss_new_entry->mac_address, | ||
2558 | adapter->scan_table[bss_idx].mac_address, | ||
2559 | sizeof(bss_new_entry->mac_address))) { | ||
2560 | continue; | ||
2561 | } | ||
2562 | /* | ||
2563 | * If the SSID matches as well, it is a | ||
2564 | * duplicate of this entry. Keep the bss_idx | ||
2565 | * set to this entry so we replace the old | ||
2566 | * contents in the table | ||
2567 | */ | ||
2568 | if ((bss_new_entry->ssid.ssid_len | ||
2569 | == adapter->scan_table[bss_idx]. ssid.ssid_len) | ||
2570 | && (!memcmp(bss_new_entry->ssid.ssid, | ||
2571 | adapter->scan_table[bss_idx].ssid.ssid, | ||
2572 | bss_new_entry->ssid.ssid_len))) { | ||
2573 | dev_dbg(adapter->dev, "info: SCAN_RESP:" | ||
2574 | " duplicate of index: %d\n", bss_idx); | ||
2575 | break; | ||
2576 | } | ||
2577 | } | ||
2578 | /* | ||
2579 | * If the bss_idx is equal to the number of entries in | ||
2580 | * the table, the new entry was not a duplicate; append | ||
2581 | * it to the scan table | ||
2582 | */ | ||
2583 | if (bss_idx == num_in_table) { | ||
2584 | /* Range check the bss_idx, keep it limited to | ||
2585 | the last entry */ | ||
2586 | if (bss_idx == IW_MAX_AP) | ||
2587 | bss_idx--; | ||
2588 | else | ||
2589 | num_in_table++; | ||
2590 | } | ||
2591 | |||
2592 | /* | ||
2593 | * Save the beacon/probe response returned for later application | ||
2594 | * retrieval. Duplicate beacon/probe responses are updated if | ||
2595 | * possible | ||
2596 | */ | ||
2597 | mwifiex_ret_802_11_scan_store_beacon(priv, bss_idx, | ||
2598 | num_in_table, bss_new_entry); | ||
2599 | /* | ||
2600 | * If the TSF TLV was appended to the scan results, save this | ||
2601 | * entry's TSF value in the networkTSF field.The networkTSF is | ||
2602 | * the firmware's TSF value at the time the beacon or probe | ||
2603 | * response was received. | ||
2604 | */ | ||
2605 | if (tsf_tlv) { | ||
2606 | memcpy(&tsf_val, &tsf_tlv->tsf_data[idx * TSF_DATA_SIZE] | ||
2607 | , sizeof(tsf_val)); | ||
2608 | memcpy(&bss_new_entry->network_tsf, &tsf_val, | ||
2609 | sizeof(bss_new_entry->network_tsf)); | ||
2610 | } | ||
2611 | band = BAND_G; | ||
2612 | if (chan_band_tlv) { | ||
2613 | chan_band = &chan_band_tlv->chan_band_param[idx]; | ||
2614 | band = mwifiex_radio_type_to_band(chan_band->radio_type | ||
2615 | & (BIT(0) | BIT(1))); | ||
2616 | } | ||
2617 | |||
2618 | /* Save the band designation for this entry for use in join */ | ||
2619 | bss_new_entry->bss_band = band; | ||
2620 | cfp = mwifiex_get_cfp_by_band_and_channel_from_cfg80211(priv, | ||
2621 | (u8) bss_new_entry->bss_band, | ||
2622 | (u16)bss_new_entry->channel); | ||
2623 | |||
2624 | if (cfp) | ||
2625 | bss_new_entry->freq = cfp->freq; | ||
2626 | else | ||
2627 | bss_new_entry->freq = 0; | ||
2628 | |||
2629 | /* Copy the locally created bss_new_entry to the scan table */ | ||
2630 | memcpy(&adapter->scan_table[bss_idx], bss_new_entry, | ||
2631 | sizeof(adapter->scan_table[bss_idx])); | ||
2632 | |||
2633 | } | ||
2634 | |||
2635 | dev_dbg(adapter->dev, | ||
2636 | "info: SCAN_RESP: Scanned %2d APs, %d valid, %d total\n", | ||
2637 | scan_rsp->number_of_sets, | ||
2638 | num_in_table - adapter->num_in_scan_table, num_in_table); | ||
2639 | |||
2640 | /* Update the total number of BSSIDs in the scan table */ | ||
2641 | adapter->num_in_scan_table = num_in_table; | ||
2642 | |||
2643 | spin_lock_irqsave(&adapter->scan_pending_q_lock, flags); | ||
2644 | if (list_empty(&adapter->scan_pending_q)) { | ||
2645 | spin_unlock_irqrestore(&adapter->scan_pending_q_lock, flags); | ||
2646 | spin_lock_irqsave(&adapter->mwifiex_cmd_lock, flags); | ||
2647 | adapter->scan_processing = false; | ||
2648 | spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, flags); | ||
2649 | /* | ||
2650 | * Process the resulting scan table: | ||
2651 | * - Remove any bad ssids | ||
2652 | * - Update our current BSS information from scan data | ||
2653 | */ | ||
2654 | mwifiex_process_scan_results(priv); | ||
2655 | |||
2656 | /* Need to indicate IOCTL complete */ | ||
2657 | wait_queue = (struct mwifiex_wait_queue *) wq_buf; | ||
2658 | if (wait_queue) { | ||
2659 | wait_queue->status = MWIFIEX_ERROR_NO_ERROR; | ||
2660 | |||
2661 | /* Indicate ioctl complete */ | ||
2662 | mwifiex_ioctl_complete(adapter, | ||
2663 | (struct mwifiex_wait_queue *) wait_queue, 0); | ||
2664 | } | ||
2665 | if (priv->report_scan_result) | ||
2666 | priv->report_scan_result = false; | ||
2667 | if (priv->scan_pending_on_block) { | ||
2668 | priv->scan_pending_on_block = false; | ||
2669 | up(&priv->async_sem); | ||
2670 | } | ||
2671 | |||
2672 | } else { | ||
2673 | /* Get scan command from scan_pending_q and put to | ||
2674 | cmd_pending_q */ | ||
2675 | cmd_node = list_first_entry(&adapter->scan_pending_q, | ||
2676 | struct cmd_ctrl_node, list); | ||
2677 | list_del(&cmd_node->list); | ||
2678 | spin_unlock_irqrestore(&adapter->scan_pending_q_lock, flags); | ||
2679 | |||
2680 | mwifiex_insert_cmd_to_pending_q(adapter, cmd_node, true); | ||
2681 | } | ||
2682 | |||
2683 | done: | ||
2684 | kfree((u8 *) bss_new_entry); | ||
2685 | return ret; | ||
2686 | } | ||
2687 | |||
2688 | /* | ||
2689 | * This function prepares command for background scan query. | ||
2690 | * | ||
2691 | * Preparation includes - | ||
2692 | * - Setting command ID and proper size | ||
2693 | * - Setting background scan flush parameter | ||
2694 | * - Ensuring correct endian-ness | ||
2695 | */ | ||
2696 | int mwifiex_cmd_802_11_bg_scan_query(struct mwifiex_private *priv, | ||
2697 | struct host_cmd_ds_command *cmd, | ||
2698 | void *data_buf) | ||
2699 | { | ||
2700 | struct host_cmd_ds_802_11_bg_scan_query *bg_query = | ||
2701 | &cmd->params.bg_scan_query; | ||
2702 | |||
2703 | cmd->command = cpu_to_le16(HostCmd_CMD_802_11_BG_SCAN_QUERY); | ||
2704 | cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_802_11_bg_scan_query) | ||
2705 | + S_DS_GEN); | ||
2706 | |||
2707 | bg_query->flush = 1; | ||
2708 | |||
2709 | return 0; | ||
2710 | } | ||
2711 | |||
2712 | /* | ||
2713 | * This function finds a SSID in the scan table. | ||
2714 | * | ||
2715 | * A BSSID may optionally be provided to qualify the SSID. | ||
2716 | * For non-Auto mode, further check is made to make sure the | ||
2717 | * BSS found in the scan table is compatible with the current | ||
2718 | * settings of the driver. | ||
2719 | */ | ||
2720 | s32 | ||
2721 | mwifiex_find_ssid_in_list(struct mwifiex_private *priv, | ||
2722 | struct mwifiex_802_11_ssid *ssid, u8 *bssid, | ||
2723 | u32 mode) | ||
2724 | { | ||
2725 | struct mwifiex_adapter *adapter = priv->adapter; | ||
2726 | s32 net = -1, j; | ||
2727 | u8 best_rssi = 0; | ||
2728 | u32 i; | ||
2729 | |||
2730 | dev_dbg(adapter->dev, "info: num of entries in table = %d\n", | ||
2731 | adapter->num_in_scan_table); | ||
2732 | |||
2733 | /* | ||
2734 | * Loop through the table until the maximum is reached or until a match | ||
2735 | * is found based on the bssid field comparison | ||
2736 | */ | ||
2737 | for (i = 0; | ||
2738 | i < adapter->num_in_scan_table && (!bssid || (bssid && net < 0)); | ||
2739 | i++) { | ||
2740 | if (!mwifiex_ssid_cmp(&adapter->scan_table[i].ssid, ssid) && | ||
2741 | (!bssid | ||
2742 | || !memcmp(adapter->scan_table[i].mac_address, bssid, | ||
2743 | ETH_ALEN)) | ||
2744 | && | ||
2745 | (mwifiex_get_cfp_by_band_and_channel_from_cfg80211 | ||
2746 | (priv, (u8) adapter->scan_table[i].bss_band, | ||
2747 | (u16) adapter->scan_table[i].channel))) { | ||
2748 | switch (mode) { | ||
2749 | case MWIFIEX_BSS_MODE_INFRA: | ||
2750 | case MWIFIEX_BSS_MODE_IBSS: | ||
2751 | j = mwifiex_is_network_compatible(priv, i, | ||
2752 | mode); | ||
2753 | |||
2754 | if (j >= 0) { | ||
2755 | if (SCAN_RSSI | ||
2756 | (adapter->scan_table[i].rssi) > | ||
2757 | best_rssi) { | ||
2758 | best_rssi = SCAN_RSSI(adapter-> | ||
2759 | scan_table | ||
2760 | [i].rssi); | ||
2761 | net = i; | ||
2762 | } | ||
2763 | } else { | ||
2764 | if (net == -1) | ||
2765 | net = j; | ||
2766 | } | ||
2767 | break; | ||
2768 | case MWIFIEX_BSS_MODE_AUTO: | ||
2769 | default: | ||
2770 | /* | ||
2771 | * Do not check compatibility if the mode | ||
2772 | * requested is Auto/Unknown. Allows generic | ||
2773 | * find to work without verifying against the | ||
2774 | * Adapter security settings | ||
2775 | */ | ||
2776 | if (SCAN_RSSI(adapter->scan_table[i].rssi) > | ||
2777 | best_rssi) { | ||
2778 | best_rssi = SCAN_RSSI(adapter-> | ||
2779 | scan_table[i].rssi); | ||
2780 | net = i; | ||
2781 | } | ||
2782 | break; | ||
2783 | } | ||
2784 | } | ||
2785 | } | ||
2786 | |||
2787 | return net; | ||
2788 | } | ||
2789 | |||
2790 | /* | ||
2791 | * This function finds a specific compatible BSSID in the scan list. | ||
2792 | * | ||
2793 | * This function loops through the scan table looking for a compatible | ||
2794 | * match. If a BSSID matches, but the BSS is found to be not compatible | ||
2795 | * the function ignores it and continues to search through the rest of | ||
2796 | * the entries in case there is an AP with multiple SSIDs assigned to | ||
2797 | * the same BSSID. | ||
2798 | */ | ||
2799 | s32 | ||
2800 | mwifiex_find_bssid_in_list(struct mwifiex_private *priv, u8 *bssid, | ||
2801 | u32 mode) | ||
2802 | { | ||
2803 | struct mwifiex_adapter *adapter = priv->adapter; | ||
2804 | s32 net = -1; | ||
2805 | u32 i; | ||
2806 | |||
2807 | if (!bssid) | ||
2808 | return -1; | ||
2809 | |||
2810 | dev_dbg(adapter->dev, "info: FindBSSID: Num of BSSIDs = %d\n", | ||
2811 | adapter->num_in_scan_table); | ||
2812 | |||
2813 | /* | ||
2814 | * Look through the scan table for a compatible match. The ret return | ||
2815 | * variable will be equal to the index in the scan table (greater | ||
2816 | * than zero) if the network is compatible. The loop will continue | ||
2817 | * past a matched bssid that is not compatible in case there is an | ||
2818 | * AP with multiple SSIDs assigned to the same BSSID | ||
2819 | */ | ||
2820 | for (i = 0; net < 0 && i < adapter->num_in_scan_table; i++) { | ||
2821 | if (!memcmp | ||
2822 | (adapter->scan_table[i].mac_address, bssid, ETH_ALEN) | ||
2823 | && mwifiex_get_cfp_by_band_and_channel_from_cfg80211 | ||
2824 | (priv, | ||
2825 | (u8) adapter-> | ||
2826 | scan_table[i]. | ||
2827 | bss_band, | ||
2828 | (u16) adapter-> | ||
2829 | scan_table[i]. | ||
2830 | channel)) { | ||
2831 | switch (mode) { | ||
2832 | case MWIFIEX_BSS_MODE_INFRA: | ||
2833 | case MWIFIEX_BSS_MODE_IBSS: | ||
2834 | net = mwifiex_is_network_compatible(priv, i, | ||
2835 | mode); | ||
2836 | break; | ||
2837 | default: | ||
2838 | net = i; | ||
2839 | break; | ||
2840 | } | ||
2841 | } | ||
2842 | } | ||
2843 | |||
2844 | return net; | ||
2845 | } | ||
2846 | |||
2847 | /* | ||
2848 | * This function inserts scan command node to the scan pending queue. | ||
2849 | */ | ||
2850 | void | ||
2851 | mwifiex_queue_scan_cmd(struct mwifiex_private *priv, | ||
2852 | struct cmd_ctrl_node *cmd_node) | ||
2853 | { | ||
2854 | struct mwifiex_adapter *adapter = priv->adapter; | ||
2855 | unsigned long flags; | ||
2856 | |||
2857 | spin_lock_irqsave(&adapter->scan_pending_q_lock, flags); | ||
2858 | list_add_tail(&cmd_node->list, &adapter->scan_pending_q); | ||
2859 | spin_unlock_irqrestore(&adapter->scan_pending_q_lock, flags); | ||
2860 | } | ||
2861 | |||
2862 | /* | ||
2863 | * This function finds an AP with specific ssid in the scan list. | ||
2864 | */ | ||
2865 | int mwifiex_find_best_network(struct mwifiex_private *priv, | ||
2866 | struct mwifiex_ssid_bssid *req_ssid_bssid) | ||
2867 | { | ||
2868 | struct mwifiex_adapter *adapter = priv->adapter; | ||
2869 | struct mwifiex_bssdescriptor *req_bss; | ||
2870 | s32 i; | ||
2871 | |||
2872 | memset(req_ssid_bssid, 0, sizeof(struct mwifiex_ssid_bssid)); | ||
2873 | |||
2874 | i = mwifiex_find_best_network_in_list(priv); | ||
2875 | |||
2876 | if (i >= 0) { | ||
2877 | req_bss = &adapter->scan_table[i]; | ||
2878 | memcpy(&req_ssid_bssid->ssid, &req_bss->ssid, | ||
2879 | sizeof(struct mwifiex_802_11_ssid)); | ||
2880 | memcpy((u8 *) &req_ssid_bssid->bssid, | ||
2881 | (u8 *) &req_bss->mac_address, ETH_ALEN); | ||
2882 | |||
2883 | /* Make sure we are in the right mode */ | ||
2884 | if (priv->bss_mode == MWIFIEX_BSS_MODE_AUTO) | ||
2885 | priv->bss_mode = req_bss->bss_mode; | ||
2886 | } | ||
2887 | |||
2888 | if (!req_ssid_bssid->ssid.ssid_len) | ||
2889 | return -1; | ||
2890 | |||
2891 | dev_dbg(adapter->dev, "info: Best network found = [%s], " | ||
2892 | "[%pM]\n", req_ssid_bssid->ssid.ssid, | ||
2893 | req_ssid_bssid->bssid); | ||
2894 | |||
2895 | return 0; | ||
2896 | } | ||
2897 | |||
2898 | /* | ||
2899 | * This function sends a scan command for all available channels to the | ||
2900 | * firmware, filtered on a specific SSID. | ||
2901 | */ | ||
2902 | static int mwifiex_scan_specific_ssid(struct mwifiex_private *priv, | ||
2903 | void *wait_buf, u16 action, | ||
2904 | struct mwifiex_802_11_ssid *req_ssid, | ||
2905 | struct mwifiex_scan_resp *scan_resp) | ||
2906 | { | ||
2907 | struct mwifiex_adapter *adapter = priv->adapter; | ||
2908 | int ret = 0; | ||
2909 | struct mwifiex_user_scan_cfg *scan_cfg; | ||
2910 | |||
2911 | if (!req_ssid) | ||
2912 | return -1; | ||
2913 | |||
2914 | if (action == HostCmd_ACT_GEN_GET) { | ||
2915 | if (scan_resp) { | ||
2916 | scan_resp->scan_table = | ||
2917 | (u8 *) &priv->curr_bss_params.bss_descriptor; | ||
2918 | scan_resp->num_in_scan_table = | ||
2919 | adapter->num_in_scan_table; | ||
2920 | } else { | ||
2921 | ret = -1; | ||
2922 | } | ||
2923 | return ret; | ||
2924 | } | ||
2925 | |||
2926 | if (adapter->scan_processing && action == HostCmd_ACT_GEN_SET) { | ||
2927 | dev_dbg(adapter->dev, "cmd: Scan already in process...\n"); | ||
2928 | return ret; | ||
2929 | } | ||
2930 | |||
2931 | if (priv->scan_block && action == HostCmd_ACT_GEN_SET) { | ||
2932 | dev_dbg(adapter->dev, | ||
2933 | "cmd: Scan is blocked during association...\n"); | ||
2934 | return ret; | ||
2935 | } | ||
2936 | |||
2937 | mwifiex_scan_delete_ssid_table_entry(priv, req_ssid); | ||
2938 | |||
2939 | scan_cfg = kzalloc(sizeof(struct mwifiex_user_scan_cfg), GFP_KERNEL); | ||
2940 | if (!scan_cfg) { | ||
2941 | dev_err(adapter->dev, "failed to alloc scan_cfg\n"); | ||
2942 | return -1; | ||
2943 | } | ||
2944 | |||
2945 | memcpy(scan_cfg->ssid_list[0].ssid, req_ssid->ssid, | ||
2946 | req_ssid->ssid_len); | ||
2947 | scan_cfg->keep_previous_scan = true; | ||
2948 | |||
2949 | ret = mwifiex_scan_networks(priv, wait_buf, action, scan_cfg, NULL); | ||
2950 | |||
2951 | kfree(scan_cfg); | ||
2952 | return ret; | ||
2953 | } | ||
2954 | |||
2955 | /* | ||
2956 | * Sends IOCTL request to start a scan. | ||
2957 | * | ||
2958 | * This function allocates the IOCTL request buffer, fills it | ||
2959 | * with requisite parameters and calls the IOCTL handler. | ||
2960 | * | ||
2961 | * Scan command can be issued for both normal scan and specific SSID | ||
2962 | * scan, depending upon whether an SSID is provided or not. | ||
2963 | */ | ||
2964 | int mwifiex_request_scan(struct mwifiex_private *priv, u8 wait_option, | ||
2965 | struct mwifiex_802_11_ssid *req_ssid) | ||
2966 | { | ||
2967 | int ret = 0; | ||
2968 | struct mwifiex_wait_queue *wait = NULL; | ||
2969 | int status = 0; | ||
2970 | |||
2971 | if (down_interruptible(&priv->async_sem)) { | ||
2972 | dev_err(priv->adapter->dev, "%s: acquire semaphore\n", | ||
2973 | __func__); | ||
2974 | return -1; | ||
2975 | } | ||
2976 | priv->scan_pending_on_block = true; | ||
2977 | |||
2978 | /* Allocate wait request buffer */ | ||
2979 | wait = mwifiex_alloc_fill_wait_queue(priv, wait_option); | ||
2980 | if (!wait) { | ||
2981 | ret = -1; | ||
2982 | goto done; | ||
2983 | } | ||
2984 | |||
2985 | if (req_ssid && req_ssid->ssid_len != 0) | ||
2986 | /* Specific SSID scan */ | ||
2987 | status = mwifiex_scan_specific_ssid(priv, wait, | ||
2988 | HostCmd_ACT_GEN_SET, | ||
2989 | req_ssid, NULL); | ||
2990 | else | ||
2991 | /* Normal scan */ | ||
2992 | status = mwifiex_scan_networks(priv, wait, HostCmd_ACT_GEN_SET, | ||
2993 | NULL, NULL); | ||
2994 | status = mwifiex_request_ioctl(priv, wait, status, wait_option); | ||
2995 | if (status == -1) | ||
2996 | ret = -1; | ||
2997 | done: | ||
2998 | if ((wait) && (status != -EINPROGRESS)) | ||
2999 | kfree(wait); | ||
3000 | if (ret == -1) { | ||
3001 | priv->scan_pending_on_block = false; | ||
3002 | up(&priv->async_sem); | ||
3003 | } | ||
3004 | return ret; | ||
3005 | } | ||
3006 | |||
3007 | /* | ||
3008 | * This function appends the vendor specific IE TLV to a buffer. | ||
3009 | */ | ||
3010 | int | ||
3011 | mwifiex_cmd_append_vsie_tlv(struct mwifiex_private *priv, | ||
3012 | u16 vsie_mask, u8 **buffer) | ||
3013 | { | ||
3014 | int id, ret_len = 0; | ||
3015 | struct mwifiex_ie_types_vendor_param_set *vs_param_set; | ||
3016 | |||
3017 | if (!buffer) | ||
3018 | return 0; | ||
3019 | if (!(*buffer)) | ||
3020 | return 0; | ||
3021 | |||
3022 | /* | ||
3023 | * Traverse through the saved vendor specific IE array and append | ||
3024 | * the selected(scan/assoc/adhoc) IE as TLV to the command | ||
3025 | */ | ||
3026 | for (id = 0; id < MWIFIEX_MAX_VSIE_NUM; id++) { | ||
3027 | if (priv->vs_ie[id].mask & vsie_mask) { | ||
3028 | vs_param_set = | ||
3029 | (struct mwifiex_ie_types_vendor_param_set *) | ||
3030 | *buffer; | ||
3031 | vs_param_set->header.type = | ||
3032 | cpu_to_le16(TLV_TYPE_PASSTHROUGH); | ||
3033 | vs_param_set->header.len = | ||
3034 | cpu_to_le16((((u16) priv->vs_ie[id].ie[1]) | ||
3035 | & 0x00FF) + 2); | ||
3036 | memcpy(vs_param_set->ie, priv->vs_ie[id].ie, | ||
3037 | le16_to_cpu(vs_param_set->header.len)); | ||
3038 | *buffer += le16_to_cpu(vs_param_set->header.len) + | ||
3039 | sizeof(struct mwifiex_ie_types_header); | ||
3040 | ret_len += le16_to_cpu(vs_param_set->header.len) + | ||
3041 | sizeof(struct mwifiex_ie_types_header); | ||
3042 | } | ||
3043 | } | ||
3044 | return ret_len; | ||
3045 | } | ||
3046 | |||
3047 | /* | ||
3048 | * This function saves a beacon buffer of the current BSS descriptor. | ||
3049 | * | ||
3050 | * The current beacon buffer is saved so that it can be restored in the | ||
3051 | * following cases that makes the beacon buffer not to contain the current | ||
3052 | * ssid's beacon buffer. | ||
3053 | * - The current ssid was not found somehow in the last scan. | ||
3054 | * - The current ssid was the last entry of the scan table and overloaded. | ||
3055 | */ | ||
3056 | void | ||
3057 | mwifiex_save_curr_bcn(struct mwifiex_private *priv) | ||
3058 | { | ||
3059 | struct mwifiex_bssdescriptor *curr_bss = | ||
3060 | &priv->curr_bss_params.bss_descriptor; | ||
3061 | |||
3062 | /* save the beacon buffer if it is not saved or updated */ | ||
3063 | if ((priv->curr_bcn_buf == NULL) || | ||
3064 | (priv->curr_bcn_size != curr_bss->beacon_buf_size) || | ||
3065 | (memcmp(priv->curr_bcn_buf, curr_bss->beacon_buf, | ||
3066 | curr_bss->beacon_buf_size))) { | ||
3067 | |||
3068 | kfree(priv->curr_bcn_buf); | ||
3069 | priv->curr_bcn_buf = NULL; | ||
3070 | |||
3071 | priv->curr_bcn_size = curr_bss->beacon_buf_size; | ||
3072 | if (!priv->curr_bcn_size) | ||
3073 | return; | ||
3074 | |||
3075 | priv->curr_bcn_buf = kzalloc(curr_bss->beacon_buf_size, | ||
3076 | GFP_KERNEL); | ||
3077 | if (!priv->curr_bcn_buf) { | ||
3078 | dev_err(priv->adapter->dev, | ||
3079 | "failed to alloc curr_bcn_buf\n"); | ||
3080 | } else { | ||
3081 | memcpy(priv->curr_bcn_buf, curr_bss->beacon_buf, | ||
3082 | curr_bss->beacon_buf_size); | ||
3083 | dev_dbg(priv->adapter->dev, | ||
3084 | "info: current beacon saved %d\n", | ||
3085 | priv->curr_bcn_size); | ||
3086 | } | ||
3087 | } | ||
3088 | } | ||
3089 | |||
3090 | /* | ||
3091 | * This function frees the current BSS descriptor beacon buffer. | ||
3092 | */ | ||
3093 | void | ||
3094 | mwifiex_free_curr_bcn(struct mwifiex_private *priv) | ||
3095 | { | ||
3096 | kfree(priv->curr_bcn_buf); | ||
3097 | priv->curr_bcn_buf = NULL; | ||
3098 | } | ||
diff --git a/drivers/net/wireless/mwifiex/sdio.c b/drivers/net/wireless/mwifiex/sdio.c new file mode 100644 index 000000000000..f21e5cd19839 --- /dev/null +++ b/drivers/net/wireless/mwifiex/sdio.c | |||
@@ -0,0 +1,1770 @@ | |||
1 | /* | ||
2 | * Marvell Wireless LAN device driver: SDIO specific handling | ||
3 | * | ||
4 | * Copyright (C) 2011, Marvell International Ltd. | ||
5 | * | ||
6 | * This software file (the "File") is distributed by Marvell International | ||
7 | * Ltd. under the terms of the GNU General Public License Version 2, June 1991 | ||
8 | * (the "License"). You may use, redistribute and/or modify this File in | ||
9 | * accordance with the terms and conditions of the License, a copy of which | ||
10 | * is available by writing to the Free Software Foundation, Inc., | ||
11 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the | ||
12 | * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. | ||
13 | * | ||
14 | * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE | ||
15 | * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE | ||
16 | * ARE EXPRESSLY DISCLAIMED. The License provides additional details about | ||
17 | * this warranty disclaimer. | ||
18 | */ | ||
19 | |||
20 | #include <linux/firmware.h> | ||
21 | |||
22 | #include "decl.h" | ||
23 | #include "ioctl.h" | ||
24 | #include "util.h" | ||
25 | #include "fw.h" | ||
26 | #include "main.h" | ||
27 | #include "wmm.h" | ||
28 | #include "11n.h" | ||
29 | #include "sdio.h" | ||
30 | |||
31 | |||
32 | #define SDIO_VERSION "1.0" | ||
33 | |||
34 | static struct mwifiex_if_ops sdio_ops; | ||
35 | |||
36 | static struct semaphore add_remove_card_sem; | ||
37 | |||
38 | /* | ||
39 | * SDIO probe. | ||
40 | * | ||
41 | * This function probes an mwifiex device and registers it. It allocates | ||
42 | * the card structure, enables SDIO function number and initiates the | ||
43 | * device registration and initialization procedure by adding a logical | ||
44 | * interface. | ||
45 | */ | ||
46 | static int | ||
47 | mwifiex_sdio_probe(struct sdio_func *func, const struct sdio_device_id *id) | ||
48 | { | ||
49 | int ret = 0; | ||
50 | struct sdio_mmc_card *card = NULL; | ||
51 | |||
52 | pr_debug("info: vendor=0x%4.04X device=0x%4.04X class=%d function=%d\n", | ||
53 | func->vendor, func->device, func->class, func->num); | ||
54 | |||
55 | card = kzalloc(sizeof(struct sdio_mmc_card), GFP_KERNEL); | ||
56 | if (!card) { | ||
57 | pr_err("%s: failed to alloc memory\n", __func__); | ||
58 | return -ENOMEM; | ||
59 | } | ||
60 | |||
61 | card->func = func; | ||
62 | |||
63 | func->card->quirks |= MMC_QUIRK_BLKSZ_FOR_BYTE_MODE; | ||
64 | |||
65 | sdio_claim_host(func); | ||
66 | ret = sdio_enable_func(func); | ||
67 | sdio_release_host(func); | ||
68 | |||
69 | if (ret) { | ||
70 | pr_err("%s: failed to enable function\n", __func__); | ||
71 | return -EIO; | ||
72 | } | ||
73 | |||
74 | if (mwifiex_add_card(card, &add_remove_card_sem, &sdio_ops)) { | ||
75 | pr_err("%s: add card failed\n", __func__); | ||
76 | kfree(card); | ||
77 | sdio_claim_host(func); | ||
78 | ret = sdio_disable_func(func); | ||
79 | sdio_release_host(func); | ||
80 | ret = -1; | ||
81 | } | ||
82 | |||
83 | return ret; | ||
84 | } | ||
85 | |||
86 | /* | ||
87 | * SDIO remove. | ||
88 | * | ||
89 | * This function removes the interface and frees up the card structure. | ||
90 | */ | ||
91 | static void | ||
92 | mwifiex_sdio_remove(struct sdio_func *func) | ||
93 | { | ||
94 | struct sdio_mmc_card *card; | ||
95 | |||
96 | pr_debug("info: SDIO func num=%d\n", func->num); | ||
97 | |||
98 | if (func) { | ||
99 | card = sdio_get_drvdata(func); | ||
100 | if (card) { | ||
101 | mwifiex_remove_card(card->adapter, | ||
102 | &add_remove_card_sem); | ||
103 | kfree(card); | ||
104 | } | ||
105 | } | ||
106 | } | ||
107 | |||
108 | /* | ||
109 | * SDIO suspend. | ||
110 | * | ||
111 | * Kernel needs to suspend all functions separately. Therefore all | ||
112 | * registered functions must have drivers with suspend and resume | ||
113 | * methods. Failing that the kernel simply removes the whole card. | ||
114 | * | ||
115 | * If already not suspended, this function allocates and sends a host | ||
116 | * sleep activate request to the firmware and turns off the traffic. | ||
117 | */ | ||
118 | static int mwifiex_sdio_suspend(struct device *dev) | ||
119 | { | ||
120 | struct sdio_func *func = dev_to_sdio_func(dev); | ||
121 | struct sdio_mmc_card *card; | ||
122 | struct mwifiex_adapter *adapter = NULL; | ||
123 | mmc_pm_flag_t pm_flag = 0; | ||
124 | int hs_actived = 0; | ||
125 | int i; | ||
126 | int ret = 0; | ||
127 | |||
128 | if (func) { | ||
129 | pm_flag = sdio_get_host_pm_caps(func); | ||
130 | pr_debug("cmd: %s: suspend: PM flag = 0x%x\n", | ||
131 | sdio_func_id(func), pm_flag); | ||
132 | if (!(pm_flag & MMC_PM_KEEP_POWER)) { | ||
133 | pr_err("%s: cannot remain alive while host is" | ||
134 | " suspended\n", sdio_func_id(func)); | ||
135 | return -ENOSYS; | ||
136 | } | ||
137 | |||
138 | card = sdio_get_drvdata(func); | ||
139 | if (!card || !card->adapter) { | ||
140 | pr_err("suspend: invalid card or adapter\n"); | ||
141 | return 0; | ||
142 | } | ||
143 | } else { | ||
144 | pr_err("suspend: sdio_func is not specified\n"); | ||
145 | return 0; | ||
146 | } | ||
147 | |||
148 | adapter = card->adapter; | ||
149 | |||
150 | /* Enable the Host Sleep */ | ||
151 | hs_actived = mwifiex_enable_hs(adapter); | ||
152 | if (hs_actived) { | ||
153 | pr_debug("cmd: suspend with MMC_PM_KEEP_POWER\n"); | ||
154 | ret = sdio_set_host_pm_flags(func, MMC_PM_KEEP_POWER); | ||
155 | } | ||
156 | |||
157 | /* Indicate device suspended */ | ||
158 | adapter->is_suspended = true; | ||
159 | |||
160 | for (i = 0; i < adapter->priv_num; i++) | ||
161 | netif_carrier_off(adapter->priv[i]->netdev); | ||
162 | |||
163 | return ret; | ||
164 | } | ||
165 | |||
166 | /* | ||
167 | * SDIO resume. | ||
168 | * | ||
169 | * Kernel needs to suspend all functions separately. Therefore all | ||
170 | * registered functions must have drivers with suspend and resume | ||
171 | * methods. Failing that the kernel simply removes the whole card. | ||
172 | * | ||
173 | * If already not resumed, this function turns on the traffic and | ||
174 | * sends a host sleep cancel request to the firmware. | ||
175 | */ | ||
176 | static int mwifiex_sdio_resume(struct device *dev) | ||
177 | { | ||
178 | struct sdio_func *func = dev_to_sdio_func(dev); | ||
179 | struct sdio_mmc_card *card; | ||
180 | struct mwifiex_adapter *adapter = NULL; | ||
181 | mmc_pm_flag_t pm_flag = 0; | ||
182 | int i; | ||
183 | |||
184 | if (func) { | ||
185 | pm_flag = sdio_get_host_pm_caps(func); | ||
186 | card = sdio_get_drvdata(func); | ||
187 | if (!card || !card->adapter) { | ||
188 | pr_err("resume: invalid card or adapter\n"); | ||
189 | return 0; | ||
190 | } | ||
191 | } else { | ||
192 | pr_err("resume: sdio_func is not specified\n"); | ||
193 | return 0; | ||
194 | } | ||
195 | |||
196 | adapter = card->adapter; | ||
197 | |||
198 | if (!adapter->is_suspended) { | ||
199 | dev_warn(adapter->dev, "device already resumed\n"); | ||
200 | return 0; | ||
201 | } | ||
202 | |||
203 | adapter->is_suspended = false; | ||
204 | |||
205 | for (i = 0; i < adapter->priv_num; i++) | ||
206 | if (adapter->priv[i]->media_connected) | ||
207 | netif_carrier_on(adapter->priv[i]->netdev); | ||
208 | |||
209 | /* Disable Host Sleep */ | ||
210 | mwifiex_cancel_hs(mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_STA), | ||
211 | MWIFIEX_NO_WAIT); | ||
212 | |||
213 | return 0; | ||
214 | } | ||
215 | |||
216 | /* Device ID for SD8787 */ | ||
217 | #define SDIO_DEVICE_ID_MARVELL_8787 (0x9119) | ||
218 | |||
219 | /* WLAN IDs */ | ||
220 | static const struct sdio_device_id mwifiex_ids[] = { | ||
221 | {SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8787)}, | ||
222 | {}, | ||
223 | }; | ||
224 | |||
225 | MODULE_DEVICE_TABLE(sdio, mwifiex_ids); | ||
226 | |||
227 | static const struct dev_pm_ops mwifiex_sdio_pm_ops = { | ||
228 | .suspend = mwifiex_sdio_suspend, | ||
229 | .resume = mwifiex_sdio_resume, | ||
230 | }; | ||
231 | |||
232 | static struct sdio_driver mwifiex_sdio = { | ||
233 | .name = "mwifiex_sdio", | ||
234 | .id_table = mwifiex_ids, | ||
235 | .probe = mwifiex_sdio_probe, | ||
236 | .remove = mwifiex_sdio_remove, | ||
237 | .drv = { | ||
238 | .owner = THIS_MODULE, | ||
239 | .pm = &mwifiex_sdio_pm_ops, | ||
240 | } | ||
241 | }; | ||
242 | |||
243 | /* | ||
244 | * This function writes data into SDIO card register. | ||
245 | */ | ||
246 | static int | ||
247 | mwifiex_write_reg(struct mwifiex_adapter *adapter, u32 reg, u32 data) | ||
248 | { | ||
249 | struct sdio_mmc_card *card = adapter->card; | ||
250 | int ret = -1; | ||
251 | |||
252 | sdio_claim_host(card->func); | ||
253 | sdio_writeb(card->func, (u8) data, reg, &ret); | ||
254 | sdio_release_host(card->func); | ||
255 | |||
256 | return ret; | ||
257 | } | ||
258 | |||
259 | /* | ||
260 | * This function reads data from SDIO card register. | ||
261 | */ | ||
262 | static int | ||
263 | mwifiex_read_reg(struct mwifiex_adapter *adapter, u32 reg, u32 *data) | ||
264 | { | ||
265 | struct sdio_mmc_card *card = adapter->card; | ||
266 | int ret = -1; | ||
267 | u8 val; | ||
268 | |||
269 | sdio_claim_host(card->func); | ||
270 | val = sdio_readb(card->func, reg, &ret); | ||
271 | sdio_release_host(card->func); | ||
272 | |||
273 | *data = val; | ||
274 | |||
275 | return ret; | ||
276 | } | ||
277 | |||
278 | /* | ||
279 | * This function writes multiple data into SDIO card memory. | ||
280 | * | ||
281 | * This does not work in suspended mode. | ||
282 | */ | ||
283 | static int | ||
284 | mwifiex_write_data_sync(struct mwifiex_adapter *adapter, | ||
285 | u8 *buffer, u32 pkt_len, u32 port, u32 timeout) | ||
286 | { | ||
287 | struct sdio_mmc_card *card = adapter->card; | ||
288 | int ret = -1; | ||
289 | u8 blk_mode = | ||
290 | (port & MWIFIEX_SDIO_BYTE_MODE_MASK) ? BYTE_MODE : BLOCK_MODE; | ||
291 | u32 blk_size = (blk_mode == BLOCK_MODE) ? MWIFIEX_SDIO_BLOCK_SIZE : 1; | ||
292 | u32 blk_cnt = | ||
293 | (blk_mode == | ||
294 | BLOCK_MODE) ? (pkt_len / | ||
295 | MWIFIEX_SDIO_BLOCK_SIZE) : pkt_len; | ||
296 | u32 ioport = (port & MWIFIEX_SDIO_IO_PORT_MASK); | ||
297 | |||
298 | if (adapter->is_suspended) { | ||
299 | dev_err(adapter->dev, | ||
300 | "%s: not allowed while suspended\n", __func__); | ||
301 | return -1; | ||
302 | } | ||
303 | |||
304 | sdio_claim_host(card->func); | ||
305 | |||
306 | if (!sdio_writesb(card->func, ioport, buffer, blk_cnt * blk_size)) | ||
307 | ret = 0; | ||
308 | |||
309 | sdio_release_host(card->func); | ||
310 | |||
311 | return ret; | ||
312 | } | ||
313 | |||
314 | /* | ||
315 | * This function reads multiple data from SDIO card memory. | ||
316 | */ | ||
317 | static int mwifiex_read_data_sync(struct mwifiex_adapter *adapter, | ||
318 | u8 *buffer, u32 len, | ||
319 | u32 port, u32 timeout, u8 claim) | ||
320 | { | ||
321 | struct sdio_mmc_card *card = adapter->card; | ||
322 | int ret = -1; | ||
323 | u8 blk_mode = | ||
324 | (port & MWIFIEX_SDIO_BYTE_MODE_MASK) ? BYTE_MODE : BLOCK_MODE; | ||
325 | u32 blk_size = (blk_mode == BLOCK_MODE) ? MWIFIEX_SDIO_BLOCK_SIZE : 1; | ||
326 | u32 blk_cnt = | ||
327 | (blk_mode == | ||
328 | BLOCK_MODE) ? (len / MWIFIEX_SDIO_BLOCK_SIZE) : len; | ||
329 | u32 ioport = (port & MWIFIEX_SDIO_IO_PORT_MASK); | ||
330 | |||
331 | if (claim) | ||
332 | sdio_claim_host(card->func); | ||
333 | |||
334 | if (!sdio_readsb(card->func, buffer, ioport, blk_cnt * blk_size)) | ||
335 | ret = 0; | ||
336 | |||
337 | if (claim) | ||
338 | sdio_release_host(card->func); | ||
339 | |||
340 | return ret; | ||
341 | } | ||
342 | |||
343 | /* | ||
344 | * This function wakes up the card. | ||
345 | * | ||
346 | * A host power up command is written to the card configuration | ||
347 | * register to wake up the card. | ||
348 | */ | ||
349 | static int mwifiex_pm_wakeup_card(struct mwifiex_adapter *adapter) | ||
350 | { | ||
351 | int ret; | ||
352 | |||
353 | dev_dbg(adapter->dev, "event: wakeup device...\n"); | ||
354 | ret = mwifiex_write_reg(adapter, CONFIGURATION_REG, HOST_POWER_UP); | ||
355 | |||
356 | return ret; | ||
357 | } | ||
358 | |||
359 | /* | ||
360 | * This function is called after the card has woken up. | ||
361 | * | ||
362 | * The card configuration register is reset. | ||
363 | */ | ||
364 | static int mwifiex_pm_wakeup_card_complete(struct mwifiex_adapter *adapter) | ||
365 | { | ||
366 | int ret; | ||
367 | |||
368 | dev_dbg(adapter->dev, "cmd: wakeup device completed\n"); | ||
369 | ret = mwifiex_write_reg(adapter, CONFIGURATION_REG, 0); | ||
370 | |||
371 | return ret; | ||
372 | } | ||
373 | |||
374 | /* | ||
375 | * This function initializes the IO ports. | ||
376 | * | ||
377 | * The following operations are performed - | ||
378 | * - Read the IO ports (0, 1 and 2) | ||
379 | * - Set host interrupt Reset-To-Read to clear | ||
380 | * - Set auto re-enable interrupt | ||
381 | */ | ||
382 | static int mwifiex_init_sdio_ioport(struct mwifiex_adapter *adapter) | ||
383 | { | ||
384 | u32 reg; | ||
385 | |||
386 | adapter->ioport = 0; | ||
387 | |||
388 | /* Read the IO port */ | ||
389 | if (!mwifiex_read_reg(adapter, IO_PORT_0_REG, ®)) | ||
390 | adapter->ioport |= (reg & 0xff); | ||
391 | else | ||
392 | return -1; | ||
393 | |||
394 | if (!mwifiex_read_reg(adapter, IO_PORT_1_REG, ®)) | ||
395 | adapter->ioport |= ((reg & 0xff) << 8); | ||
396 | else | ||
397 | return -1; | ||
398 | |||
399 | if (!mwifiex_read_reg(adapter, IO_PORT_2_REG, ®)) | ||
400 | adapter->ioport |= ((reg & 0xff) << 16); | ||
401 | else | ||
402 | return -1; | ||
403 | |||
404 | pr_debug("info: SDIO FUNC1 IO port: %#x\n", adapter->ioport); | ||
405 | |||
406 | /* Set Host interrupt reset to read to clear */ | ||
407 | if (!mwifiex_read_reg(adapter, HOST_INT_RSR_REG, ®)) | ||
408 | mwifiex_write_reg(adapter, HOST_INT_RSR_REG, | ||
409 | reg | SDIO_INT_MASK); | ||
410 | else | ||
411 | return -1; | ||
412 | |||
413 | /* Dnld/Upld ready set to auto reset */ | ||
414 | if (!mwifiex_read_reg(adapter, CARD_MISC_CFG_REG, ®)) | ||
415 | mwifiex_write_reg(adapter, CARD_MISC_CFG_REG, | ||
416 | reg | AUTO_RE_ENABLE_INT); | ||
417 | else | ||
418 | return -1; | ||
419 | |||
420 | return 0; | ||
421 | } | ||
422 | |||
423 | /* | ||
424 | * This function sends data to the card. | ||
425 | */ | ||
426 | static int mwifiex_write_data_to_card(struct mwifiex_adapter *adapter, | ||
427 | u8 *payload, u32 pkt_len, u32 port) | ||
428 | { | ||
429 | u32 i = 0; | ||
430 | int ret = 0; | ||
431 | |||
432 | do { | ||
433 | ret = mwifiex_write_data_sync(adapter, payload, pkt_len, | ||
434 | port, 0); | ||
435 | if (ret) { | ||
436 | i++; | ||
437 | dev_err(adapter->dev, "host_to_card, write iomem" | ||
438 | " (%d) failed: %d\n", i, ret); | ||
439 | if (mwifiex_write_reg(adapter, | ||
440 | CONFIGURATION_REG, 0x04)) | ||
441 | dev_err(adapter->dev, "write CFG reg failed\n"); | ||
442 | |||
443 | ret = -1; | ||
444 | if (i > MAX_WRITE_IOMEM_RETRY) | ||
445 | return ret; | ||
446 | } | ||
447 | } while (ret == -1); | ||
448 | |||
449 | return ret; | ||
450 | } | ||
451 | |||
452 | /* | ||
453 | * This function gets the read port. | ||
454 | * | ||
455 | * If control port bit is set in MP read bitmap, the control port | ||
456 | * is returned, otherwise the current read port is returned and | ||
457 | * the value is increased (provided it does not reach the maximum | ||
458 | * limit, in which case it is reset to 1) | ||
459 | */ | ||
460 | static int mwifiex_get_rd_port(struct mwifiex_adapter *adapter, u8 *port) | ||
461 | { | ||
462 | struct sdio_mmc_card *card = adapter->card; | ||
463 | u16 rd_bitmap = card->mp_rd_bitmap; | ||
464 | |||
465 | dev_dbg(adapter->dev, "data: mp_rd_bitmap=0x%04x\n", rd_bitmap); | ||
466 | |||
467 | if (!(rd_bitmap & (CTRL_PORT_MASK | DATA_PORT_MASK))) | ||
468 | return -1; | ||
469 | |||
470 | if (card->mp_rd_bitmap & CTRL_PORT_MASK) { | ||
471 | card->mp_rd_bitmap &= (u16) (~CTRL_PORT_MASK); | ||
472 | *port = CTRL_PORT; | ||
473 | dev_dbg(adapter->dev, "data: port=%d mp_rd_bitmap=0x%04x\n", | ||
474 | *port, card->mp_rd_bitmap); | ||
475 | } else { | ||
476 | if (card->mp_rd_bitmap & (1 << card->curr_rd_port)) { | ||
477 | card->mp_rd_bitmap &= | ||
478 | (u16) (~(1 << card->curr_rd_port)); | ||
479 | *port = card->curr_rd_port; | ||
480 | |||
481 | if (++card->curr_rd_port == MAX_PORT) | ||
482 | card->curr_rd_port = 1; | ||
483 | } else { | ||
484 | return -1; | ||
485 | } | ||
486 | |||
487 | dev_dbg(adapter->dev, | ||
488 | "data: port=%d mp_rd_bitmap=0x%04x -> 0x%04x\n", | ||
489 | *port, rd_bitmap, card->mp_rd_bitmap); | ||
490 | } | ||
491 | return 0; | ||
492 | } | ||
493 | |||
494 | /* | ||
495 | * This function gets the write port for data. | ||
496 | * | ||
497 | * The current write port is returned if available and the value is | ||
498 | * increased (provided it does not reach the maximum limit, in which | ||
499 | * case it is reset to 1) | ||
500 | */ | ||
501 | static int mwifiex_get_wr_port_data(struct mwifiex_adapter *adapter, u8 *port) | ||
502 | { | ||
503 | struct sdio_mmc_card *card = adapter->card; | ||
504 | u16 wr_bitmap = card->mp_wr_bitmap; | ||
505 | |||
506 | dev_dbg(adapter->dev, "data: mp_wr_bitmap=0x%04x\n", wr_bitmap); | ||
507 | |||
508 | if (!(wr_bitmap & card->mp_data_port_mask)) | ||
509 | return -1; | ||
510 | |||
511 | if (card->mp_wr_bitmap & (1 << card->curr_wr_port)) { | ||
512 | card->mp_wr_bitmap &= (u16) (~(1 << card->curr_wr_port)); | ||
513 | *port = card->curr_wr_port; | ||
514 | if (++card->curr_wr_port == card->mp_end_port) | ||
515 | card->curr_wr_port = 1; | ||
516 | } else { | ||
517 | adapter->data_sent = true; | ||
518 | return -EBUSY; | ||
519 | } | ||
520 | |||
521 | if (*port == CTRL_PORT) { | ||
522 | dev_err(adapter->dev, "invalid data port=%d cur port=%d" | ||
523 | " mp_wr_bitmap=0x%04x -> 0x%04x\n", | ||
524 | *port, card->curr_wr_port, wr_bitmap, | ||
525 | card->mp_wr_bitmap); | ||
526 | return -1; | ||
527 | } | ||
528 | |||
529 | dev_dbg(adapter->dev, "data: port=%d mp_wr_bitmap=0x%04x -> 0x%04x\n", | ||
530 | *port, wr_bitmap, card->mp_wr_bitmap); | ||
531 | |||
532 | return 0; | ||
533 | } | ||
534 | |||
535 | /* | ||
536 | * This function polls the card status. | ||
537 | */ | ||
538 | static int | ||
539 | mwifiex_sdio_poll_card_status(struct mwifiex_adapter *adapter, u8 bits) | ||
540 | { | ||
541 | u32 tries; | ||
542 | u32 cs = 0; | ||
543 | |||
544 | for (tries = 0; tries < MAX_POLL_TRIES; tries++) { | ||
545 | if (mwifiex_read_reg(adapter, CARD_STATUS_REG, &cs)) | ||
546 | break; | ||
547 | else if ((cs & bits) == bits) | ||
548 | return 0; | ||
549 | |||
550 | udelay(10); | ||
551 | } | ||
552 | |||
553 | dev_err(adapter->dev, "poll card status failed, tries = %d\n", | ||
554 | tries); | ||
555 | return -1; | ||
556 | } | ||
557 | |||
558 | /* | ||
559 | * This function reads the firmware status. | ||
560 | */ | ||
561 | static int | ||
562 | mwifiex_sdio_read_fw_status(struct mwifiex_adapter *adapter, u16 *dat) | ||
563 | { | ||
564 | u32 fws0 = 0, fws1 = 0; | ||
565 | |||
566 | if (mwifiex_read_reg(adapter, CARD_FW_STATUS0_REG, &fws0)) | ||
567 | return -1; | ||
568 | |||
569 | if (mwifiex_read_reg(adapter, CARD_FW_STATUS1_REG, &fws1)) | ||
570 | return -1; | ||
571 | |||
572 | *dat = (u16) ((fws1 << 8) | fws0); | ||
573 | |||
574 | return 0; | ||
575 | } | ||
576 | |||
577 | /* | ||
578 | * This function disables the host interrupt. | ||
579 | * | ||
580 | * The host interrupt mask is read, the disable bit is reset and | ||
581 | * written back to the card host interrupt mask register. | ||
582 | */ | ||
583 | static int mwifiex_sdio_disable_host_int(struct mwifiex_adapter *adapter) | ||
584 | { | ||
585 | u32 host_int_mask = 0; | ||
586 | |||
587 | /* Read back the host_int_mask register */ | ||
588 | if (mwifiex_read_reg(adapter, HOST_INT_MASK_REG, &host_int_mask)) | ||
589 | return -1; | ||
590 | |||
591 | /* Update with the mask and write back to the register */ | ||
592 | host_int_mask &= ~HOST_INT_DISABLE; | ||
593 | |||
594 | if (mwifiex_write_reg(adapter, HOST_INT_MASK_REG, host_int_mask)) { | ||
595 | dev_err(adapter->dev, "disable host interrupt failed\n"); | ||
596 | return -1; | ||
597 | } | ||
598 | |||
599 | return 0; | ||
600 | } | ||
601 | |||
602 | /* | ||
603 | * This function enables the host interrupt. | ||
604 | * | ||
605 | * The host interrupt enable mask is written to the card | ||
606 | * host interrupt mask register. | ||
607 | */ | ||
608 | static int mwifiex_sdio_enable_host_int(struct mwifiex_adapter *adapter) | ||
609 | { | ||
610 | /* Simply write the mask to the register */ | ||
611 | if (mwifiex_write_reg(adapter, HOST_INT_MASK_REG, HOST_INT_ENABLE)) { | ||
612 | dev_err(adapter->dev, "enable host interrupt failed\n"); | ||
613 | return -1; | ||
614 | } | ||
615 | return 0; | ||
616 | } | ||
617 | |||
618 | /* | ||
619 | * This function sends a data buffer to the card. | ||
620 | */ | ||
621 | static int mwifiex_sdio_card_to_host(struct mwifiex_adapter *adapter, | ||
622 | u32 *type, u8 *buffer, | ||
623 | u32 npayload, u32 ioport) | ||
624 | { | ||
625 | int ret = 0; | ||
626 | u32 nb; | ||
627 | |||
628 | if (!buffer) { | ||
629 | dev_err(adapter->dev, "%s: buffer is NULL\n", __func__); | ||
630 | return -1; | ||
631 | } | ||
632 | |||
633 | ret = mwifiex_read_data_sync(adapter, buffer, npayload, ioport, 0, 1); | ||
634 | |||
635 | if (ret) { | ||
636 | dev_err(adapter->dev, "%s: read iomem failed: %d\n", __func__, | ||
637 | ret); | ||
638 | return -1; | ||
639 | } | ||
640 | |||
641 | nb = le16_to_cpu(*(__le16 *) (buffer)); | ||
642 | if (nb > npayload) { | ||
643 | dev_err(adapter->dev, "%s: invalid packet, nb=%d, npayload=%d\n", | ||
644 | __func__, nb, npayload); | ||
645 | return -1; | ||
646 | } | ||
647 | |||
648 | *type = le16_to_cpu(*(__le16 *) (buffer + 2)); | ||
649 | |||
650 | return ret; | ||
651 | } | ||
652 | |||
653 | /* | ||
654 | * This function downloads the firmware to the card. | ||
655 | * | ||
656 | * Firmware is downloaded to the card in blocks. Every block download | ||
657 | * is tested for CRC errors, and retried a number of times before | ||
658 | * returning failure. | ||
659 | */ | ||
660 | static int mwifiex_prog_fw_w_helper(struct mwifiex_adapter *adapter, | ||
661 | struct mwifiex_fw_image *fw) | ||
662 | { | ||
663 | int ret = 0; | ||
664 | u8 *firmware = fw->fw_buf; | ||
665 | u32 firmware_len = fw->fw_len; | ||
666 | u32 offset = 0; | ||
667 | u32 base0, base1; | ||
668 | u8 *fwbuf; | ||
669 | u16 len = 0; | ||
670 | u32 txlen = 0, tx_blocks = 0, tries = 0; | ||
671 | u32 i = 0; | ||
672 | |||
673 | if (!firmware_len) { | ||
674 | dev_err(adapter->dev, "firmware image not found!" | ||
675 | " Terminating download\n"); | ||
676 | return -1; | ||
677 | } | ||
678 | |||
679 | dev_dbg(adapter->dev, "info: downloading FW image (%d bytes)\n", | ||
680 | firmware_len); | ||
681 | |||
682 | /* Assume that the allocated buffer is 8-byte aligned */ | ||
683 | fwbuf = kzalloc(MWIFIEX_UPLD_SIZE, GFP_KERNEL); | ||
684 | if (!fwbuf) { | ||
685 | dev_err(adapter->dev, "unable to alloc buffer for firmware." | ||
686 | " Terminating download\n"); | ||
687 | return -1; | ||
688 | } | ||
689 | |||
690 | /* Perform firmware data transfer */ | ||
691 | do { | ||
692 | /* The host polls for the DN_LD_CARD_RDY and CARD_IO_READY | ||
693 | bits */ | ||
694 | ret = mwifiex_sdio_poll_card_status(adapter, CARD_IO_READY | | ||
695 | DN_LD_CARD_RDY); | ||
696 | if (ret) { | ||
697 | dev_err(adapter->dev, "FW download with helper:" | ||
698 | " poll status timeout @ %d\n", offset); | ||
699 | goto done; | ||
700 | } | ||
701 | |||
702 | /* More data? */ | ||
703 | if (offset >= firmware_len) | ||
704 | break; | ||
705 | |||
706 | for (tries = 0; tries < MAX_POLL_TRIES; tries++) { | ||
707 | ret = mwifiex_read_reg(adapter, HOST_F1_RD_BASE_0, | ||
708 | &base0); | ||
709 | if (ret) { | ||
710 | dev_err(adapter->dev, "dev BASE0 register read" | ||
711 | " failed: base0=0x%04X(%d). Terminating " | ||
712 | "download\n", base0, base0); | ||
713 | goto done; | ||
714 | } | ||
715 | ret = mwifiex_read_reg(adapter, HOST_F1_RD_BASE_1, | ||
716 | &base1); | ||
717 | if (ret) { | ||
718 | dev_err(adapter->dev, "dev BASE1 register read" | ||
719 | " failed: base1=0x%04X(%d). Terminating " | ||
720 | "download\n", base1, base1); | ||
721 | goto done; | ||
722 | } | ||
723 | len = (u16) (((base1 & 0xff) << 8) | (base0 & 0xff)); | ||
724 | |||
725 | if (len) | ||
726 | break; | ||
727 | |||
728 | udelay(10); | ||
729 | } | ||
730 | |||
731 | if (!len) { | ||
732 | break; | ||
733 | } else if (len > MWIFIEX_UPLD_SIZE) { | ||
734 | dev_err(adapter->dev, "FW download failed @ %d," | ||
735 | " invalid length %d\n", offset, len); | ||
736 | ret = -1; | ||
737 | goto done; | ||
738 | } | ||
739 | |||
740 | txlen = len; | ||
741 | |||
742 | if (len & BIT(0)) { | ||
743 | i++; | ||
744 | if (i > MAX_WRITE_IOMEM_RETRY) { | ||
745 | dev_err(adapter->dev, "FW download failed @" | ||
746 | " %d, over max retry count\n", offset); | ||
747 | ret = -1; | ||
748 | goto done; | ||
749 | } | ||
750 | dev_err(adapter->dev, "CRC indicated by the helper:" | ||
751 | " len = 0x%04X, txlen = %d\n", len, txlen); | ||
752 | len &= ~BIT(0); | ||
753 | /* Setting this to 0 to resend from same offset */ | ||
754 | txlen = 0; | ||
755 | } else { | ||
756 | i = 0; | ||
757 | |||
758 | /* Set blocksize to transfer - checking for last | ||
759 | block */ | ||
760 | if (firmware_len - offset < txlen) | ||
761 | txlen = firmware_len - offset; | ||
762 | |||
763 | tx_blocks = (txlen + MWIFIEX_SDIO_BLOCK_SIZE - | ||
764 | 1) / MWIFIEX_SDIO_BLOCK_SIZE; | ||
765 | |||
766 | /* Copy payload to buffer */ | ||
767 | memmove(fwbuf, &firmware[offset], txlen); | ||
768 | } | ||
769 | |||
770 | ret = mwifiex_write_data_sync(adapter, fwbuf, tx_blocks * | ||
771 | MWIFIEX_SDIO_BLOCK_SIZE, | ||
772 | adapter->ioport, 0); | ||
773 | if (ret) { | ||
774 | dev_err(adapter->dev, "FW download, write iomem (%d)" | ||
775 | " failed @ %d\n", i, offset); | ||
776 | if (mwifiex_write_reg(adapter, CONFIGURATION_REG, 0x04)) | ||
777 | dev_err(adapter->dev, "write CFG reg failed\n"); | ||
778 | |||
779 | ret = -1; | ||
780 | goto done; | ||
781 | } | ||
782 | |||
783 | offset += txlen; | ||
784 | } while (true); | ||
785 | |||
786 | dev_dbg(adapter->dev, "info: FW download over, size %d bytes\n", | ||
787 | offset); | ||
788 | |||
789 | ret = 0; | ||
790 | done: | ||
791 | kfree(fwbuf); | ||
792 | return ret; | ||
793 | } | ||
794 | |||
795 | /* | ||
796 | * This function checks the firmware status in card. | ||
797 | * | ||
798 | * The winner interface is also determined by this function. | ||
799 | */ | ||
800 | static int mwifiex_check_fw_status(struct mwifiex_adapter *adapter, | ||
801 | u32 poll_num, int *winner) | ||
802 | { | ||
803 | int ret = 0; | ||
804 | u16 firmware_stat; | ||
805 | u32 tries; | ||
806 | u32 winner_status; | ||
807 | |||
808 | /* Wait for firmware initialization event */ | ||
809 | for (tries = 0; tries < poll_num; tries++) { | ||
810 | ret = mwifiex_sdio_read_fw_status(adapter, &firmware_stat); | ||
811 | if (ret) | ||
812 | continue; | ||
813 | if (firmware_stat == FIRMWARE_READY) { | ||
814 | ret = 0; | ||
815 | break; | ||
816 | } else { | ||
817 | mdelay(100); | ||
818 | ret = -1; | ||
819 | } | ||
820 | } | ||
821 | |||
822 | if (winner && ret) { | ||
823 | if (mwifiex_read_reg | ||
824 | (adapter, CARD_FW_STATUS0_REG, &winner_status)) | ||
825 | winner_status = 0; | ||
826 | |||
827 | if (winner_status) | ||
828 | *winner = 0; | ||
829 | else | ||
830 | *winner = 1; | ||
831 | } | ||
832 | return ret; | ||
833 | } | ||
834 | |||
835 | /* | ||
836 | * This function reads the interrupt status from card. | ||
837 | */ | ||
838 | static void mwifiex_interrupt_status(struct mwifiex_adapter *adapter) | ||
839 | { | ||
840 | struct sdio_mmc_card *card = adapter->card; | ||
841 | u32 sdio_ireg = 0; | ||
842 | unsigned long flags; | ||
843 | |||
844 | if (mwifiex_read_data_sync(adapter, card->mp_regs, MAX_MP_REGS, | ||
845 | REG_PORT | MWIFIEX_SDIO_BYTE_MODE_MASK, 0, | ||
846 | 0)) { | ||
847 | dev_err(adapter->dev, "read mp_regs failed\n"); | ||
848 | return; | ||
849 | } | ||
850 | |||
851 | sdio_ireg = card->mp_regs[HOST_INTSTATUS_REG]; | ||
852 | if (sdio_ireg) { | ||
853 | /* | ||
854 | * DN_LD_HOST_INT_STATUS and/or UP_LD_HOST_INT_STATUS | ||
855 | * Clear the interrupt status register | ||
856 | */ | ||
857 | dev_dbg(adapter->dev, "int: sdio_ireg = %#x\n", sdio_ireg); | ||
858 | spin_lock_irqsave(&adapter->int_lock, flags); | ||
859 | adapter->int_status |= sdio_ireg; | ||
860 | spin_unlock_irqrestore(&adapter->int_lock, flags); | ||
861 | } | ||
862 | |||
863 | return; | ||
864 | } | ||
865 | |||
866 | /* | ||
867 | * SDIO interrupt handler. | ||
868 | * | ||
869 | * This function reads the interrupt status from firmware and assigns | ||
870 | * the main process in workqueue which will handle the interrupt. | ||
871 | */ | ||
872 | static void | ||
873 | mwifiex_sdio_interrupt(struct sdio_func *func) | ||
874 | { | ||
875 | struct mwifiex_adapter *adapter; | ||
876 | struct sdio_mmc_card *card; | ||
877 | |||
878 | card = sdio_get_drvdata(func); | ||
879 | if (!card || !card->adapter) { | ||
880 | pr_debug("int: func=%p card=%p adapter=%p\n", | ||
881 | func, card, card ? card->adapter : NULL); | ||
882 | return; | ||
883 | } | ||
884 | adapter = card->adapter; | ||
885 | |||
886 | if (adapter->surprise_removed) | ||
887 | return; | ||
888 | |||
889 | if (!adapter->pps_uapsd_mode && adapter->ps_state == PS_STATE_SLEEP) | ||
890 | adapter->ps_state = PS_STATE_AWAKE; | ||
891 | |||
892 | mwifiex_interrupt_status(adapter); | ||
893 | queue_work(adapter->workqueue, &adapter->main_work); | ||
894 | |||
895 | return; | ||
896 | } | ||
897 | |||
898 | /* | ||
899 | * This function decodes a received packet. | ||
900 | * | ||
901 | * Based on the type, the packet is treated as either a data, or | ||
902 | * a command response, or an event, and the correct handler | ||
903 | * function is invoked. | ||
904 | */ | ||
905 | static int mwifiex_decode_rx_packet(struct mwifiex_adapter *adapter, | ||
906 | struct sk_buff *skb, u32 upld_typ) | ||
907 | { | ||
908 | u8 *cmd_buf; | ||
909 | |||
910 | skb_pull(skb, INTF_HEADER_LEN); | ||
911 | |||
912 | switch (upld_typ) { | ||
913 | case MWIFIEX_TYPE_DATA: | ||
914 | dev_dbg(adapter->dev, "info: --- Rx: Data packet ---\n"); | ||
915 | mwifiex_handle_rx_packet(adapter, skb); | ||
916 | break; | ||
917 | |||
918 | case MWIFIEX_TYPE_CMD: | ||
919 | dev_dbg(adapter->dev, "info: --- Rx: Cmd Response ---\n"); | ||
920 | /* take care of curr_cmd = NULL case */ | ||
921 | if (!adapter->curr_cmd) { | ||
922 | cmd_buf = adapter->upld_buf; | ||
923 | |||
924 | if (adapter->ps_state == PS_STATE_SLEEP_CFM) | ||
925 | mwifiex_process_sleep_confirm_resp(adapter, | ||
926 | skb->data, skb->len); | ||
927 | |||
928 | memcpy(cmd_buf, skb->data, min_t(u32, | ||
929 | MWIFIEX_SIZE_OF_CMD_BUFFER, skb->len)); | ||
930 | |||
931 | dev_kfree_skb_any(skb); | ||
932 | } else { | ||
933 | adapter->cmd_resp_received = true; | ||
934 | adapter->curr_cmd->resp_skb = skb; | ||
935 | } | ||
936 | break; | ||
937 | |||
938 | case MWIFIEX_TYPE_EVENT: | ||
939 | dev_dbg(adapter->dev, "info: --- Rx: Event ---\n"); | ||
940 | adapter->event_cause = *(u32 *) skb->data; | ||
941 | |||
942 | skb_pull(skb, MWIFIEX_EVENT_HEADER_LEN); | ||
943 | |||
944 | if ((skb->len > 0) && (skb->len < MAX_EVENT_SIZE)) | ||
945 | memcpy(adapter->event_body, skb->data, skb->len); | ||
946 | |||
947 | /* event cause has been saved to adapter->event_cause */ | ||
948 | adapter->event_received = true; | ||
949 | adapter->event_skb = skb; | ||
950 | |||
951 | break; | ||
952 | |||
953 | default: | ||
954 | dev_err(adapter->dev, "unknown upload type %#x\n", upld_typ); | ||
955 | dev_kfree_skb_any(skb); | ||
956 | break; | ||
957 | } | ||
958 | |||
959 | return 0; | ||
960 | } | ||
961 | |||
962 | /* | ||
963 | * This function transfers received packets from card to driver, performing | ||
964 | * aggregation if required. | ||
965 | * | ||
966 | * For data received on control port, or if aggregation is disabled, the | ||
967 | * received buffers are uploaded as separate packets. However, if aggregation | ||
968 | * is enabled and required, the buffers are copied onto an aggregation buffer, | ||
969 | * provided there is space left, processed and finally uploaded. | ||
970 | */ | ||
971 | static int mwifiex_sdio_card_to_host_mp_aggr(struct mwifiex_adapter *adapter, | ||
972 | struct sk_buff *skb, u8 port) | ||
973 | { | ||
974 | struct sdio_mmc_card *card = adapter->card; | ||
975 | s32 f_do_rx_aggr = 0; | ||
976 | s32 f_do_rx_cur = 0; | ||
977 | s32 f_aggr_cur = 0; | ||
978 | struct sk_buff *skb_deaggr; | ||
979 | u32 pind = 0; | ||
980 | u32 pkt_len, pkt_type = 0; | ||
981 | u8 *curr_ptr; | ||
982 | u32 rx_len = skb->len; | ||
983 | |||
984 | if (port == CTRL_PORT) { | ||
985 | /* Read the command Resp without aggr */ | ||
986 | dev_dbg(adapter->dev, "info: %s: no aggregation for cmd " | ||
987 | "response\n", __func__); | ||
988 | |||
989 | f_do_rx_cur = 1; | ||
990 | goto rx_curr_single; | ||
991 | } | ||
992 | |||
993 | if (!card->mpa_rx.enabled) { | ||
994 | dev_dbg(adapter->dev, "info: %s: rx aggregation disabled\n", | ||
995 | __func__); | ||
996 | |||
997 | f_do_rx_cur = 1; | ||
998 | goto rx_curr_single; | ||
999 | } | ||
1000 | |||
1001 | if (card->mp_rd_bitmap & (~((u16) CTRL_PORT_MASK))) { | ||
1002 | /* Some more data RX pending */ | ||
1003 | dev_dbg(adapter->dev, "info: %s: not last packet\n", __func__); | ||
1004 | |||
1005 | if (MP_RX_AGGR_IN_PROGRESS(card)) { | ||
1006 | if (MP_RX_AGGR_BUF_HAS_ROOM(card, skb->len)) { | ||
1007 | f_aggr_cur = 1; | ||
1008 | } else { | ||
1009 | /* No room in Aggr buf, do rx aggr now */ | ||
1010 | f_do_rx_aggr = 1; | ||
1011 | f_do_rx_cur = 1; | ||
1012 | } | ||
1013 | } else { | ||
1014 | /* Rx aggr not in progress */ | ||
1015 | f_aggr_cur = 1; | ||
1016 | } | ||
1017 | |||
1018 | } else { | ||
1019 | /* No more data RX pending */ | ||
1020 | dev_dbg(adapter->dev, "info: %s: last packet\n", __func__); | ||
1021 | |||
1022 | if (MP_RX_AGGR_IN_PROGRESS(card)) { | ||
1023 | f_do_rx_aggr = 1; | ||
1024 | if (MP_RX_AGGR_BUF_HAS_ROOM(card, skb->len)) | ||
1025 | f_aggr_cur = 1; | ||
1026 | else | ||
1027 | /* No room in Aggr buf, do rx aggr now */ | ||
1028 | f_do_rx_cur = 1; | ||
1029 | } else { | ||
1030 | f_do_rx_cur = 1; | ||
1031 | } | ||
1032 | } | ||
1033 | |||
1034 | if (f_aggr_cur) { | ||
1035 | dev_dbg(adapter->dev, "info: current packet aggregation\n"); | ||
1036 | /* Curr pkt can be aggregated */ | ||
1037 | MP_RX_AGGR_SETUP(card, skb, port); | ||
1038 | |||
1039 | if (MP_RX_AGGR_PKT_LIMIT_REACHED(card) || | ||
1040 | MP_RX_AGGR_PORT_LIMIT_REACHED(card)) { | ||
1041 | dev_dbg(adapter->dev, "info: %s: aggregated packet " | ||
1042 | "limit reached\n", __func__); | ||
1043 | /* No more pkts allowed in Aggr buf, rx it */ | ||
1044 | f_do_rx_aggr = 1; | ||
1045 | } | ||
1046 | } | ||
1047 | |||
1048 | if (f_do_rx_aggr) { | ||
1049 | /* do aggr RX now */ | ||
1050 | dev_dbg(adapter->dev, "info: do_rx_aggr: num of packets: %d\n", | ||
1051 | card->mpa_rx.pkt_cnt); | ||
1052 | |||
1053 | if (mwifiex_read_data_sync(adapter, card->mpa_rx.buf, | ||
1054 | card->mpa_rx.buf_len, | ||
1055 | (adapter->ioport | 0x1000 | | ||
1056 | (card->mpa_rx.ports << 4)) + | ||
1057 | card->mpa_rx.start_port, 0, 1)) | ||
1058 | return -1; | ||
1059 | |||
1060 | curr_ptr = card->mpa_rx.buf; | ||
1061 | |||
1062 | for (pind = 0; pind < card->mpa_rx.pkt_cnt; pind++) { | ||
1063 | |||
1064 | /* get curr PKT len & type */ | ||
1065 | pkt_len = *(u16 *) &curr_ptr[0]; | ||
1066 | pkt_type = *(u16 *) &curr_ptr[2]; | ||
1067 | |||
1068 | /* copy pkt to deaggr buf */ | ||
1069 | skb_deaggr = card->mpa_rx.skb_arr[pind]; | ||
1070 | |||
1071 | if ((pkt_type == MWIFIEX_TYPE_DATA) && (pkt_len <= | ||
1072 | card->mpa_rx.len_arr[pind])) { | ||
1073 | |||
1074 | memcpy(skb_deaggr->data, curr_ptr, pkt_len); | ||
1075 | |||
1076 | skb_trim(skb_deaggr, pkt_len); | ||
1077 | |||
1078 | /* Process de-aggr packet */ | ||
1079 | mwifiex_decode_rx_packet(adapter, skb_deaggr, | ||
1080 | pkt_type); | ||
1081 | } else { | ||
1082 | dev_err(adapter->dev, "wrong aggr pkt:" | ||
1083 | " type=%d len=%d max_len=%d\n", | ||
1084 | pkt_type, pkt_len, | ||
1085 | card->mpa_rx.len_arr[pind]); | ||
1086 | dev_kfree_skb_any(skb_deaggr); | ||
1087 | } | ||
1088 | curr_ptr += card->mpa_rx.len_arr[pind]; | ||
1089 | } | ||
1090 | MP_RX_AGGR_BUF_RESET(card); | ||
1091 | } | ||
1092 | |||
1093 | rx_curr_single: | ||
1094 | if (f_do_rx_cur) { | ||
1095 | dev_dbg(adapter->dev, "info: RX: port: %d, rx_len: %d\n", | ||
1096 | port, rx_len); | ||
1097 | |||
1098 | if (mwifiex_sdio_card_to_host(adapter, &pkt_type, | ||
1099 | skb->data, skb->len, | ||
1100 | adapter->ioport + port)) | ||
1101 | return -1; | ||
1102 | |||
1103 | mwifiex_decode_rx_packet(adapter, skb, pkt_type); | ||
1104 | } | ||
1105 | |||
1106 | return 0; | ||
1107 | } | ||
1108 | |||
1109 | /* | ||
1110 | * This function checks the current interrupt status. | ||
1111 | * | ||
1112 | * The following interrupts are checked and handled by this function - | ||
1113 | * - Data sent | ||
1114 | * - Command sent | ||
1115 | * - Packets received | ||
1116 | * | ||
1117 | * Since the firmware does not generate download ready interrupt if the | ||
1118 | * port updated is command port only, command sent interrupt checking | ||
1119 | * should be done manually, and for every SDIO interrupt. | ||
1120 | * | ||
1121 | * In case of Rx packets received, the packets are uploaded from card to | ||
1122 | * host and processed accordingly. | ||
1123 | */ | ||
1124 | static int mwifiex_process_int_status(struct mwifiex_adapter *adapter) | ||
1125 | { | ||
1126 | struct sdio_mmc_card *card = adapter->card; | ||
1127 | int ret = 0; | ||
1128 | u8 sdio_ireg; | ||
1129 | struct sk_buff *skb = NULL; | ||
1130 | u8 port = CTRL_PORT; | ||
1131 | u32 len_reg_l, len_reg_u; | ||
1132 | u32 rx_blocks; | ||
1133 | u16 rx_len; | ||
1134 | unsigned long flags; | ||
1135 | |||
1136 | spin_lock_irqsave(&adapter->int_lock, flags); | ||
1137 | sdio_ireg = adapter->int_status; | ||
1138 | adapter->int_status = 0; | ||
1139 | spin_unlock_irqrestore(&adapter->int_lock, flags); | ||
1140 | |||
1141 | if (!sdio_ireg) | ||
1142 | return ret; | ||
1143 | |||
1144 | if (sdio_ireg & DN_LD_HOST_INT_STATUS) { | ||
1145 | card->mp_wr_bitmap = ((u16) card->mp_regs[WR_BITMAP_U]) << 8; | ||
1146 | card->mp_wr_bitmap |= (u16) card->mp_regs[WR_BITMAP_L]; | ||
1147 | dev_dbg(adapter->dev, "int: DNLD: wr_bitmap=0x%04x\n", | ||
1148 | card->mp_wr_bitmap); | ||
1149 | if (adapter->data_sent && | ||
1150 | (card->mp_wr_bitmap & card->mp_data_port_mask)) { | ||
1151 | dev_dbg(adapter->dev, | ||
1152 | "info: <--- Tx DONE Interrupt --->\n"); | ||
1153 | adapter->data_sent = false; | ||
1154 | } | ||
1155 | } | ||
1156 | |||
1157 | /* As firmware will not generate download ready interrupt if the port | ||
1158 | updated is command port only, cmd_sent should be done for any SDIO | ||
1159 | interrupt. */ | ||
1160 | if (adapter->cmd_sent) { | ||
1161 | /* Check if firmware has attach buffer at command port and | ||
1162 | update just that in wr_bit_map. */ | ||
1163 | card->mp_wr_bitmap |= | ||
1164 | (u16) card->mp_regs[WR_BITMAP_L] & CTRL_PORT_MASK; | ||
1165 | if (card->mp_wr_bitmap & CTRL_PORT_MASK) | ||
1166 | adapter->cmd_sent = false; | ||
1167 | } | ||
1168 | |||
1169 | dev_dbg(adapter->dev, "info: cmd_sent=%d data_sent=%d\n", | ||
1170 | adapter->cmd_sent, adapter->data_sent); | ||
1171 | if (sdio_ireg & UP_LD_HOST_INT_STATUS) { | ||
1172 | card->mp_rd_bitmap = ((u16) card->mp_regs[RD_BITMAP_U]) << 8; | ||
1173 | card->mp_rd_bitmap |= (u16) card->mp_regs[RD_BITMAP_L]; | ||
1174 | dev_dbg(adapter->dev, "int: UPLD: rd_bitmap=0x%04x\n", | ||
1175 | card->mp_rd_bitmap); | ||
1176 | |||
1177 | while (true) { | ||
1178 | ret = mwifiex_get_rd_port(adapter, &port); | ||
1179 | if (ret) { | ||
1180 | dev_dbg(adapter->dev, | ||
1181 | "info: no more rd_port available\n"); | ||
1182 | break; | ||
1183 | } | ||
1184 | len_reg_l = RD_LEN_P0_L + (port << 1); | ||
1185 | len_reg_u = RD_LEN_P0_U + (port << 1); | ||
1186 | rx_len = ((u16) card->mp_regs[len_reg_u]) << 8; | ||
1187 | rx_len |= (u16) card->mp_regs[len_reg_l]; | ||
1188 | dev_dbg(adapter->dev, "info: RX: port=%d rx_len=%u\n", | ||
1189 | port, rx_len); | ||
1190 | rx_blocks = | ||
1191 | (rx_len + MWIFIEX_SDIO_BLOCK_SIZE - | ||
1192 | 1) / MWIFIEX_SDIO_BLOCK_SIZE; | ||
1193 | if (rx_len <= INTF_HEADER_LEN | ||
1194 | || (rx_blocks * MWIFIEX_SDIO_BLOCK_SIZE) > | ||
1195 | MWIFIEX_RX_DATA_BUF_SIZE) { | ||
1196 | dev_err(adapter->dev, "invalid rx_len=%d\n", | ||
1197 | rx_len); | ||
1198 | return -1; | ||
1199 | } | ||
1200 | rx_len = (u16) (rx_blocks * MWIFIEX_SDIO_BLOCK_SIZE); | ||
1201 | |||
1202 | skb = dev_alloc_skb(rx_len); | ||
1203 | |||
1204 | if (!skb) { | ||
1205 | dev_err(adapter->dev, "%s: failed to alloc skb", | ||
1206 | __func__); | ||
1207 | return -1; | ||
1208 | } | ||
1209 | |||
1210 | skb_put(skb, rx_len); | ||
1211 | |||
1212 | dev_dbg(adapter->dev, "info: rx_len = %d skb->len = %d\n", | ||
1213 | rx_len, skb->len); | ||
1214 | |||
1215 | if (mwifiex_sdio_card_to_host_mp_aggr(adapter, skb, | ||
1216 | port)) { | ||
1217 | u32 cr = 0; | ||
1218 | |||
1219 | dev_err(adapter->dev, "card_to_host_mpa failed:" | ||
1220 | " int status=%#x\n", sdio_ireg); | ||
1221 | if (mwifiex_read_reg(adapter, | ||
1222 | CONFIGURATION_REG, &cr)) | ||
1223 | dev_err(adapter->dev, | ||
1224 | "read CFG reg failed\n"); | ||
1225 | |||
1226 | dev_dbg(adapter->dev, | ||
1227 | "info: CFG reg val = %d\n", cr); | ||
1228 | if (mwifiex_write_reg(adapter, | ||
1229 | CONFIGURATION_REG, | ||
1230 | (cr | 0x04))) | ||
1231 | dev_err(adapter->dev, | ||
1232 | "write CFG reg failed\n"); | ||
1233 | |||
1234 | dev_dbg(adapter->dev, "info: write success\n"); | ||
1235 | if (mwifiex_read_reg(adapter, | ||
1236 | CONFIGURATION_REG, &cr)) | ||
1237 | dev_err(adapter->dev, | ||
1238 | "read CFG reg failed\n"); | ||
1239 | |||
1240 | dev_dbg(adapter->dev, | ||
1241 | "info: CFG reg val =%x\n", cr); | ||
1242 | dev_kfree_skb_any(skb); | ||
1243 | return -1; | ||
1244 | } | ||
1245 | } | ||
1246 | } | ||
1247 | |||
1248 | return 0; | ||
1249 | } | ||
1250 | |||
1251 | /* | ||
1252 | * This function aggregates transmission buffers in driver and downloads | ||
1253 | * the aggregated packet to card. | ||
1254 | * | ||
1255 | * The individual packets are aggregated by copying into an aggregation | ||
1256 | * buffer and then downloaded to the card. Previous unsent packets in the | ||
1257 | * aggregation buffer are pre-copied first before new packets are added. | ||
1258 | * Aggregation is done till there is space left in the aggregation buffer, | ||
1259 | * or till new packets are available. | ||
1260 | * | ||
1261 | * The function will only download the packet to the card when aggregation | ||
1262 | * stops, otherwise it will just aggregate the packet in aggregation buffer | ||
1263 | * and return. | ||
1264 | */ | ||
1265 | static int mwifiex_host_to_card_mp_aggr(struct mwifiex_adapter *adapter, | ||
1266 | u8 *payload, u32 pkt_len, u8 port, | ||
1267 | u32 next_pkt_len) | ||
1268 | { | ||
1269 | struct sdio_mmc_card *card = adapter->card; | ||
1270 | int ret = 0; | ||
1271 | s32 f_send_aggr_buf = 0; | ||
1272 | s32 f_send_cur_buf = 0; | ||
1273 | s32 f_precopy_cur_buf = 0; | ||
1274 | s32 f_postcopy_cur_buf = 0; | ||
1275 | |||
1276 | if ((!card->mpa_tx.enabled) || (port == CTRL_PORT)) { | ||
1277 | dev_dbg(adapter->dev, "info: %s: tx aggregation disabled\n", | ||
1278 | __func__); | ||
1279 | |||
1280 | f_send_cur_buf = 1; | ||
1281 | goto tx_curr_single; | ||
1282 | } | ||
1283 | |||
1284 | if (next_pkt_len) { | ||
1285 | /* More pkt in TX queue */ | ||
1286 | dev_dbg(adapter->dev, "info: %s: more packets in queue.\n", | ||
1287 | __func__); | ||
1288 | |||
1289 | if (MP_TX_AGGR_IN_PROGRESS(card)) { | ||
1290 | if (!MP_TX_AGGR_PORT_LIMIT_REACHED(card) && | ||
1291 | MP_TX_AGGR_BUF_HAS_ROOM(card, pkt_len)) { | ||
1292 | f_precopy_cur_buf = 1; | ||
1293 | |||
1294 | if (!(card->mp_wr_bitmap & | ||
1295 | (1 << card->curr_wr_port)) | ||
1296 | || !MP_TX_AGGR_BUF_HAS_ROOM( | ||
1297 | card, next_pkt_len)) | ||
1298 | f_send_aggr_buf = 1; | ||
1299 | } else { | ||
1300 | /* No room in Aggr buf, send it */ | ||
1301 | f_send_aggr_buf = 1; | ||
1302 | |||
1303 | if (MP_TX_AGGR_PORT_LIMIT_REACHED(card) || | ||
1304 | !(card->mp_wr_bitmap & | ||
1305 | (1 << card->curr_wr_port))) | ||
1306 | f_send_cur_buf = 1; | ||
1307 | else | ||
1308 | f_postcopy_cur_buf = 1; | ||
1309 | } | ||
1310 | } else { | ||
1311 | if (MP_TX_AGGR_BUF_HAS_ROOM(card, pkt_len) | ||
1312 | && (card->mp_wr_bitmap & (1 << card->curr_wr_port))) | ||
1313 | f_precopy_cur_buf = 1; | ||
1314 | else | ||
1315 | f_send_cur_buf = 1; | ||
1316 | } | ||
1317 | } else { | ||
1318 | /* Last pkt in TX queue */ | ||
1319 | dev_dbg(adapter->dev, "info: %s: Last packet in Tx Queue.\n", | ||
1320 | __func__); | ||
1321 | |||
1322 | if (MP_TX_AGGR_IN_PROGRESS(card)) { | ||
1323 | /* some packs in Aggr buf already */ | ||
1324 | f_send_aggr_buf = 1; | ||
1325 | |||
1326 | if (MP_TX_AGGR_BUF_HAS_ROOM(card, pkt_len)) | ||
1327 | f_precopy_cur_buf = 1; | ||
1328 | else | ||
1329 | /* No room in Aggr buf, send it */ | ||
1330 | f_send_cur_buf = 1; | ||
1331 | } else { | ||
1332 | f_send_cur_buf = 1; | ||
1333 | } | ||
1334 | } | ||
1335 | |||
1336 | if (f_precopy_cur_buf) { | ||
1337 | dev_dbg(adapter->dev, "data: %s: precopy current buffer\n", | ||
1338 | __func__); | ||
1339 | MP_TX_AGGR_BUF_PUT(card, payload, pkt_len, port); | ||
1340 | |||
1341 | if (MP_TX_AGGR_PKT_LIMIT_REACHED(card) || | ||
1342 | MP_TX_AGGR_PORT_LIMIT_REACHED(card)) | ||
1343 | /* No more pkts allowed in Aggr buf, send it */ | ||
1344 | f_send_aggr_buf = 1; | ||
1345 | } | ||
1346 | |||
1347 | if (f_send_aggr_buf) { | ||
1348 | dev_dbg(adapter->dev, "data: %s: send aggr buffer: %d %d\n", | ||
1349 | __func__, | ||
1350 | card->mpa_tx.start_port, card->mpa_tx.ports); | ||
1351 | ret = mwifiex_write_data_to_card(adapter, card->mpa_tx.buf, | ||
1352 | card->mpa_tx.buf_len, | ||
1353 | (adapter->ioport | 0x1000 | | ||
1354 | (card->mpa_tx.ports << 4)) + | ||
1355 | card->mpa_tx.start_port); | ||
1356 | |||
1357 | MP_TX_AGGR_BUF_RESET(card); | ||
1358 | } | ||
1359 | |||
1360 | tx_curr_single: | ||
1361 | if (f_send_cur_buf) { | ||
1362 | dev_dbg(adapter->dev, "data: %s: send current buffer %d\n", | ||
1363 | __func__, port); | ||
1364 | ret = mwifiex_write_data_to_card(adapter, payload, pkt_len, | ||
1365 | adapter->ioport + port); | ||
1366 | } | ||
1367 | |||
1368 | if (f_postcopy_cur_buf) { | ||
1369 | dev_dbg(adapter->dev, "data: %s: postcopy current buffer\n", | ||
1370 | __func__); | ||
1371 | MP_TX_AGGR_BUF_PUT(card, payload, pkt_len, port); | ||
1372 | } | ||
1373 | |||
1374 | return ret; | ||
1375 | } | ||
1376 | |||
1377 | /* | ||
1378 | * This function downloads data from driver to card. | ||
1379 | * | ||
1380 | * Both commands and data packets are transferred to the card by this | ||
1381 | * function. | ||
1382 | * | ||
1383 | * This function adds the SDIO specific header to the front of the buffer | ||
1384 | * before transferring. The header contains the length of the packet and | ||
1385 | * the type. The firmware handles the packets based upon this set type. | ||
1386 | */ | ||
1387 | static int mwifiex_sdio_host_to_card(struct mwifiex_adapter *adapter, | ||
1388 | u8 type, u8 *payload, u32 pkt_len, | ||
1389 | struct mwifiex_tx_param *tx_param) | ||
1390 | { | ||
1391 | struct sdio_mmc_card *card = adapter->card; | ||
1392 | int ret = 0; | ||
1393 | u32 buf_block_len; | ||
1394 | u32 blk_size; | ||
1395 | u8 port = CTRL_PORT; | ||
1396 | |||
1397 | /* Allocate buffer and copy payload */ | ||
1398 | blk_size = MWIFIEX_SDIO_BLOCK_SIZE; | ||
1399 | buf_block_len = (pkt_len + blk_size - 1) / blk_size; | ||
1400 | *(u16 *) &payload[0] = (u16) pkt_len; | ||
1401 | *(u16 *) &payload[2] = type; | ||
1402 | |||
1403 | /* | ||
1404 | * This is SDIO specific header | ||
1405 | * u16 length, | ||
1406 | * u16 type (MWIFIEX_TYPE_DATA = 0, MWIFIEX_TYPE_CMD = 1, | ||
1407 | * MWIFIEX_TYPE_EVENT = 3) | ||
1408 | */ | ||
1409 | if (type == MWIFIEX_TYPE_DATA) { | ||
1410 | ret = mwifiex_get_wr_port_data(adapter, &port); | ||
1411 | if (ret) { | ||
1412 | dev_err(adapter->dev, "%s: no wr_port available\n", | ||
1413 | __func__); | ||
1414 | return ret; | ||
1415 | } | ||
1416 | } else { | ||
1417 | adapter->cmd_sent = true; | ||
1418 | /* Type must be MWIFIEX_TYPE_CMD */ | ||
1419 | |||
1420 | if (pkt_len <= INTF_HEADER_LEN || | ||
1421 | pkt_len > MWIFIEX_UPLD_SIZE) | ||
1422 | dev_err(adapter->dev, "%s: payload=%p, nb=%d\n", | ||
1423 | __func__, payload, pkt_len); | ||
1424 | } | ||
1425 | |||
1426 | /* Transfer data to card */ | ||
1427 | pkt_len = buf_block_len * blk_size; | ||
1428 | |||
1429 | if (tx_param) | ||
1430 | ret = mwifiex_host_to_card_mp_aggr(adapter, payload, pkt_len, | ||
1431 | port, tx_param->next_pkt_len); | ||
1432 | else | ||
1433 | ret = mwifiex_host_to_card_mp_aggr(adapter, payload, pkt_len, | ||
1434 | port, 0); | ||
1435 | |||
1436 | if (ret) { | ||
1437 | if (type == MWIFIEX_TYPE_CMD) | ||
1438 | adapter->cmd_sent = false; | ||
1439 | if (type == MWIFIEX_TYPE_DATA) | ||
1440 | adapter->data_sent = false; | ||
1441 | } else { | ||
1442 | if (type == MWIFIEX_TYPE_DATA) { | ||
1443 | if (!(card->mp_wr_bitmap & (1 << card->curr_wr_port))) | ||
1444 | adapter->data_sent = true; | ||
1445 | else | ||
1446 | adapter->data_sent = false; | ||
1447 | } | ||
1448 | } | ||
1449 | |||
1450 | return ret; | ||
1451 | } | ||
1452 | |||
1453 | /* | ||
1454 | * This function allocates the MPA Tx and Rx buffers. | ||
1455 | */ | ||
1456 | static int mwifiex_alloc_sdio_mpa_buffers(struct mwifiex_adapter *adapter, | ||
1457 | u32 mpa_tx_buf_size, u32 mpa_rx_buf_size) | ||
1458 | { | ||
1459 | struct sdio_mmc_card *card = adapter->card; | ||
1460 | int ret = 0; | ||
1461 | |||
1462 | card->mpa_tx.buf = kzalloc(mpa_tx_buf_size, GFP_KERNEL); | ||
1463 | if (!card->mpa_tx.buf) { | ||
1464 | dev_err(adapter->dev, "could not alloc buffer for MP-A TX\n"); | ||
1465 | ret = -1; | ||
1466 | goto error; | ||
1467 | } | ||
1468 | |||
1469 | card->mpa_tx.buf_size = mpa_tx_buf_size; | ||
1470 | |||
1471 | card->mpa_rx.buf = kzalloc(mpa_rx_buf_size, GFP_KERNEL); | ||
1472 | if (!card->mpa_rx.buf) { | ||
1473 | dev_err(adapter->dev, "could not alloc buffer for MP-A RX\n"); | ||
1474 | ret = -1; | ||
1475 | goto error; | ||
1476 | } | ||
1477 | |||
1478 | card->mpa_rx.buf_size = mpa_rx_buf_size; | ||
1479 | |||
1480 | error: | ||
1481 | if (ret) { | ||
1482 | kfree(card->mpa_tx.buf); | ||
1483 | kfree(card->mpa_rx.buf); | ||
1484 | } | ||
1485 | |||
1486 | return ret; | ||
1487 | } | ||
1488 | |||
1489 | /* | ||
1490 | * This function unregisters the SDIO device. | ||
1491 | * | ||
1492 | * The SDIO IRQ is released, the function is disabled and driver | ||
1493 | * data is set to null. | ||
1494 | */ | ||
1495 | static void | ||
1496 | mwifiex_unregister_dev(struct mwifiex_adapter *adapter) | ||
1497 | { | ||
1498 | struct sdio_mmc_card *card = adapter->card; | ||
1499 | |||
1500 | if (adapter->card) { | ||
1501 | /* Release the SDIO IRQ */ | ||
1502 | sdio_claim_host(card->func); | ||
1503 | sdio_release_irq(card->func); | ||
1504 | sdio_disable_func(card->func); | ||
1505 | sdio_release_host(card->func); | ||
1506 | sdio_set_drvdata(card->func, NULL); | ||
1507 | } | ||
1508 | } | ||
1509 | |||
1510 | /* | ||
1511 | * This function registers the SDIO device. | ||
1512 | * | ||
1513 | * SDIO IRQ is claimed, block size is set and driver data is initialized. | ||
1514 | */ | ||
1515 | static int mwifiex_register_dev(struct mwifiex_adapter *adapter) | ||
1516 | { | ||
1517 | int ret = 0; | ||
1518 | struct sdio_mmc_card *card = adapter->card; | ||
1519 | struct sdio_func *func = card->func; | ||
1520 | |||
1521 | /* save adapter pointer in card */ | ||
1522 | card->adapter = adapter; | ||
1523 | |||
1524 | sdio_claim_host(func); | ||
1525 | |||
1526 | /* Request the SDIO IRQ */ | ||
1527 | ret = sdio_claim_irq(func, mwifiex_sdio_interrupt); | ||
1528 | if (ret) { | ||
1529 | pr_err("claim irq failed: ret=%d\n", ret); | ||
1530 | goto disable_func; | ||
1531 | } | ||
1532 | |||
1533 | /* Set block size */ | ||
1534 | ret = sdio_set_block_size(card->func, MWIFIEX_SDIO_BLOCK_SIZE); | ||
1535 | if (ret) { | ||
1536 | pr_err("cannot set SDIO block size\n"); | ||
1537 | ret = -1; | ||
1538 | goto release_irq; | ||
1539 | } | ||
1540 | |||
1541 | sdio_release_host(func); | ||
1542 | sdio_set_drvdata(func, card); | ||
1543 | |||
1544 | adapter->dev = &func->dev; | ||
1545 | |||
1546 | return 0; | ||
1547 | |||
1548 | release_irq: | ||
1549 | sdio_release_irq(func); | ||
1550 | disable_func: | ||
1551 | sdio_disable_func(func); | ||
1552 | sdio_release_host(func); | ||
1553 | adapter->card = NULL; | ||
1554 | |||
1555 | return -1; | ||
1556 | } | ||
1557 | |||
1558 | /* | ||
1559 | * This function initializes the SDIO driver. | ||
1560 | * | ||
1561 | * The following initializations steps are followed - | ||
1562 | * - Read the Host interrupt status register to acknowledge | ||
1563 | * the first interrupt got from bootloader | ||
1564 | * - Disable host interrupt mask register | ||
1565 | * - Get SDIO port | ||
1566 | * - Get revision ID | ||
1567 | * - Initialize SDIO variables in card | ||
1568 | * - Allocate MP registers | ||
1569 | * - Allocate MPA Tx and Rx buffers | ||
1570 | */ | ||
1571 | static int mwifiex_init_sdio(struct mwifiex_adapter *adapter) | ||
1572 | { | ||
1573 | struct sdio_mmc_card *card = adapter->card; | ||
1574 | int ret; | ||
1575 | u32 sdio_ireg = 0; | ||
1576 | |||
1577 | /* | ||
1578 | * Read the HOST_INT_STATUS_REG for ACK the first interrupt got | ||
1579 | * from the bootloader. If we don't do this we get a interrupt | ||
1580 | * as soon as we register the irq. | ||
1581 | */ | ||
1582 | mwifiex_read_reg(adapter, HOST_INTSTATUS_REG, &sdio_ireg); | ||
1583 | |||
1584 | /* Disable host interrupt mask register for SDIO */ | ||
1585 | mwifiex_sdio_disable_host_int(adapter); | ||
1586 | |||
1587 | /* Get SDIO ioport */ | ||
1588 | mwifiex_init_sdio_ioport(adapter); | ||
1589 | |||
1590 | /* Get revision ID */ | ||
1591 | #define REV_ID_REG 0x5c | ||
1592 | mwifiex_read_reg(adapter, REV_ID_REG, &adapter->revision_id); | ||
1593 | |||
1594 | /* Initialize SDIO variables in card */ | ||
1595 | card->mp_rd_bitmap = 0; | ||
1596 | card->mp_wr_bitmap = 0; | ||
1597 | card->curr_rd_port = 1; | ||
1598 | card->curr_wr_port = 1; | ||
1599 | |||
1600 | card->mp_data_port_mask = DATA_PORT_MASK; | ||
1601 | |||
1602 | card->mpa_tx.buf_len = 0; | ||
1603 | card->mpa_tx.pkt_cnt = 0; | ||
1604 | card->mpa_tx.start_port = 0; | ||
1605 | |||
1606 | card->mpa_tx.enabled = 0; | ||
1607 | card->mpa_tx.pkt_aggr_limit = SDIO_MP_AGGR_DEF_PKT_LIMIT; | ||
1608 | |||
1609 | card->mpa_rx.buf_len = 0; | ||
1610 | card->mpa_rx.pkt_cnt = 0; | ||
1611 | card->mpa_rx.start_port = 0; | ||
1612 | |||
1613 | card->mpa_rx.enabled = 0; | ||
1614 | card->mpa_rx.pkt_aggr_limit = SDIO_MP_AGGR_DEF_PKT_LIMIT; | ||
1615 | |||
1616 | /* Allocate buffers for SDIO MP-A */ | ||
1617 | card->mp_regs = kzalloc(MAX_MP_REGS, GFP_KERNEL); | ||
1618 | if (!card->mp_regs) { | ||
1619 | dev_err(adapter->dev, "failed to alloc mp_regs\n"); | ||
1620 | return -1; | ||
1621 | } | ||
1622 | |||
1623 | ret = mwifiex_alloc_sdio_mpa_buffers(adapter, | ||
1624 | SDIO_MP_TX_AGGR_DEF_BUF_SIZE, | ||
1625 | SDIO_MP_RX_AGGR_DEF_BUF_SIZE); | ||
1626 | if (ret) { | ||
1627 | dev_err(adapter->dev, "failed to alloc sdio mp-a buffers\n"); | ||
1628 | kfree(card->mp_regs); | ||
1629 | return -1; | ||
1630 | } | ||
1631 | |||
1632 | return ret; | ||
1633 | } | ||
1634 | |||
1635 | /* | ||
1636 | * This function resets the MPA Tx and Rx buffers. | ||
1637 | */ | ||
1638 | static void mwifiex_cleanup_mpa_buf(struct mwifiex_adapter *adapter) | ||
1639 | { | ||
1640 | struct sdio_mmc_card *card = adapter->card; | ||
1641 | |||
1642 | MP_TX_AGGR_BUF_RESET(card); | ||
1643 | MP_RX_AGGR_BUF_RESET(card); | ||
1644 | } | ||
1645 | |||
1646 | /* | ||
1647 | * This function cleans up the allocated card buffers. | ||
1648 | * | ||
1649 | * The following are freed by this function - | ||
1650 | * - MP registers | ||
1651 | * - MPA Tx buffer | ||
1652 | * - MPA Rx buffer | ||
1653 | */ | ||
1654 | static void mwifiex_cleanup_sdio(struct mwifiex_adapter *adapter) | ||
1655 | { | ||
1656 | struct sdio_mmc_card *card = adapter->card; | ||
1657 | |||
1658 | kfree(card->mp_regs); | ||
1659 | kfree(card->mpa_tx.buf); | ||
1660 | kfree(card->mpa_rx.buf); | ||
1661 | } | ||
1662 | |||
1663 | /* | ||
1664 | * This function updates the MP end port in card. | ||
1665 | */ | ||
1666 | static void | ||
1667 | mwifiex_update_mp_end_port(struct mwifiex_adapter *adapter, u16 port) | ||
1668 | { | ||
1669 | struct sdio_mmc_card *card = adapter->card; | ||
1670 | int i; | ||
1671 | |||
1672 | card->mp_end_port = port; | ||
1673 | |||
1674 | card->mp_data_port_mask = DATA_PORT_MASK; | ||
1675 | |||
1676 | for (i = 1; i <= MAX_PORT - card->mp_end_port; i++) | ||
1677 | card->mp_data_port_mask &= ~(1 << (MAX_PORT - i)); | ||
1678 | |||
1679 | card->curr_wr_port = 1; | ||
1680 | |||
1681 | dev_dbg(adapter->dev, "cmd: mp_end_port %d, data port mask 0x%x\n", | ||
1682 | port, card->mp_data_port_mask); | ||
1683 | } | ||
1684 | |||
1685 | static struct mwifiex_if_ops sdio_ops = { | ||
1686 | .init_if = mwifiex_init_sdio, | ||
1687 | .cleanup_if = mwifiex_cleanup_sdio, | ||
1688 | .check_fw_status = mwifiex_check_fw_status, | ||
1689 | .prog_fw = mwifiex_prog_fw_w_helper, | ||
1690 | .register_dev = mwifiex_register_dev, | ||
1691 | .unregister_dev = mwifiex_unregister_dev, | ||
1692 | .enable_int = mwifiex_sdio_enable_host_int, | ||
1693 | .process_int_status = mwifiex_process_int_status, | ||
1694 | .host_to_card = mwifiex_sdio_host_to_card, | ||
1695 | .wakeup = mwifiex_pm_wakeup_card, | ||
1696 | .wakeup_complete = mwifiex_pm_wakeup_card_complete, | ||
1697 | |||
1698 | /* SDIO specific */ | ||
1699 | .update_mp_end_port = mwifiex_update_mp_end_port, | ||
1700 | .cleanup_mpa_buf = mwifiex_cleanup_mpa_buf, | ||
1701 | }; | ||
1702 | |||
1703 | /* | ||
1704 | * This function initializes the SDIO driver. | ||
1705 | * | ||
1706 | * This initiates the semaphore and registers the device with | ||
1707 | * SDIO bus. | ||
1708 | */ | ||
1709 | static int | ||
1710 | mwifiex_sdio_init_module(void) | ||
1711 | { | ||
1712 | int ret; | ||
1713 | |||
1714 | sema_init(&add_remove_card_sem, 1); | ||
1715 | |||
1716 | ret = sdio_register_driver(&mwifiex_sdio); | ||
1717 | |||
1718 | return ret; | ||
1719 | } | ||
1720 | |||
1721 | /* | ||
1722 | * This function cleans up the SDIO driver. | ||
1723 | * | ||
1724 | * The following major steps are followed for cleanup - | ||
1725 | * - Resume the device if its suspended | ||
1726 | * - Disconnect the device if connected | ||
1727 | * - Shutdown the firmware | ||
1728 | * - Unregister the device from SDIO bus. | ||
1729 | */ | ||
1730 | static void | ||
1731 | mwifiex_sdio_cleanup_module(void) | ||
1732 | { | ||
1733 | struct mwifiex_adapter *adapter = g_adapter; | ||
1734 | int i; | ||
1735 | |||
1736 | if (down_interruptible(&add_remove_card_sem)) | ||
1737 | goto exit_sem_err; | ||
1738 | |||
1739 | if (!adapter || !adapter->priv_num) | ||
1740 | goto exit; | ||
1741 | |||
1742 | if (adapter->is_suspended) | ||
1743 | mwifiex_sdio_resume(adapter->dev); | ||
1744 | |||
1745 | for (i = 0; i < adapter->priv_num; i++) | ||
1746 | if ((GET_BSS_ROLE(adapter->priv[i]) == MWIFIEX_BSS_ROLE_STA) && | ||
1747 | adapter->priv[i]->media_connected) | ||
1748 | mwifiex_disconnect(adapter->priv[i], MWIFIEX_CMD_WAIT, | ||
1749 | NULL); | ||
1750 | |||
1751 | if (!adapter->surprise_removed) | ||
1752 | mwifiex_shutdown_fw(mwifiex_get_priv | ||
1753 | (adapter, MWIFIEX_BSS_ROLE_ANY), | ||
1754 | MWIFIEX_CMD_WAIT); | ||
1755 | |||
1756 | exit: | ||
1757 | up(&add_remove_card_sem); | ||
1758 | |||
1759 | exit_sem_err: | ||
1760 | sdio_unregister_driver(&mwifiex_sdio); | ||
1761 | } | ||
1762 | |||
1763 | module_init(mwifiex_sdio_init_module); | ||
1764 | module_exit(mwifiex_sdio_cleanup_module); | ||
1765 | |||
1766 | MODULE_AUTHOR("Marvell International Ltd."); | ||
1767 | MODULE_DESCRIPTION("Marvell WiFi-Ex SDIO Driver version " SDIO_VERSION); | ||
1768 | MODULE_VERSION(SDIO_VERSION); | ||
1769 | MODULE_LICENSE("GPL v2"); | ||
1770 | MODULE_FIRMWARE("sd8787.bin"); | ||
diff --git a/drivers/net/wireless/mwifiex/sdio.h b/drivers/net/wireless/mwifiex/sdio.h new file mode 100644 index 000000000000..a0e9bc5253e0 --- /dev/null +++ b/drivers/net/wireless/mwifiex/sdio.h | |||
@@ -0,0 +1,305 @@ | |||
1 | /* | ||
2 | * Marvell Wireless LAN device driver: SDIO specific definitions | ||
3 | * | ||
4 | * Copyright (C) 2011, Marvell International Ltd. | ||
5 | * | ||
6 | * This software file (the "File") is distributed by Marvell International | ||
7 | * Ltd. under the terms of the GNU General Public License Version 2, June 1991 | ||
8 | * (the "License"). You may use, redistribute and/or modify this File in | ||
9 | * accordance with the terms and conditions of the License, a copy of which | ||
10 | * is available by writing to the Free Software Foundation, Inc., | ||
11 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the | ||
12 | * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. | ||
13 | * | ||
14 | * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE | ||
15 | * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE | ||
16 | * ARE EXPRESSLY DISCLAIMED. The License provides additional details about | ||
17 | * this warranty disclaimer. | ||
18 | */ | ||
19 | |||
20 | #ifndef _MWIFIEX_SDIO_H | ||
21 | #define _MWIFIEX_SDIO_H | ||
22 | |||
23 | |||
24 | #include <linux/mmc/sdio.h> | ||
25 | #include <linux/mmc/sdio_ids.h> | ||
26 | #include <linux/mmc/sdio_func.h> | ||
27 | #include <linux/mmc/card.h> | ||
28 | |||
29 | #include "main.h" | ||
30 | |||
31 | #define BLOCK_MODE 1 | ||
32 | #define BYTE_MODE 0 | ||
33 | |||
34 | #define REG_PORT 0 | ||
35 | #define RD_BITMAP_L 0x04 | ||
36 | #define RD_BITMAP_U 0x05 | ||
37 | #define WR_BITMAP_L 0x06 | ||
38 | #define WR_BITMAP_U 0x07 | ||
39 | #define RD_LEN_P0_L 0x08 | ||
40 | #define RD_LEN_P0_U 0x09 | ||
41 | |||
42 | #define MWIFIEX_SDIO_IO_PORT_MASK 0xfffff | ||
43 | |||
44 | #define MWIFIEX_SDIO_BYTE_MODE_MASK 0x80000000 | ||
45 | |||
46 | #define CTRL_PORT 0 | ||
47 | #define CTRL_PORT_MASK 0x0001 | ||
48 | #define DATA_PORT_MASK 0xfffe | ||
49 | |||
50 | #define MAX_MP_REGS 64 | ||
51 | #define MAX_PORT 16 | ||
52 | |||
53 | #define SDIO_MP_AGGR_DEF_PKT_LIMIT 8 | ||
54 | |||
55 | #define SDIO_MP_TX_AGGR_DEF_BUF_SIZE (4096) /* 4K */ | ||
56 | |||
57 | /* Multi port RX aggregation buffer size */ | ||
58 | #define SDIO_MP_RX_AGGR_DEF_BUF_SIZE (4096) /* 4K */ | ||
59 | |||
60 | /* Misc. Config Register : Auto Re-enable interrupts */ | ||
61 | #define AUTO_RE_ENABLE_INT BIT(4) | ||
62 | |||
63 | /* Host Control Registers */ | ||
64 | /* Host Control Registers : I/O port 0 */ | ||
65 | #define IO_PORT_0_REG 0x78 | ||
66 | /* Host Control Registers : I/O port 1 */ | ||
67 | #define IO_PORT_1_REG 0x79 | ||
68 | /* Host Control Registers : I/O port 2 */ | ||
69 | #define IO_PORT_2_REG 0x7A | ||
70 | |||
71 | /* Host Control Registers : Configuration */ | ||
72 | #define CONFIGURATION_REG 0x00 | ||
73 | /* Host Control Registers : Host without Command 53 finish host*/ | ||
74 | #define HOST_TO_CARD_EVENT (0x1U << 3) | ||
75 | /* Host Control Registers : Host without Command 53 finish host */ | ||
76 | #define HOST_WO_CMD53_FINISH_HOST (0x1U << 2) | ||
77 | /* Host Control Registers : Host power up */ | ||
78 | #define HOST_POWER_UP (0x1U << 1) | ||
79 | /* Host Control Registers : Host power down */ | ||
80 | #define HOST_POWER_DOWN (0x1U << 0) | ||
81 | |||
82 | /* Host Control Registers : Host interrupt mask */ | ||
83 | #define HOST_INT_MASK_REG 0x02 | ||
84 | /* Host Control Registers : Upload host interrupt mask */ | ||
85 | #define UP_LD_HOST_INT_MASK (0x1U) | ||
86 | /* Host Control Registers : Download host interrupt mask */ | ||
87 | #define DN_LD_HOST_INT_MASK (0x2U) | ||
88 | /* Enable Host interrupt mask */ | ||
89 | #define HOST_INT_ENABLE (UP_LD_HOST_INT_MASK | DN_LD_HOST_INT_MASK) | ||
90 | /* Disable Host interrupt mask */ | ||
91 | #define HOST_INT_DISABLE 0xff | ||
92 | |||
93 | /* Host Control Registers : Host interrupt status */ | ||
94 | #define HOST_INTSTATUS_REG 0x03 | ||
95 | /* Host Control Registers : Upload host interrupt status */ | ||
96 | #define UP_LD_HOST_INT_STATUS (0x1U) | ||
97 | /* Host Control Registers : Download host interrupt status */ | ||
98 | #define DN_LD_HOST_INT_STATUS (0x2U) | ||
99 | |||
100 | /* Host Control Registers : Host interrupt RSR */ | ||
101 | #define HOST_INT_RSR_REG 0x01 | ||
102 | /* Host Control Registers : Upload host interrupt RSR */ | ||
103 | #define UP_LD_HOST_INT_RSR (0x1U) | ||
104 | #define SDIO_INT_MASK 0x3F | ||
105 | |||
106 | /* Host Control Registers : Host interrupt status */ | ||
107 | #define HOST_INT_STATUS_REG 0x28 | ||
108 | /* Host Control Registers : Upload CRC error */ | ||
109 | #define UP_LD_CRC_ERR (0x1U << 2) | ||
110 | /* Host Control Registers : Upload restart */ | ||
111 | #define UP_LD_RESTART (0x1U << 1) | ||
112 | /* Host Control Registers : Download restart */ | ||
113 | #define DN_LD_RESTART (0x1U << 0) | ||
114 | |||
115 | /* Card Control Registers : Card status register */ | ||
116 | #define CARD_STATUS_REG 0x30 | ||
117 | /* Card Control Registers : Card I/O ready */ | ||
118 | #define CARD_IO_READY (0x1U << 3) | ||
119 | /* Card Control Registers : CIS card ready */ | ||
120 | #define CIS_CARD_RDY (0x1U << 2) | ||
121 | /* Card Control Registers : Upload card ready */ | ||
122 | #define UP_LD_CARD_RDY (0x1U << 1) | ||
123 | /* Card Control Registers : Download card ready */ | ||
124 | #define DN_LD_CARD_RDY (0x1U << 0) | ||
125 | |||
126 | /* Card Control Registers : Host interrupt mask register */ | ||
127 | #define HOST_INTERRUPT_MASK_REG 0x34 | ||
128 | /* Card Control Registers : Host power interrupt mask */ | ||
129 | #define HOST_POWER_INT_MASK (0x1U << 3) | ||
130 | /* Card Control Registers : Abort card interrupt mask */ | ||
131 | #define ABORT_CARD_INT_MASK (0x1U << 2) | ||
132 | /* Card Control Registers : Upload card interrupt mask */ | ||
133 | #define UP_LD_CARD_INT_MASK (0x1U << 1) | ||
134 | /* Card Control Registers : Download card interrupt mask */ | ||
135 | #define DN_LD_CARD_INT_MASK (0x1U << 0) | ||
136 | |||
137 | /* Card Control Registers : Card interrupt status register */ | ||
138 | #define CARD_INTERRUPT_STATUS_REG 0x38 | ||
139 | /* Card Control Registers : Power up interrupt */ | ||
140 | #define POWER_UP_INT (0x1U << 4) | ||
141 | /* Card Control Registers : Power down interrupt */ | ||
142 | #define POWER_DOWN_INT (0x1U << 3) | ||
143 | |||
144 | /* Card Control Registers : Card interrupt RSR register */ | ||
145 | #define CARD_INTERRUPT_RSR_REG 0x3c | ||
146 | /* Card Control Registers : Power up RSR */ | ||
147 | #define POWER_UP_RSR (0x1U << 4) | ||
148 | /* Card Control Registers : Power down RSR */ | ||
149 | #define POWER_DOWN_RSR (0x1U << 3) | ||
150 | |||
151 | /* Card Control Registers : Miscellaneous Configuration Register */ | ||
152 | #define CARD_MISC_CFG_REG 0x6C | ||
153 | |||
154 | /* Host F1 read base 0 */ | ||
155 | #define HOST_F1_RD_BASE_0 0x0040 | ||
156 | /* Host F1 read base 1 */ | ||
157 | #define HOST_F1_RD_BASE_1 0x0041 | ||
158 | /* Host F1 card ready */ | ||
159 | #define HOST_F1_CARD_RDY 0x0020 | ||
160 | |||
161 | /* Firmware status 0 register */ | ||
162 | #define CARD_FW_STATUS0_REG 0x60 | ||
163 | /* Firmware status 1 register */ | ||
164 | #define CARD_FW_STATUS1_REG 0x61 | ||
165 | /* Rx length register */ | ||
166 | #define CARD_RX_LEN_REG 0x62 | ||
167 | /* Rx unit register */ | ||
168 | #define CARD_RX_UNIT_REG 0x63 | ||
169 | |||
170 | /* Event header Len*/ | ||
171 | #define MWIFIEX_EVENT_HEADER_LEN 8 | ||
172 | |||
173 | /* Max retry number of CMD53 write */ | ||
174 | #define MAX_WRITE_IOMEM_RETRY 2 | ||
175 | |||
176 | /* SDIO Tx aggregation in progress ? */ | ||
177 | #define MP_TX_AGGR_IN_PROGRESS(a) (a->mpa_tx.pkt_cnt > 0) | ||
178 | |||
179 | /* SDIO Tx aggregation buffer room for next packet ? */ | ||
180 | #define MP_TX_AGGR_BUF_HAS_ROOM(a, len) ((a->mpa_tx.buf_len+len) \ | ||
181 | <= a->mpa_tx.buf_size) | ||
182 | |||
183 | /* Copy current packet (SDIO Tx aggregation buffer) to SDIO buffer */ | ||
184 | #define MP_TX_AGGR_BUF_PUT(a, payload, pkt_len, port) do { \ | ||
185 | memmove(&a->mpa_tx.buf[a->mpa_tx.buf_len], \ | ||
186 | payload, pkt_len); \ | ||
187 | a->mpa_tx.buf_len += pkt_len; \ | ||
188 | if (!a->mpa_tx.pkt_cnt) \ | ||
189 | a->mpa_tx.start_port = port; \ | ||
190 | if (a->mpa_tx.start_port <= port) \ | ||
191 | a->mpa_tx.ports |= (1<<(a->mpa_tx.pkt_cnt)); \ | ||
192 | else \ | ||
193 | a->mpa_tx.ports |= (1<<(a->mpa_tx.pkt_cnt+1+(MAX_PORT - \ | ||
194 | a->mp_end_port))); \ | ||
195 | a->mpa_tx.pkt_cnt++; \ | ||
196 | } while (0); | ||
197 | |||
198 | /* SDIO Tx aggregation limit ? */ | ||
199 | #define MP_TX_AGGR_PKT_LIMIT_REACHED(a) \ | ||
200 | (a->mpa_tx.pkt_cnt == a->mpa_tx.pkt_aggr_limit) | ||
201 | |||
202 | /* SDIO Tx aggregation port limit ? */ | ||
203 | #define MP_TX_AGGR_PORT_LIMIT_REACHED(a) ((a->curr_wr_port < \ | ||
204 | a->mpa_tx.start_port) && (((MAX_PORT - \ | ||
205 | a->mpa_tx.start_port) + a->curr_wr_port) >= \ | ||
206 | SDIO_MP_AGGR_DEF_PKT_LIMIT)) | ||
207 | |||
208 | /* Reset SDIO Tx aggregation buffer parameters */ | ||
209 | #define MP_TX_AGGR_BUF_RESET(a) do { \ | ||
210 | a->mpa_tx.pkt_cnt = 0; \ | ||
211 | a->mpa_tx.buf_len = 0; \ | ||
212 | a->mpa_tx.ports = 0; \ | ||
213 | a->mpa_tx.start_port = 0; \ | ||
214 | } while (0); | ||
215 | |||
216 | /* SDIO Rx aggregation limit ? */ | ||
217 | #define MP_RX_AGGR_PKT_LIMIT_REACHED(a) \ | ||
218 | (a->mpa_rx.pkt_cnt == a->mpa_rx.pkt_aggr_limit) | ||
219 | |||
220 | /* SDIO Tx aggregation port limit ? */ | ||
221 | #define MP_RX_AGGR_PORT_LIMIT_REACHED(a) ((a->curr_rd_port < \ | ||
222 | a->mpa_rx.start_port) && (((MAX_PORT - \ | ||
223 | a->mpa_rx.start_port) + a->curr_rd_port) >= \ | ||
224 | SDIO_MP_AGGR_DEF_PKT_LIMIT)) | ||
225 | |||
226 | /* SDIO Rx aggregation in progress ? */ | ||
227 | #define MP_RX_AGGR_IN_PROGRESS(a) (a->mpa_rx.pkt_cnt > 0) | ||
228 | |||
229 | /* SDIO Rx aggregation buffer room for next packet ? */ | ||
230 | #define MP_RX_AGGR_BUF_HAS_ROOM(a, rx_len) \ | ||
231 | ((a->mpa_rx.buf_len+rx_len) <= a->mpa_rx.buf_size) | ||
232 | |||
233 | /* Prepare to copy current packet from card to SDIO Rx aggregation buffer */ | ||
234 | #define MP_RX_AGGR_SETUP(a, skb, port) do { \ | ||
235 | a->mpa_rx.buf_len += skb->len; \ | ||
236 | if (!a->mpa_rx.pkt_cnt) \ | ||
237 | a->mpa_rx.start_port = port; \ | ||
238 | if (a->mpa_rx.start_port <= port) \ | ||
239 | a->mpa_rx.ports |= (1<<(a->mpa_rx.pkt_cnt)); \ | ||
240 | else \ | ||
241 | a->mpa_rx.ports |= (1<<(a->mpa_rx.pkt_cnt+1)); \ | ||
242 | a->mpa_rx.skb_arr[a->mpa_rx.pkt_cnt] = skb; \ | ||
243 | a->mpa_rx.len_arr[a->mpa_rx.pkt_cnt] = skb->len; \ | ||
244 | a->mpa_rx.pkt_cnt++; \ | ||
245 | } while (0); | ||
246 | |||
247 | /* Reset SDIO Rx aggregation buffer parameters */ | ||
248 | #define MP_RX_AGGR_BUF_RESET(a) do { \ | ||
249 | a->mpa_rx.pkt_cnt = 0; \ | ||
250 | a->mpa_rx.buf_len = 0; \ | ||
251 | a->mpa_rx.ports = 0; \ | ||
252 | a->mpa_rx.start_port = 0; \ | ||
253 | } while (0); | ||
254 | |||
255 | |||
256 | /* data structure for SDIO MPA TX */ | ||
257 | struct mwifiex_sdio_mpa_tx { | ||
258 | /* multiport tx aggregation buffer pointer */ | ||
259 | u8 *buf; | ||
260 | u32 buf_len; | ||
261 | u32 pkt_cnt; | ||
262 | u16 ports; | ||
263 | u16 start_port; | ||
264 | u8 enabled; | ||
265 | u32 buf_size; | ||
266 | u32 pkt_aggr_limit; | ||
267 | }; | ||
268 | |||
269 | struct mwifiex_sdio_mpa_rx { | ||
270 | u8 *buf; | ||
271 | u32 buf_len; | ||
272 | u32 pkt_cnt; | ||
273 | u16 ports; | ||
274 | u16 start_port; | ||
275 | |||
276 | struct sk_buff *skb_arr[SDIO_MP_AGGR_DEF_PKT_LIMIT]; | ||
277 | u32 len_arr[SDIO_MP_AGGR_DEF_PKT_LIMIT]; | ||
278 | |||
279 | u8 enabled; | ||
280 | u32 buf_size; | ||
281 | u32 pkt_aggr_limit; | ||
282 | }; | ||
283 | |||
284 | int mwifiex_bus_register(void); | ||
285 | void mwifiex_bus_unregister(void); | ||
286 | |||
287 | struct sdio_mmc_card { | ||
288 | struct sdio_func *func; | ||
289 | struct mwifiex_adapter *adapter; | ||
290 | |||
291 | u16 mp_rd_bitmap; | ||
292 | u16 mp_wr_bitmap; | ||
293 | |||
294 | u16 mp_end_port; | ||
295 | u16 mp_data_port_mask; | ||
296 | |||
297 | u8 curr_rd_port; | ||
298 | u8 curr_wr_port; | ||
299 | |||
300 | u8 *mp_regs; | ||
301 | |||
302 | struct mwifiex_sdio_mpa_tx mpa_tx; | ||
303 | struct mwifiex_sdio_mpa_rx mpa_rx; | ||
304 | }; | ||
305 | #endif /* _MWIFIEX_SDIO_H */ | ||
diff --git a/drivers/net/wireless/mwifiex/sta_cmd.c b/drivers/net/wireless/mwifiex/sta_cmd.c new file mode 100644 index 000000000000..795b1eae768d --- /dev/null +++ b/drivers/net/wireless/mwifiex/sta_cmd.c | |||
@@ -0,0 +1,1226 @@ | |||
1 | /* | ||
2 | * Marvell Wireless LAN device driver: station command handling | ||
3 | * | ||
4 | * Copyright (C) 2011, Marvell International Ltd. | ||
5 | * | ||
6 | * This software file (the "File") is distributed by Marvell International | ||
7 | * Ltd. under the terms of the GNU General Public License Version 2, June 1991 | ||
8 | * (the "License"). You may use, redistribute and/or modify this File in | ||
9 | * accordance with the terms and conditions of the License, a copy of which | ||
10 | * is available by writing to the Free Software Foundation, Inc., | ||
11 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the | ||
12 | * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. | ||
13 | * | ||
14 | * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE | ||
15 | * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE | ||
16 | * ARE EXPRESSLY DISCLAIMED. The License provides additional details about | ||
17 | * this warranty disclaimer. | ||
18 | */ | ||
19 | |||
20 | #include "decl.h" | ||
21 | #include "ioctl.h" | ||
22 | #include "util.h" | ||
23 | #include "fw.h" | ||
24 | #include "main.h" | ||
25 | #include "wmm.h" | ||
26 | #include "11n.h" | ||
27 | |||
28 | /* | ||
29 | * This function prepares command to set/get RSSI information. | ||
30 | * | ||
31 | * Preparation includes - | ||
32 | * - Setting command ID, action and proper size | ||
33 | * - Setting data/beacon average factors | ||
34 | * - Resetting SNR/NF/RSSI values in private structure | ||
35 | * - Ensuring correct endian-ness | ||
36 | */ | ||
37 | static int | ||
38 | mwifiex_cmd_802_11_rssi_info(struct mwifiex_private *priv, | ||
39 | struct host_cmd_ds_command *cmd, u16 cmd_action) | ||
40 | { | ||
41 | cmd->command = cpu_to_le16(HostCmd_CMD_RSSI_INFO); | ||
42 | cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_802_11_rssi_info) + | ||
43 | S_DS_GEN); | ||
44 | cmd->params.rssi_info.action = cpu_to_le16(cmd_action); | ||
45 | cmd->params.rssi_info.ndata = cpu_to_le16(priv->data_avg_factor); | ||
46 | cmd->params.rssi_info.nbcn = cpu_to_le16(priv->bcn_avg_factor); | ||
47 | |||
48 | /* Reset SNR/NF/RSSI values in private structure */ | ||
49 | priv->data_rssi_last = 0; | ||
50 | priv->data_nf_last = 0; | ||
51 | priv->data_rssi_avg = 0; | ||
52 | priv->data_nf_avg = 0; | ||
53 | priv->bcn_rssi_last = 0; | ||
54 | priv->bcn_nf_last = 0; | ||
55 | priv->bcn_rssi_avg = 0; | ||
56 | priv->bcn_nf_avg = 0; | ||
57 | |||
58 | return 0; | ||
59 | } | ||
60 | |||
61 | /* | ||
62 | * This function prepares command to set MAC control. | ||
63 | * | ||
64 | * Preparation includes - | ||
65 | * - Setting command ID, action and proper size | ||
66 | * - Ensuring correct endian-ness | ||
67 | */ | ||
68 | static int mwifiex_cmd_mac_control(struct mwifiex_private *priv, | ||
69 | struct host_cmd_ds_command *cmd, | ||
70 | u16 cmd_action, void *data_buf) | ||
71 | { | ||
72 | struct host_cmd_ds_mac_control *mac_ctrl = &cmd->params.mac_ctrl; | ||
73 | u16 action = *((u16 *) data_buf); | ||
74 | |||
75 | if (cmd_action != HostCmd_ACT_GEN_SET) { | ||
76 | dev_err(priv->adapter->dev, | ||
77 | "mac_control: only support set cmd\n"); | ||
78 | return -1; | ||
79 | } | ||
80 | |||
81 | cmd->command = cpu_to_le16(HostCmd_CMD_MAC_CONTROL); | ||
82 | cmd->size = | ||
83 | cpu_to_le16(sizeof(struct host_cmd_ds_mac_control) + S_DS_GEN); | ||
84 | mac_ctrl->action = cpu_to_le16(action); | ||
85 | |||
86 | return 0; | ||
87 | } | ||
88 | |||
89 | /* | ||
90 | * This function prepares command to set/get SNMP MIB. | ||
91 | * | ||
92 | * Preparation includes - | ||
93 | * - Setting command ID, action and proper size | ||
94 | * - Setting SNMP MIB OID number and value | ||
95 | * (as required) | ||
96 | * - Ensuring correct endian-ness | ||
97 | * | ||
98 | * The following SNMP MIB OIDs are supported - | ||
99 | * - FRAG_THRESH_I : Fragmentation threshold | ||
100 | * - RTS_THRESH_I : RTS threshold | ||
101 | * - SHORT_RETRY_LIM_I : Short retry limit | ||
102 | * - DOT11D_I : 11d support | ||
103 | */ | ||
104 | static int mwifiex_cmd_802_11_snmp_mib(struct mwifiex_private *priv, | ||
105 | struct host_cmd_ds_command *cmd, | ||
106 | u16 cmd_action, u32 cmd_oid, | ||
107 | void *data_buf) | ||
108 | { | ||
109 | struct host_cmd_ds_802_11_snmp_mib *snmp_mib = &cmd->params.smib; | ||
110 | u32 ul_temp; | ||
111 | |||
112 | dev_dbg(priv->adapter->dev, "cmd: SNMP_CMD: cmd_oid = 0x%x\n", cmd_oid); | ||
113 | cmd->command = cpu_to_le16(HostCmd_CMD_802_11_SNMP_MIB); | ||
114 | cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_802_11_snmp_mib) | ||
115 | - 1 + S_DS_GEN); | ||
116 | |||
117 | if (cmd_action == HostCmd_ACT_GEN_GET) { | ||
118 | snmp_mib->query_type = cpu_to_le16(HostCmd_ACT_GEN_GET); | ||
119 | snmp_mib->buf_size = cpu_to_le16(MAX_SNMP_BUF_SIZE); | ||
120 | cmd->size = cpu_to_le16(le16_to_cpu(cmd->size) | ||
121 | + MAX_SNMP_BUF_SIZE); | ||
122 | } | ||
123 | |||
124 | switch (cmd_oid) { | ||
125 | case FRAG_THRESH_I: | ||
126 | snmp_mib->oid = cpu_to_le16((u16) FRAG_THRESH_I); | ||
127 | if (cmd_action == HostCmd_ACT_GEN_SET) { | ||
128 | snmp_mib->query_type = cpu_to_le16(HostCmd_ACT_GEN_SET); | ||
129 | snmp_mib->buf_size = cpu_to_le16(sizeof(u16)); | ||
130 | ul_temp = *((u32 *) data_buf); | ||
131 | *((__le16 *) (snmp_mib->value)) = | ||
132 | cpu_to_le16((u16) ul_temp); | ||
133 | cmd->size = cpu_to_le16(le16_to_cpu(cmd->size) | ||
134 | + sizeof(u16)); | ||
135 | } | ||
136 | break; | ||
137 | case RTS_THRESH_I: | ||
138 | snmp_mib->oid = cpu_to_le16((u16) RTS_THRESH_I); | ||
139 | if (cmd_action == HostCmd_ACT_GEN_SET) { | ||
140 | snmp_mib->query_type = cpu_to_le16(HostCmd_ACT_GEN_SET); | ||
141 | snmp_mib->buf_size = cpu_to_le16(sizeof(u16)); | ||
142 | ul_temp = *((u32 *) data_buf); | ||
143 | *(__le16 *) (snmp_mib->value) = | ||
144 | cpu_to_le16((u16) ul_temp); | ||
145 | cmd->size = cpu_to_le16(le16_to_cpu(cmd->size) | ||
146 | + sizeof(u16)); | ||
147 | } | ||
148 | break; | ||
149 | |||
150 | case SHORT_RETRY_LIM_I: | ||
151 | snmp_mib->oid = cpu_to_le16((u16) SHORT_RETRY_LIM_I); | ||
152 | if (cmd_action == HostCmd_ACT_GEN_SET) { | ||
153 | snmp_mib->query_type = cpu_to_le16(HostCmd_ACT_GEN_SET); | ||
154 | snmp_mib->buf_size = cpu_to_le16(sizeof(u16)); | ||
155 | ul_temp = (*(u32 *) data_buf); | ||
156 | *((__le16 *) (snmp_mib->value)) = | ||
157 | cpu_to_le16((u16) ul_temp); | ||
158 | cmd->size = cpu_to_le16(le16_to_cpu(cmd->size) | ||
159 | + sizeof(u16)); | ||
160 | } | ||
161 | break; | ||
162 | case DOT11D_I: | ||
163 | snmp_mib->oid = cpu_to_le16((u16) DOT11D_I); | ||
164 | if (cmd_action == HostCmd_ACT_GEN_SET) { | ||
165 | snmp_mib->query_type = cpu_to_le16(HostCmd_ACT_GEN_SET); | ||
166 | snmp_mib->buf_size = cpu_to_le16(sizeof(u16)); | ||
167 | ul_temp = *(u32 *) data_buf; | ||
168 | *((__le16 *) (snmp_mib->value)) = | ||
169 | cpu_to_le16((u16) ul_temp); | ||
170 | cmd->size = cpu_to_le16(le16_to_cpu(cmd->size) | ||
171 | + sizeof(u16)); | ||
172 | } | ||
173 | break; | ||
174 | default: | ||
175 | break; | ||
176 | } | ||
177 | dev_dbg(priv->adapter->dev, | ||
178 | "cmd: SNMP_CMD: Action=0x%x, OID=0x%x, OIDSize=0x%x," | ||
179 | " Value=0x%x\n", | ||
180 | cmd_action, cmd_oid, le16_to_cpu(snmp_mib->buf_size), | ||
181 | le16_to_cpu(*(__le16 *) snmp_mib->value)); | ||
182 | return 0; | ||
183 | } | ||
184 | |||
185 | /* | ||
186 | * This function prepares command to get log. | ||
187 | * | ||
188 | * Preparation includes - | ||
189 | * - Setting command ID and proper size | ||
190 | * - Ensuring correct endian-ness | ||
191 | */ | ||
192 | static int | ||
193 | mwifiex_cmd_802_11_get_log(struct mwifiex_private *priv, | ||
194 | struct host_cmd_ds_command *cmd) | ||
195 | { | ||
196 | cmd->command = cpu_to_le16(HostCmd_CMD_802_11_GET_LOG); | ||
197 | cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_802_11_get_log) + | ||
198 | S_DS_GEN); | ||
199 | return 0; | ||
200 | } | ||
201 | |||
202 | /* | ||
203 | * This function prepares command to set/get Tx data rate configuration. | ||
204 | * | ||
205 | * Preparation includes - | ||
206 | * - Setting command ID, action and proper size | ||
207 | * - Setting configuration index, rate scope and rate drop pattern | ||
208 | * parameters (as required) | ||
209 | * - Ensuring correct endian-ness | ||
210 | */ | ||
211 | static int mwifiex_cmd_tx_rate_cfg(struct mwifiex_private *priv, | ||
212 | struct host_cmd_ds_command *cmd, | ||
213 | u16 cmd_action, void *data_buf) | ||
214 | { | ||
215 | struct host_cmd_ds_tx_rate_cfg *rate_cfg = &cmd->params.tx_rate_cfg; | ||
216 | struct mwifiex_rate_scope *rate_scope; | ||
217 | struct mwifiex_rate_drop_pattern *rate_drop; | ||
218 | u16 *pbitmap_rates = (u16 *) data_buf; | ||
219 | |||
220 | u32 i; | ||
221 | |||
222 | cmd->command = cpu_to_le16(HostCmd_CMD_TX_RATE_CFG); | ||
223 | |||
224 | rate_cfg->action = cpu_to_le16(cmd_action); | ||
225 | rate_cfg->cfg_index = 0; | ||
226 | |||
227 | rate_scope = (struct mwifiex_rate_scope *) ((u8 *) rate_cfg + | ||
228 | sizeof(struct host_cmd_ds_tx_rate_cfg)); | ||
229 | rate_scope->type = cpu_to_le16(TLV_TYPE_RATE_SCOPE); | ||
230 | rate_scope->length = cpu_to_le16(sizeof(struct mwifiex_rate_scope) - | ||
231 | sizeof(struct mwifiex_ie_types_header)); | ||
232 | if (pbitmap_rates != NULL) { | ||
233 | rate_scope->hr_dsss_rate_bitmap = cpu_to_le16(pbitmap_rates[0]); | ||
234 | rate_scope->ofdm_rate_bitmap = cpu_to_le16(pbitmap_rates[1]); | ||
235 | for (i = 0; | ||
236 | i < sizeof(rate_scope->ht_mcs_rate_bitmap) / sizeof(u16); | ||
237 | i++) | ||
238 | rate_scope->ht_mcs_rate_bitmap[i] = | ||
239 | cpu_to_le16(pbitmap_rates[2 + i]); | ||
240 | } else { | ||
241 | rate_scope->hr_dsss_rate_bitmap = | ||
242 | cpu_to_le16(priv->bitmap_rates[0]); | ||
243 | rate_scope->ofdm_rate_bitmap = | ||
244 | cpu_to_le16(priv->bitmap_rates[1]); | ||
245 | for (i = 0; | ||
246 | i < sizeof(rate_scope->ht_mcs_rate_bitmap) / sizeof(u16); | ||
247 | i++) | ||
248 | rate_scope->ht_mcs_rate_bitmap[i] = | ||
249 | cpu_to_le16(priv->bitmap_rates[2 + i]); | ||
250 | } | ||
251 | |||
252 | rate_drop = (struct mwifiex_rate_drop_pattern *) ((u8 *) rate_scope + | ||
253 | sizeof(struct mwifiex_rate_scope)); | ||
254 | rate_drop->type = cpu_to_le16(TLV_TYPE_RATE_DROP_CONTROL); | ||
255 | rate_drop->length = cpu_to_le16(sizeof(rate_drop->rate_drop_mode)); | ||
256 | rate_drop->rate_drop_mode = 0; | ||
257 | |||
258 | cmd->size = | ||
259 | cpu_to_le16(S_DS_GEN + sizeof(struct host_cmd_ds_tx_rate_cfg) + | ||
260 | sizeof(struct mwifiex_rate_scope) + | ||
261 | sizeof(struct mwifiex_rate_drop_pattern)); | ||
262 | |||
263 | return 0; | ||
264 | } | ||
265 | |||
266 | /* | ||
267 | * This function prepares command to set/get Tx power configuration. | ||
268 | * | ||
269 | * Preparation includes - | ||
270 | * - Setting command ID, action and proper size | ||
271 | * - Setting Tx power mode, power group TLV | ||
272 | * (as required) | ||
273 | * - Ensuring correct endian-ness | ||
274 | */ | ||
275 | static int mwifiex_cmd_tx_power_cfg(struct mwifiex_private *priv, | ||
276 | struct host_cmd_ds_command *cmd, | ||
277 | u16 cmd_action, void *data_buf) | ||
278 | { | ||
279 | struct mwifiex_types_power_group *pg_tlv = NULL; | ||
280 | struct host_cmd_ds_txpwr_cfg *txp = NULL; | ||
281 | struct host_cmd_ds_txpwr_cfg *cmd_txp_cfg = &cmd->params.txp_cfg; | ||
282 | |||
283 | cmd->command = cpu_to_le16(HostCmd_CMD_TXPWR_CFG); | ||
284 | cmd->size = | ||
285 | cpu_to_le16(S_DS_GEN + sizeof(struct host_cmd_ds_txpwr_cfg)); | ||
286 | switch (cmd_action) { | ||
287 | case HostCmd_ACT_GEN_SET: | ||
288 | txp = (struct host_cmd_ds_txpwr_cfg *) data_buf; | ||
289 | if (txp->mode) { | ||
290 | pg_tlv = (struct mwifiex_types_power_group | ||
291 | *) ((unsigned long) data_buf + | ||
292 | sizeof(struct host_cmd_ds_txpwr_cfg)); | ||
293 | memmove(cmd_txp_cfg, data_buf, | ||
294 | sizeof(struct host_cmd_ds_txpwr_cfg) + | ||
295 | sizeof(struct mwifiex_types_power_group) + | ||
296 | pg_tlv->length); | ||
297 | |||
298 | pg_tlv = (struct mwifiex_types_power_group *) ((u8 *) | ||
299 | cmd_txp_cfg + | ||
300 | sizeof(struct host_cmd_ds_txpwr_cfg)); | ||
301 | cmd->size = cpu_to_le16(le16_to_cpu(cmd->size) + | ||
302 | sizeof(struct mwifiex_types_power_group) + | ||
303 | pg_tlv->length); | ||
304 | } else { | ||
305 | memmove(cmd_txp_cfg, data_buf, | ||
306 | sizeof(struct host_cmd_ds_txpwr_cfg)); | ||
307 | } | ||
308 | cmd_txp_cfg->action = cpu_to_le16(cmd_action); | ||
309 | break; | ||
310 | case HostCmd_ACT_GEN_GET: | ||
311 | cmd_txp_cfg->action = cpu_to_le16(cmd_action); | ||
312 | break; | ||
313 | } | ||
314 | |||
315 | return 0; | ||
316 | } | ||
317 | |||
318 | /* | ||
319 | * This function prepares command to set Host Sleep configuration. | ||
320 | * | ||
321 | * Preparation includes - | ||
322 | * - Setting command ID and proper size | ||
323 | * - Setting Host Sleep action, conditions, ARP filters | ||
324 | * (as required) | ||
325 | * - Ensuring correct endian-ness | ||
326 | */ | ||
327 | static int mwifiex_cmd_802_11_hs_cfg(struct mwifiex_private *priv, | ||
328 | struct host_cmd_ds_command *cmd, | ||
329 | u16 cmd_action, | ||
330 | struct mwifiex_hs_config_param *data_buf) | ||
331 | { | ||
332 | struct mwifiex_adapter *adapter = priv->adapter; | ||
333 | struct host_cmd_ds_802_11_hs_cfg_enh *hs_cfg = &cmd->params.opt_hs_cfg; | ||
334 | u16 hs_activate = false; | ||
335 | |||
336 | if (data_buf == NULL) | ||
337 | /* New Activate command */ | ||
338 | hs_activate = true; | ||
339 | cmd->command = cpu_to_le16(HostCmd_CMD_802_11_HS_CFG_ENH); | ||
340 | |||
341 | if (!hs_activate && | ||
342 | (data_buf->conditions | ||
343 | != cpu_to_le32(HOST_SLEEP_CFG_CANCEL)) | ||
344 | && ((adapter->arp_filter_size > 0) | ||
345 | && (adapter->arp_filter_size <= ARP_FILTER_MAX_BUF_SIZE))) { | ||
346 | dev_dbg(adapter->dev, | ||
347 | "cmd: Attach %d bytes ArpFilter to HSCfg cmd\n", | ||
348 | adapter->arp_filter_size); | ||
349 | memcpy(((u8 *) hs_cfg) + | ||
350 | sizeof(struct host_cmd_ds_802_11_hs_cfg_enh), | ||
351 | adapter->arp_filter, adapter->arp_filter_size); | ||
352 | cmd->size = cpu_to_le16(adapter->arp_filter_size + | ||
353 | sizeof(struct host_cmd_ds_802_11_hs_cfg_enh) | ||
354 | + S_DS_GEN); | ||
355 | } else { | ||
356 | cmd->size = cpu_to_le16(S_DS_GEN + sizeof(struct | ||
357 | host_cmd_ds_802_11_hs_cfg_enh)); | ||
358 | } | ||
359 | if (hs_activate) { | ||
360 | hs_cfg->action = cpu_to_le16(HS_ACTIVATE); | ||
361 | hs_cfg->params.hs_activate.resp_ctrl = RESP_NEEDED; | ||
362 | } else { | ||
363 | hs_cfg->action = cpu_to_le16(HS_CONFIGURE); | ||
364 | hs_cfg->params.hs_config.conditions = data_buf->conditions; | ||
365 | hs_cfg->params.hs_config.gpio = data_buf->gpio; | ||
366 | hs_cfg->params.hs_config.gap = data_buf->gap; | ||
367 | dev_dbg(adapter->dev, | ||
368 | "cmd: HS_CFG_CMD: condition:0x%x gpio:0x%x gap:0x%x\n", | ||
369 | hs_cfg->params.hs_config.conditions, | ||
370 | hs_cfg->params.hs_config.gpio, | ||
371 | hs_cfg->params.hs_config.gap); | ||
372 | } | ||
373 | |||
374 | return 0; | ||
375 | } | ||
376 | |||
377 | /* | ||
378 | * This function prepares command to set/get MAC address. | ||
379 | * | ||
380 | * Preparation includes - | ||
381 | * - Setting command ID, action and proper size | ||
382 | * - Setting MAC address (for SET only) | ||
383 | * - Ensuring correct endian-ness | ||
384 | */ | ||
385 | static int mwifiex_cmd_802_11_mac_address(struct mwifiex_private *priv, | ||
386 | struct host_cmd_ds_command *cmd, | ||
387 | u16 cmd_action) | ||
388 | { | ||
389 | cmd->command = cpu_to_le16(HostCmd_CMD_802_11_MAC_ADDRESS); | ||
390 | cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_802_11_mac_address) + | ||
391 | S_DS_GEN); | ||
392 | cmd->result = 0; | ||
393 | |||
394 | cmd->params.mac_addr.action = cpu_to_le16(cmd_action); | ||
395 | |||
396 | if (cmd_action == HostCmd_ACT_GEN_SET) | ||
397 | memcpy(cmd->params.mac_addr.mac_addr, priv->curr_addr, | ||
398 | ETH_ALEN); | ||
399 | return 0; | ||
400 | } | ||
401 | |||
402 | /* | ||
403 | * This function prepares command to set MAC multicast address. | ||
404 | * | ||
405 | * Preparation includes - | ||
406 | * - Setting command ID, action and proper size | ||
407 | * - Setting MAC multicast address | ||
408 | * - Ensuring correct endian-ness | ||
409 | */ | ||
410 | static int mwifiex_cmd_mac_multicast_adr(struct mwifiex_private *priv, | ||
411 | struct host_cmd_ds_command *cmd, | ||
412 | u16 cmd_action, void *data_buf) | ||
413 | { | ||
414 | struct mwifiex_multicast_list *mcast_list = | ||
415 | (struct mwifiex_multicast_list *) data_buf; | ||
416 | struct host_cmd_ds_mac_multicast_adr *mcast_addr = &cmd->params.mc_addr; | ||
417 | |||
418 | cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_mac_multicast_adr) + | ||
419 | S_DS_GEN); | ||
420 | cmd->command = cpu_to_le16(HostCmd_CMD_MAC_MULTICAST_ADR); | ||
421 | |||
422 | mcast_addr->action = cpu_to_le16(cmd_action); | ||
423 | mcast_addr->num_of_adrs = | ||
424 | cpu_to_le16((u16) mcast_list->num_multicast_addr); | ||
425 | memcpy(mcast_addr->mac_list, mcast_list->mac_list, | ||
426 | mcast_list->num_multicast_addr * ETH_ALEN); | ||
427 | |||
428 | return 0; | ||
429 | } | ||
430 | |||
431 | /* | ||
432 | * This function prepares command to deauthenticate. | ||
433 | * | ||
434 | * Preparation includes - | ||
435 | * - Setting command ID and proper size | ||
436 | * - Setting AP MAC address and reason code | ||
437 | * - Ensuring correct endian-ness | ||
438 | */ | ||
439 | static int mwifiex_cmd_802_11_deauthenticate(struct mwifiex_private *priv, | ||
440 | struct host_cmd_ds_command *cmd, | ||
441 | void *data_buf) | ||
442 | { | ||
443 | struct host_cmd_ds_802_11_deauthenticate *deauth = &cmd->params.deauth; | ||
444 | |||
445 | cmd->command = cpu_to_le16(HostCmd_CMD_802_11_DEAUTHENTICATE); | ||
446 | cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_802_11_deauthenticate) | ||
447 | + S_DS_GEN); | ||
448 | |||
449 | /* Set AP MAC address */ | ||
450 | memcpy(deauth->mac_addr, (u8 *) data_buf, ETH_ALEN); | ||
451 | |||
452 | dev_dbg(priv->adapter->dev, "cmd: Deauth: %pM\n", deauth->mac_addr); | ||
453 | |||
454 | deauth->reason_code = cpu_to_le16(WLAN_REASON_DEAUTH_LEAVING); | ||
455 | |||
456 | return 0; | ||
457 | } | ||
458 | |||
459 | /* | ||
460 | * This function prepares command to stop Ad-Hoc network. | ||
461 | * | ||
462 | * Preparation includes - | ||
463 | * - Setting command ID and proper size | ||
464 | * - Ensuring correct endian-ness | ||
465 | */ | ||
466 | static int mwifiex_cmd_802_11_ad_hoc_stop(struct mwifiex_private *priv, | ||
467 | struct host_cmd_ds_command *cmd) | ||
468 | { | ||
469 | cmd->command = cpu_to_le16(HostCmd_CMD_802_11_AD_HOC_STOP); | ||
470 | cmd->size = cpu_to_le16(S_DS_GEN); | ||
471 | return 0; | ||
472 | } | ||
473 | |||
474 | /* | ||
475 | * This function sets WEP key(s) to key parameter TLV(s). | ||
476 | * | ||
477 | * Multi-key parameter TLVs are supported, so we can send multiple | ||
478 | * WEP keys in a single buffer. | ||
479 | */ | ||
480 | static int | ||
481 | mwifiex_set_keyparamset_wep(struct mwifiex_private *priv, | ||
482 | struct mwifiex_ie_type_key_param_set *key_param_set, | ||
483 | u16 *key_param_len) | ||
484 | { | ||
485 | int cur_key_param_len = 0; | ||
486 | u8 i; | ||
487 | |||
488 | /* Multi-key_param_set TLV is supported */ | ||
489 | for (i = 0; i < NUM_WEP_KEYS; i++) { | ||
490 | if ((priv->wep_key[i].key_length == WLAN_KEY_LEN_WEP40) || | ||
491 | (priv->wep_key[i].key_length == WLAN_KEY_LEN_WEP104)) { | ||
492 | key_param_set->type = | ||
493 | cpu_to_le16(TLV_TYPE_KEY_MATERIAL); | ||
494 | /* Key_param_set WEP fixed length */ | ||
495 | #define KEYPARAMSET_WEP_FIXED_LEN 8 | ||
496 | key_param_set->length = cpu_to_le16((u16) | ||
497 | (priv->wep_key[i]. | ||
498 | key_length + | ||
499 | KEYPARAMSET_WEP_FIXED_LEN)); | ||
500 | key_param_set->key_type_id = | ||
501 | cpu_to_le16(KEY_TYPE_ID_WEP); | ||
502 | key_param_set->key_info = | ||
503 | cpu_to_le16(KEY_INFO_WEP_ENABLED | | ||
504 | KEY_INFO_WEP_UNICAST | | ||
505 | KEY_INFO_WEP_MCAST); | ||
506 | key_param_set->key_len = | ||
507 | cpu_to_le16(priv->wep_key[i].key_length); | ||
508 | /* Set WEP key index */ | ||
509 | key_param_set->key[0] = i; | ||
510 | /* Set default Tx key flag */ | ||
511 | if (i == | ||
512 | (priv-> | ||
513 | wep_key_curr_index & HostCmd_WEP_KEY_INDEX_MASK)) | ||
514 | key_param_set->key[1] = 1; | ||
515 | else | ||
516 | key_param_set->key[1] = 0; | ||
517 | memmove(&key_param_set->key[2], | ||
518 | priv->wep_key[i].key_material, | ||
519 | priv->wep_key[i].key_length); | ||
520 | |||
521 | cur_key_param_len = priv->wep_key[i].key_length + | ||
522 | KEYPARAMSET_WEP_FIXED_LEN + | ||
523 | sizeof(struct mwifiex_ie_types_header); | ||
524 | *key_param_len += (u16) cur_key_param_len; | ||
525 | key_param_set = | ||
526 | (struct mwifiex_ie_type_key_param_set *) | ||
527 | ((u8 *)key_param_set + | ||
528 | cur_key_param_len); | ||
529 | } else if (!priv->wep_key[i].key_length) { | ||
530 | continue; | ||
531 | } else { | ||
532 | dev_err(priv->adapter->dev, | ||
533 | "key%d Length = %d is incorrect\n", | ||
534 | (i + 1), priv->wep_key[i].key_length); | ||
535 | return -1; | ||
536 | } | ||
537 | } | ||
538 | |||
539 | return 0; | ||
540 | } | ||
541 | |||
542 | /* | ||
543 | * This function prepares command to set/get/reset network key(s). | ||
544 | * | ||
545 | * Preparation includes - | ||
546 | * - Setting command ID, action and proper size | ||
547 | * - Setting WEP keys, WAPI keys or WPA keys along with required | ||
548 | * encryption (TKIP, AES) (as required) | ||
549 | * - Ensuring correct endian-ness | ||
550 | */ | ||
551 | static int mwifiex_cmd_802_11_key_material(struct mwifiex_private *priv, | ||
552 | struct host_cmd_ds_command *cmd, | ||
553 | u16 cmd_action, | ||
554 | u32 cmd_oid, void *data_buf) | ||
555 | { | ||
556 | struct host_cmd_ds_802_11_key_material *key_material = | ||
557 | &cmd->params.key_material; | ||
558 | struct mwifiex_ds_encrypt_key *enc_key = | ||
559 | (struct mwifiex_ds_encrypt_key *) data_buf; | ||
560 | u16 key_param_len = 0; | ||
561 | int ret = 0; | ||
562 | const u8 bc_mac[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; | ||
563 | |||
564 | cmd->command = cpu_to_le16(HostCmd_CMD_802_11_KEY_MATERIAL); | ||
565 | key_material->action = cpu_to_le16(cmd_action); | ||
566 | |||
567 | if (cmd_action == HostCmd_ACT_GEN_GET) { | ||
568 | cmd->size = | ||
569 | cpu_to_le16(sizeof(key_material->action) + S_DS_GEN); | ||
570 | return ret; | ||
571 | } | ||
572 | |||
573 | if (!enc_key) { | ||
574 | memset(&key_material->key_param_set, 0, | ||
575 | (NUM_WEP_KEYS * | ||
576 | sizeof(struct mwifiex_ie_type_key_param_set))); | ||
577 | ret = mwifiex_set_keyparamset_wep(priv, | ||
578 | &key_material->key_param_set, | ||
579 | &key_param_len); | ||
580 | cmd->size = cpu_to_le16(key_param_len + | ||
581 | sizeof(key_material->action) + S_DS_GEN); | ||
582 | return ret; | ||
583 | } else | ||
584 | memset(&key_material->key_param_set, 0, | ||
585 | sizeof(struct mwifiex_ie_type_key_param_set)); | ||
586 | if (enc_key->is_wapi_key) { | ||
587 | dev_dbg(priv->adapter->dev, "info: Set WAPI Key\n"); | ||
588 | key_material->key_param_set.key_type_id = | ||
589 | cpu_to_le16(KEY_TYPE_ID_WAPI); | ||
590 | if (cmd_oid == KEY_INFO_ENABLED) | ||
591 | key_material->key_param_set.key_info = | ||
592 | cpu_to_le16(KEY_INFO_WAPI_ENABLED); | ||
593 | else | ||
594 | key_material->key_param_set.key_info = | ||
595 | cpu_to_le16(!KEY_INFO_WAPI_ENABLED); | ||
596 | |||
597 | key_material->key_param_set.key[0] = enc_key->key_index; | ||
598 | if (!priv->sec_info.wapi_key_on) | ||
599 | key_material->key_param_set.key[1] = 1; | ||
600 | else | ||
601 | /* set 0 when re-key */ | ||
602 | key_material->key_param_set.key[1] = 0; | ||
603 | |||
604 | if (0 != memcmp(enc_key->mac_addr, bc_mac, sizeof(bc_mac))) { | ||
605 | /* WAPI pairwise key: unicast */ | ||
606 | key_material->key_param_set.key_info |= | ||
607 | cpu_to_le16(KEY_INFO_WAPI_UNICAST); | ||
608 | } else { /* WAPI group key: multicast */ | ||
609 | key_material->key_param_set.key_info |= | ||
610 | cpu_to_le16(KEY_INFO_WAPI_MCAST); | ||
611 | priv->sec_info.wapi_key_on = true; | ||
612 | } | ||
613 | |||
614 | key_material->key_param_set.type = | ||
615 | cpu_to_le16(TLV_TYPE_KEY_MATERIAL); | ||
616 | key_material->key_param_set.key_len = | ||
617 | cpu_to_le16(WAPI_KEY_LEN); | ||
618 | memcpy(&key_material->key_param_set.key[2], | ||
619 | enc_key->key_material, enc_key->key_len); | ||
620 | memcpy(&key_material->key_param_set.key[2 + enc_key->key_len], | ||
621 | enc_key->wapi_rxpn, WAPI_RXPN_LEN); | ||
622 | key_material->key_param_set.length = | ||
623 | cpu_to_le16(WAPI_KEY_LEN + KEYPARAMSET_FIXED_LEN); | ||
624 | |||
625 | key_param_len = (WAPI_KEY_LEN + KEYPARAMSET_FIXED_LEN) + | ||
626 | sizeof(struct mwifiex_ie_types_header); | ||
627 | cmd->size = cpu_to_le16(key_param_len + | ||
628 | sizeof(key_material->action) + S_DS_GEN); | ||
629 | return ret; | ||
630 | } | ||
631 | if (enc_key->key_len == WLAN_KEY_LEN_CCMP) { | ||
632 | dev_dbg(priv->adapter->dev, "cmd: WPA_AES\n"); | ||
633 | key_material->key_param_set.key_type_id = | ||
634 | cpu_to_le16(KEY_TYPE_ID_AES); | ||
635 | if (cmd_oid == KEY_INFO_ENABLED) | ||
636 | key_material->key_param_set.key_info = | ||
637 | cpu_to_le16(KEY_INFO_AES_ENABLED); | ||
638 | else | ||
639 | key_material->key_param_set.key_info = | ||
640 | cpu_to_le16(!KEY_INFO_AES_ENABLED); | ||
641 | |||
642 | if (enc_key->key_index & MWIFIEX_KEY_INDEX_UNICAST) | ||
643 | /* AES pairwise key: unicast */ | ||
644 | key_material->key_param_set.key_info |= | ||
645 | cpu_to_le16(KEY_INFO_AES_UNICAST); | ||
646 | else /* AES group key: multicast */ | ||
647 | key_material->key_param_set.key_info |= | ||
648 | cpu_to_le16(KEY_INFO_AES_MCAST); | ||
649 | } else if (enc_key->key_len == WLAN_KEY_LEN_TKIP) { | ||
650 | dev_dbg(priv->adapter->dev, "cmd: WPA_TKIP\n"); | ||
651 | key_material->key_param_set.key_type_id = | ||
652 | cpu_to_le16(KEY_TYPE_ID_TKIP); | ||
653 | key_material->key_param_set.key_info = | ||
654 | cpu_to_le16(KEY_INFO_TKIP_ENABLED); | ||
655 | |||
656 | if (enc_key->key_index & MWIFIEX_KEY_INDEX_UNICAST) | ||
657 | /* TKIP pairwise key: unicast */ | ||
658 | key_material->key_param_set.key_info |= | ||
659 | cpu_to_le16(KEY_INFO_TKIP_UNICAST); | ||
660 | else /* TKIP group key: multicast */ | ||
661 | key_material->key_param_set.key_info |= | ||
662 | cpu_to_le16(KEY_INFO_TKIP_MCAST); | ||
663 | } | ||
664 | |||
665 | if (key_material->key_param_set.key_type_id) { | ||
666 | key_material->key_param_set.type = | ||
667 | cpu_to_le16(TLV_TYPE_KEY_MATERIAL); | ||
668 | key_material->key_param_set.key_len = | ||
669 | cpu_to_le16((u16) enc_key->key_len); | ||
670 | memcpy(key_material->key_param_set.key, enc_key->key_material, | ||
671 | enc_key->key_len); | ||
672 | key_material->key_param_set.length = | ||
673 | cpu_to_le16((u16) enc_key->key_len + | ||
674 | KEYPARAMSET_FIXED_LEN); | ||
675 | |||
676 | key_param_len = (u16) (enc_key->key_len + KEYPARAMSET_FIXED_LEN) | ||
677 | + sizeof(struct mwifiex_ie_types_header); | ||
678 | |||
679 | cmd->size = cpu_to_le16(key_param_len + | ||
680 | sizeof(key_material->action) + S_DS_GEN); | ||
681 | } | ||
682 | |||
683 | return ret; | ||
684 | } | ||
685 | |||
686 | /* | ||
687 | * This function prepares command to set/get 11d domain information. | ||
688 | * | ||
689 | * Preparation includes - | ||
690 | * - Setting command ID, action and proper size | ||
691 | * - Setting domain information fields (for SET only) | ||
692 | * - Ensuring correct endian-ness | ||
693 | */ | ||
694 | static int mwifiex_cmd_802_11d_domain_info(struct mwifiex_private *priv, | ||
695 | struct host_cmd_ds_command *cmd, | ||
696 | u16 cmd_action) | ||
697 | { | ||
698 | struct mwifiex_adapter *adapter = priv->adapter; | ||
699 | struct host_cmd_ds_802_11d_domain_info *domain_info = | ||
700 | &cmd->params.domain_info; | ||
701 | struct mwifiex_ietypes_domain_param_set *domain = | ||
702 | &domain_info->domain; | ||
703 | u8 no_of_triplet = adapter->domain_reg.no_of_triplet; | ||
704 | |||
705 | dev_dbg(adapter->dev, "info: 11D: no_of_triplet=0x%x\n", no_of_triplet); | ||
706 | |||
707 | cmd->command = cpu_to_le16(HostCmd_CMD_802_11D_DOMAIN_INFO); | ||
708 | domain_info->action = cpu_to_le16(cmd_action); | ||
709 | if (cmd_action == HostCmd_ACT_GEN_GET) { | ||
710 | cmd->size = cpu_to_le16(sizeof(domain_info->action) + S_DS_GEN); | ||
711 | return 0; | ||
712 | } | ||
713 | |||
714 | /* Set domain info fields */ | ||
715 | domain->header.type = cpu_to_le16(WLAN_EID_COUNTRY); | ||
716 | memcpy(domain->country_code, adapter->domain_reg.country_code, | ||
717 | sizeof(domain->country_code)); | ||
718 | |||
719 | domain->header.len = cpu_to_le16((no_of_triplet * | ||
720 | sizeof(struct ieee80211_country_ie_triplet)) + | ||
721 | sizeof(domain->country_code)); | ||
722 | |||
723 | if (no_of_triplet) { | ||
724 | memcpy(domain->triplet, adapter->domain_reg.triplet, | ||
725 | no_of_triplet * | ||
726 | sizeof(struct ieee80211_country_ie_triplet)); | ||
727 | |||
728 | cmd->size = cpu_to_le16(sizeof(domain_info->action) + | ||
729 | le16_to_cpu(domain->header.len) + | ||
730 | sizeof(struct mwifiex_ie_types_header) | ||
731 | + S_DS_GEN); | ||
732 | } else { | ||
733 | cmd->size = cpu_to_le16(sizeof(domain_info->action) + S_DS_GEN); | ||
734 | } | ||
735 | |||
736 | return 0; | ||
737 | } | ||
738 | |||
739 | /* | ||
740 | * This function prepares command to set/get RF channel. | ||
741 | * | ||
742 | * Preparation includes - | ||
743 | * - Setting command ID, action and proper size | ||
744 | * - Setting RF type and current RF channel (for SET only) | ||
745 | * - Ensuring correct endian-ness | ||
746 | */ | ||
747 | static int mwifiex_cmd_802_11_rf_channel(struct mwifiex_private *priv, | ||
748 | struct host_cmd_ds_command *cmd, | ||
749 | u16 cmd_action, void *data_buf) | ||
750 | { | ||
751 | struct host_cmd_ds_802_11_rf_channel *rf_chan = | ||
752 | &cmd->params.rf_channel; | ||
753 | uint16_t rf_type = le16_to_cpu(rf_chan->rf_type); | ||
754 | |||
755 | cmd->command = cpu_to_le16(HostCmd_CMD_802_11_RF_CHANNEL); | ||
756 | cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_802_11_rf_channel) | ||
757 | + S_DS_GEN); | ||
758 | |||
759 | if (cmd_action == HostCmd_ACT_GEN_SET) { | ||
760 | if ((priv->adapter->adhoc_start_band & BAND_A) | ||
761 | || (priv->adapter->adhoc_start_band & BAND_AN)) | ||
762 | rf_chan->rf_type = | ||
763 | cpu_to_le16(HostCmd_SCAN_RADIO_TYPE_A); | ||
764 | |||
765 | rf_type = le16_to_cpu(rf_chan->rf_type); | ||
766 | SET_SECONDARYCHAN(rf_type, priv->adapter->chan_offset); | ||
767 | rf_chan->current_channel = cpu_to_le16(*((u16 *) data_buf)); | ||
768 | } | ||
769 | rf_chan->action = cpu_to_le16(cmd_action); | ||
770 | return 0; | ||
771 | } | ||
772 | |||
773 | /* | ||
774 | * This function prepares command to set/get IBSS coalescing status. | ||
775 | * | ||
776 | * Preparation includes - | ||
777 | * - Setting command ID, action and proper size | ||
778 | * - Setting status to enable or disable (for SET only) | ||
779 | * - Ensuring correct endian-ness | ||
780 | */ | ||
781 | static int mwifiex_cmd_ibss_coalescing_status(struct mwifiex_private *priv, | ||
782 | struct host_cmd_ds_command *cmd, | ||
783 | u16 cmd_action, void *data_buf) | ||
784 | { | ||
785 | struct host_cmd_ds_802_11_ibss_status *ibss_coal = | ||
786 | &(cmd->params.ibss_coalescing); | ||
787 | u16 enable = 0; | ||
788 | |||
789 | cmd->command = cpu_to_le16(HostCmd_CMD_802_11_IBSS_COALESCING_STATUS); | ||
790 | cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_802_11_ibss_status) + | ||
791 | S_DS_GEN); | ||
792 | cmd->result = 0; | ||
793 | ibss_coal->action = cpu_to_le16(cmd_action); | ||
794 | |||
795 | switch (cmd_action) { | ||
796 | case HostCmd_ACT_GEN_SET: | ||
797 | if (data_buf != NULL) | ||
798 | enable = *(u16 *) data_buf; | ||
799 | ibss_coal->enable = cpu_to_le16(enable); | ||
800 | break; | ||
801 | |||
802 | /* In other case.. Nothing to do */ | ||
803 | case HostCmd_ACT_GEN_GET: | ||
804 | default: | ||
805 | break; | ||
806 | } | ||
807 | |||
808 | return 0; | ||
809 | } | ||
810 | |||
811 | /* | ||
812 | * This function prepares command to set/get register value. | ||
813 | * | ||
814 | * Preparation includes - | ||
815 | * - Setting command ID, action and proper size | ||
816 | * - Setting register offset (for both GET and SET) and | ||
817 | * register value (for SET only) | ||
818 | * - Ensuring correct endian-ness | ||
819 | * | ||
820 | * The following type of registers can be accessed with this function - | ||
821 | * - MAC register | ||
822 | * - BBP register | ||
823 | * - RF register | ||
824 | * - PMIC register | ||
825 | * - CAU register | ||
826 | * - EEPROM | ||
827 | */ | ||
828 | static int mwifiex_cmd_reg_access(struct host_cmd_ds_command *cmd, | ||
829 | u16 cmd_action, void *data_buf) | ||
830 | { | ||
831 | struct mwifiex_ds_reg_rw *reg_rw; | ||
832 | |||
833 | reg_rw = (struct mwifiex_ds_reg_rw *) data_buf; | ||
834 | switch (le16_to_cpu(cmd->command)) { | ||
835 | case HostCmd_CMD_MAC_REG_ACCESS: | ||
836 | { | ||
837 | struct host_cmd_ds_mac_reg_access *mac_reg; | ||
838 | |||
839 | cmd->size = cpu_to_le16(sizeof(*mac_reg) + S_DS_GEN); | ||
840 | mac_reg = (struct host_cmd_ds_mac_reg_access *) &cmd-> | ||
841 | params.mac_reg; | ||
842 | mac_reg->action = cpu_to_le16(cmd_action); | ||
843 | mac_reg->offset = | ||
844 | cpu_to_le16((u16) le32_to_cpu(reg_rw->offset)); | ||
845 | mac_reg->value = reg_rw->value; | ||
846 | break; | ||
847 | } | ||
848 | case HostCmd_CMD_BBP_REG_ACCESS: | ||
849 | { | ||
850 | struct host_cmd_ds_bbp_reg_access *bbp_reg; | ||
851 | |||
852 | cmd->size = cpu_to_le16(sizeof(*bbp_reg) + S_DS_GEN); | ||
853 | bbp_reg = (struct host_cmd_ds_bbp_reg_access *) &cmd-> | ||
854 | params.bbp_reg; | ||
855 | bbp_reg->action = cpu_to_le16(cmd_action); | ||
856 | bbp_reg->offset = | ||
857 | cpu_to_le16((u16) le32_to_cpu(reg_rw->offset)); | ||
858 | bbp_reg->value = (u8) le32_to_cpu(reg_rw->value); | ||
859 | break; | ||
860 | } | ||
861 | case HostCmd_CMD_RF_REG_ACCESS: | ||
862 | { | ||
863 | struct host_cmd_ds_rf_reg_access *rf_reg; | ||
864 | |||
865 | cmd->size = cpu_to_le16(sizeof(*rf_reg) + S_DS_GEN); | ||
866 | rf_reg = (struct host_cmd_ds_rf_reg_access *) &cmd-> | ||
867 | params.rf_reg; | ||
868 | rf_reg->action = cpu_to_le16(cmd_action); | ||
869 | rf_reg->offset = | ||
870 | cpu_to_le16((u16) le32_to_cpu(reg_rw->offset)); | ||
871 | rf_reg->value = (u8) le32_to_cpu(reg_rw->value); | ||
872 | break; | ||
873 | } | ||
874 | case HostCmd_CMD_PMIC_REG_ACCESS: | ||
875 | { | ||
876 | struct host_cmd_ds_pmic_reg_access *pmic_reg; | ||
877 | |||
878 | cmd->size = cpu_to_le16(sizeof(*pmic_reg) + S_DS_GEN); | ||
879 | pmic_reg = (struct host_cmd_ds_pmic_reg_access *) &cmd-> | ||
880 | params.pmic_reg; | ||
881 | pmic_reg->action = cpu_to_le16(cmd_action); | ||
882 | pmic_reg->offset = | ||
883 | cpu_to_le16((u16) le32_to_cpu(reg_rw->offset)); | ||
884 | pmic_reg->value = (u8) le32_to_cpu(reg_rw->value); | ||
885 | break; | ||
886 | } | ||
887 | case HostCmd_CMD_CAU_REG_ACCESS: | ||
888 | { | ||
889 | struct host_cmd_ds_rf_reg_access *cau_reg; | ||
890 | |||
891 | cmd->size = cpu_to_le16(sizeof(*cau_reg) + S_DS_GEN); | ||
892 | cau_reg = (struct host_cmd_ds_rf_reg_access *) &cmd-> | ||
893 | params.rf_reg; | ||
894 | cau_reg->action = cpu_to_le16(cmd_action); | ||
895 | cau_reg->offset = | ||
896 | cpu_to_le16((u16) le32_to_cpu(reg_rw->offset)); | ||
897 | cau_reg->value = (u8) le32_to_cpu(reg_rw->value); | ||
898 | break; | ||
899 | } | ||
900 | case HostCmd_CMD_802_11_EEPROM_ACCESS: | ||
901 | { | ||
902 | struct mwifiex_ds_read_eeprom *rd_eeprom = | ||
903 | (struct mwifiex_ds_read_eeprom *) data_buf; | ||
904 | struct host_cmd_ds_802_11_eeprom_access *cmd_eeprom = | ||
905 | (struct host_cmd_ds_802_11_eeprom_access *) | ||
906 | &cmd->params.eeprom; | ||
907 | |||
908 | cmd->size = cpu_to_le16(sizeof(*cmd_eeprom) + S_DS_GEN); | ||
909 | cmd_eeprom->action = cpu_to_le16(cmd_action); | ||
910 | cmd_eeprom->offset = rd_eeprom->offset; | ||
911 | cmd_eeprom->byte_count = rd_eeprom->byte_count; | ||
912 | cmd_eeprom->value = 0; | ||
913 | break; | ||
914 | } | ||
915 | default: | ||
916 | return -1; | ||
917 | } | ||
918 | |||
919 | return 0; | ||
920 | } | ||
921 | |||
922 | /* | ||
923 | * This function prepares the commands before sending them to the firmware. | ||
924 | * | ||
925 | * This is a generic function which calls specific command preparation | ||
926 | * routines based upon the command number. | ||
927 | */ | ||
928 | int mwifiex_sta_prepare_cmd(struct mwifiex_private *priv, uint16_t cmd_no, | ||
929 | u16 cmd_action, u32 cmd_oid, | ||
930 | void *data_buf, void *cmd_buf) | ||
931 | { | ||
932 | struct host_cmd_ds_command *cmd_ptr = | ||
933 | (struct host_cmd_ds_command *) cmd_buf; | ||
934 | int ret = 0; | ||
935 | |||
936 | /* Prepare command */ | ||
937 | switch (cmd_no) { | ||
938 | case HostCmd_CMD_GET_HW_SPEC: | ||
939 | ret = mwifiex_cmd_get_hw_spec(priv, cmd_ptr); | ||
940 | break; | ||
941 | case HostCmd_CMD_MAC_CONTROL: | ||
942 | ret = mwifiex_cmd_mac_control(priv, cmd_ptr, cmd_action, | ||
943 | data_buf); | ||
944 | break; | ||
945 | case HostCmd_CMD_802_11_MAC_ADDRESS: | ||
946 | ret = mwifiex_cmd_802_11_mac_address(priv, cmd_ptr, | ||
947 | cmd_action); | ||
948 | break; | ||
949 | case HostCmd_CMD_MAC_MULTICAST_ADR: | ||
950 | ret = mwifiex_cmd_mac_multicast_adr(priv, cmd_ptr, cmd_action, | ||
951 | data_buf); | ||
952 | break; | ||
953 | case HostCmd_CMD_TX_RATE_CFG: | ||
954 | ret = mwifiex_cmd_tx_rate_cfg(priv, cmd_ptr, cmd_action, | ||
955 | data_buf); | ||
956 | break; | ||
957 | case HostCmd_CMD_TXPWR_CFG: | ||
958 | ret = mwifiex_cmd_tx_power_cfg(priv, cmd_ptr, cmd_action, | ||
959 | data_buf); | ||
960 | break; | ||
961 | case HostCmd_CMD_802_11_PS_MODE_ENH: | ||
962 | ret = mwifiex_cmd_enh_power_mode(priv, cmd_ptr, cmd_action, | ||
963 | (uint16_t)cmd_oid, data_buf); | ||
964 | break; | ||
965 | case HostCmd_CMD_802_11_HS_CFG_ENH: | ||
966 | ret = mwifiex_cmd_802_11_hs_cfg(priv, cmd_ptr, cmd_action, | ||
967 | (struct mwifiex_hs_config_param *) data_buf); | ||
968 | break; | ||
969 | case HostCmd_CMD_802_11_SCAN: | ||
970 | ret = mwifiex_cmd_802_11_scan(priv, cmd_ptr, data_buf); | ||
971 | break; | ||
972 | case HostCmd_CMD_802_11_BG_SCAN_QUERY: | ||
973 | ret = mwifiex_cmd_802_11_bg_scan_query(priv, cmd_ptr, | ||
974 | data_buf); | ||
975 | break; | ||
976 | case HostCmd_CMD_802_11_ASSOCIATE: | ||
977 | ret = mwifiex_cmd_802_11_associate(priv, cmd_ptr, data_buf); | ||
978 | break; | ||
979 | case HostCmd_CMD_802_11_DEAUTHENTICATE: | ||
980 | ret = mwifiex_cmd_802_11_deauthenticate(priv, cmd_ptr, | ||
981 | data_buf); | ||
982 | break; | ||
983 | case HostCmd_CMD_802_11_AD_HOC_START: | ||
984 | ret = mwifiex_cmd_802_11_ad_hoc_start(priv, cmd_ptr, | ||
985 | data_buf); | ||
986 | break; | ||
987 | case HostCmd_CMD_802_11_GET_LOG: | ||
988 | ret = mwifiex_cmd_802_11_get_log(priv, cmd_ptr); | ||
989 | break; | ||
990 | case HostCmd_CMD_802_11_AD_HOC_JOIN: | ||
991 | ret = mwifiex_cmd_802_11_ad_hoc_join(priv, cmd_ptr, | ||
992 | data_buf); | ||
993 | break; | ||
994 | case HostCmd_CMD_802_11_AD_HOC_STOP: | ||
995 | ret = mwifiex_cmd_802_11_ad_hoc_stop(priv, cmd_ptr); | ||
996 | break; | ||
997 | case HostCmd_CMD_RSSI_INFO: | ||
998 | ret = mwifiex_cmd_802_11_rssi_info(priv, cmd_ptr, cmd_action); | ||
999 | break; | ||
1000 | case HostCmd_CMD_802_11_SNMP_MIB: | ||
1001 | ret = mwifiex_cmd_802_11_snmp_mib(priv, cmd_ptr, cmd_action, | ||
1002 | cmd_oid, data_buf); | ||
1003 | break; | ||
1004 | case HostCmd_CMD_802_11_TX_RATE_QUERY: | ||
1005 | cmd_ptr->command = | ||
1006 | cpu_to_le16(HostCmd_CMD_802_11_TX_RATE_QUERY); | ||
1007 | cmd_ptr->size = | ||
1008 | cpu_to_le16(sizeof(struct host_cmd_ds_tx_rate_query) + | ||
1009 | S_DS_GEN); | ||
1010 | priv->tx_rate = 0; | ||
1011 | ret = 0; | ||
1012 | break; | ||
1013 | case HostCmd_CMD_VERSION_EXT: | ||
1014 | cmd_ptr->command = cpu_to_le16(cmd_no); | ||
1015 | cmd_ptr->params.verext.version_str_sel = | ||
1016 | (u8) (*((u32 *) data_buf)); | ||
1017 | memcpy(&cmd_ptr->params, data_buf, | ||
1018 | sizeof(struct host_cmd_ds_version_ext)); | ||
1019 | cmd_ptr->size = | ||
1020 | cpu_to_le16(sizeof(struct host_cmd_ds_version_ext) + | ||
1021 | S_DS_GEN); | ||
1022 | ret = 0; | ||
1023 | break; | ||
1024 | case HostCmd_CMD_802_11_RF_CHANNEL: | ||
1025 | ret = mwifiex_cmd_802_11_rf_channel(priv, cmd_ptr, cmd_action, | ||
1026 | data_buf); | ||
1027 | break; | ||
1028 | case HostCmd_CMD_FUNC_INIT: | ||
1029 | if (priv->adapter->hw_status == MWIFIEX_HW_STATUS_RESET) | ||
1030 | priv->adapter->hw_status = MWIFIEX_HW_STATUS_READY; | ||
1031 | cmd_ptr->command = cpu_to_le16(cmd_no); | ||
1032 | cmd_ptr->size = cpu_to_le16(S_DS_GEN); | ||
1033 | break; | ||
1034 | case HostCmd_CMD_FUNC_SHUTDOWN: | ||
1035 | priv->adapter->hw_status = MWIFIEX_HW_STATUS_RESET; | ||
1036 | cmd_ptr->command = cpu_to_le16(cmd_no); | ||
1037 | cmd_ptr->size = cpu_to_le16(S_DS_GEN); | ||
1038 | break; | ||
1039 | case HostCmd_CMD_11N_ADDBA_REQ: | ||
1040 | ret = mwifiex_cmd_11n_addba_req(priv, cmd_ptr, data_buf); | ||
1041 | break; | ||
1042 | case HostCmd_CMD_11N_DELBA: | ||
1043 | ret = mwifiex_cmd_11n_delba(priv, cmd_ptr, data_buf); | ||
1044 | break; | ||
1045 | case HostCmd_CMD_11N_ADDBA_RSP: | ||
1046 | ret = mwifiex_cmd_11n_addba_rsp_gen(priv, cmd_ptr, data_buf); | ||
1047 | break; | ||
1048 | case HostCmd_CMD_802_11_KEY_MATERIAL: | ||
1049 | ret = mwifiex_cmd_802_11_key_material(priv, cmd_ptr, | ||
1050 | cmd_action, cmd_oid, | ||
1051 | data_buf); | ||
1052 | break; | ||
1053 | case HostCmd_CMD_802_11D_DOMAIN_INFO: | ||
1054 | ret = mwifiex_cmd_802_11d_domain_info(priv, cmd_ptr, | ||
1055 | cmd_action); | ||
1056 | break; | ||
1057 | case HostCmd_CMD_RECONFIGURE_TX_BUFF: | ||
1058 | ret = mwifiex_cmd_recfg_tx_buf(priv, cmd_ptr, cmd_action, | ||
1059 | data_buf); | ||
1060 | break; | ||
1061 | case HostCmd_CMD_AMSDU_AGGR_CTRL: | ||
1062 | ret = mwifiex_cmd_amsdu_aggr_ctrl(priv, cmd_ptr, cmd_action, | ||
1063 | data_buf); | ||
1064 | break; | ||
1065 | case HostCmd_CMD_11N_CFG: | ||
1066 | ret = mwifiex_cmd_11n_cfg(priv, cmd_ptr, cmd_action, | ||
1067 | data_buf); | ||
1068 | break; | ||
1069 | case HostCmd_CMD_WMM_GET_STATUS: | ||
1070 | dev_dbg(priv->adapter->dev, | ||
1071 | "cmd: WMM: WMM_GET_STATUS cmd sent\n"); | ||
1072 | cmd_ptr->command = cpu_to_le16(HostCmd_CMD_WMM_GET_STATUS); | ||
1073 | cmd_ptr->size = | ||
1074 | cpu_to_le16(sizeof(struct host_cmd_ds_wmm_get_status) + | ||
1075 | S_DS_GEN); | ||
1076 | ret = 0; | ||
1077 | break; | ||
1078 | case HostCmd_CMD_802_11_IBSS_COALESCING_STATUS: | ||
1079 | ret = mwifiex_cmd_ibss_coalescing_status(priv, cmd_ptr, | ||
1080 | cmd_action, data_buf); | ||
1081 | break; | ||
1082 | case HostCmd_CMD_MAC_REG_ACCESS: | ||
1083 | case HostCmd_CMD_BBP_REG_ACCESS: | ||
1084 | case HostCmd_CMD_RF_REG_ACCESS: | ||
1085 | case HostCmd_CMD_PMIC_REG_ACCESS: | ||
1086 | case HostCmd_CMD_CAU_REG_ACCESS: | ||
1087 | case HostCmd_CMD_802_11_EEPROM_ACCESS: | ||
1088 | ret = mwifiex_cmd_reg_access(cmd_ptr, cmd_action, data_buf); | ||
1089 | break; | ||
1090 | case HostCmd_CMD_SET_BSS_MODE: | ||
1091 | cmd_ptr->command = cpu_to_le16(cmd_no); | ||
1092 | if (priv->bss_mode == MWIFIEX_BSS_MODE_IBSS) | ||
1093 | cmd_ptr->params.bss_mode.con_type = | ||
1094 | CONNECTION_TYPE_ADHOC; | ||
1095 | else if (priv->bss_mode == MWIFIEX_BSS_MODE_INFRA) | ||
1096 | cmd_ptr->params.bss_mode.con_type = | ||
1097 | CONNECTION_TYPE_INFRA; | ||
1098 | cmd_ptr->size = cpu_to_le16(sizeof(struct | ||
1099 | host_cmd_ds_set_bss_mode) + S_DS_GEN); | ||
1100 | ret = 0; | ||
1101 | break; | ||
1102 | default: | ||
1103 | dev_err(priv->adapter->dev, | ||
1104 | "PREP_CMD: unknown cmd- %#x\n", cmd_no); | ||
1105 | ret = -1; | ||
1106 | break; | ||
1107 | } | ||
1108 | return ret; | ||
1109 | } | ||
1110 | |||
1111 | /* | ||
1112 | * This function issues commands to initialize firmware. | ||
1113 | * | ||
1114 | * This is called after firmware download to bring the card to | ||
1115 | * working state. | ||
1116 | * | ||
1117 | * The following commands are issued sequentially - | ||
1118 | * - Function init (for first interface only) | ||
1119 | * - Read MAC address (for first interface only) | ||
1120 | * - Reconfigure Tx buffer size (for first interface only) | ||
1121 | * - Enable auto deep sleep (for first interface only) | ||
1122 | * - Get Tx rate | ||
1123 | * - Get Tx power | ||
1124 | * - Set IBSS coalescing status | ||
1125 | * - Set AMSDU aggregation control | ||
1126 | * - Set 11d control | ||
1127 | * - Set MAC control (this must be the last command to initialize firmware) | ||
1128 | */ | ||
1129 | int mwifiex_sta_init_cmd(struct mwifiex_private *priv, u8 first_sta) | ||
1130 | { | ||
1131 | int ret = 0; | ||
1132 | u16 enable = true; | ||
1133 | struct mwifiex_ds_11n_amsdu_aggr_ctrl amsdu_aggr_ctrl; | ||
1134 | struct mwifiex_ds_auto_ds auto_ds; | ||
1135 | enum state_11d_t state_11d; | ||
1136 | |||
1137 | if (first_sta) { | ||
1138 | |||
1139 | ret = mwifiex_prepare_cmd(priv, HostCmd_CMD_FUNC_INIT, | ||
1140 | HostCmd_ACT_GEN_SET, 0, NULL, NULL); | ||
1141 | if (ret) | ||
1142 | return -1; | ||
1143 | /* Read MAC address from HW */ | ||
1144 | ret = mwifiex_prepare_cmd(priv, HostCmd_CMD_GET_HW_SPEC, | ||
1145 | HostCmd_ACT_GEN_GET, 0, NULL, NULL); | ||
1146 | if (ret) | ||
1147 | return -1; | ||
1148 | |||
1149 | /* Reconfigure tx buf size */ | ||
1150 | ret = mwifiex_prepare_cmd(priv, HostCmd_CMD_RECONFIGURE_TX_BUFF, | ||
1151 | HostCmd_ACT_GEN_SET, 0, NULL, | ||
1152 | &priv->adapter->tx_buf_size); | ||
1153 | if (ret) | ||
1154 | return -1; | ||
1155 | |||
1156 | /* Enable IEEE PS by default */ | ||
1157 | priv->adapter->ps_mode = MWIFIEX_802_11_POWER_MODE_PSP; | ||
1158 | ret = mwifiex_prepare_cmd(priv, HostCmd_CMD_802_11_PS_MODE_ENH, | ||
1159 | EN_AUTO_PS, BITMAP_STA_PS, NULL, | ||
1160 | NULL); | ||
1161 | if (ret) | ||
1162 | return -1; | ||
1163 | } | ||
1164 | |||
1165 | /* get tx rate */ | ||
1166 | ret = mwifiex_prepare_cmd(priv, HostCmd_CMD_TX_RATE_CFG, | ||
1167 | HostCmd_ACT_GEN_GET, 0, NULL, NULL); | ||
1168 | if (ret) | ||
1169 | return -1; | ||
1170 | priv->data_rate = 0; | ||
1171 | |||
1172 | /* get tx power */ | ||
1173 | ret = mwifiex_prepare_cmd(priv, HostCmd_CMD_TXPWR_CFG, | ||
1174 | HostCmd_ACT_GEN_GET, 0, NULL, NULL); | ||
1175 | if (ret) | ||
1176 | return -1; | ||
1177 | |||
1178 | /* set ibss coalescing_status */ | ||
1179 | ret = mwifiex_prepare_cmd(priv, | ||
1180 | HostCmd_CMD_802_11_IBSS_COALESCING_STATUS, | ||
1181 | HostCmd_ACT_GEN_SET, 0, NULL, &enable); | ||
1182 | if (ret) | ||
1183 | return -1; | ||
1184 | |||
1185 | memset(&amsdu_aggr_ctrl, 0, sizeof(amsdu_aggr_ctrl)); | ||
1186 | amsdu_aggr_ctrl.enable = true; | ||
1187 | /* Send request to firmware */ | ||
1188 | ret = mwifiex_prepare_cmd(priv, HostCmd_CMD_AMSDU_AGGR_CTRL, | ||
1189 | HostCmd_ACT_GEN_SET, 0, NULL, | ||
1190 | (void *) &amsdu_aggr_ctrl); | ||
1191 | if (ret) | ||
1192 | return -1; | ||
1193 | /* MAC Control must be the last command in init_fw */ | ||
1194 | /* set MAC Control */ | ||
1195 | ret = mwifiex_prepare_cmd(priv, HostCmd_CMD_MAC_CONTROL, | ||
1196 | HostCmd_ACT_GEN_SET, 0, NULL, | ||
1197 | &priv->curr_pkt_filter); | ||
1198 | if (ret) | ||
1199 | return -1; | ||
1200 | |||
1201 | if (first_sta) { | ||
1202 | /* Enable auto deep sleep */ | ||
1203 | auto_ds.auto_ds = DEEP_SLEEP_ON; | ||
1204 | auto_ds.idle_time = DEEP_SLEEP_IDLE_TIME; | ||
1205 | ret = mwifiex_prepare_cmd(priv, | ||
1206 | HostCmd_CMD_802_11_PS_MODE_ENH, | ||
1207 | EN_AUTO_PS, BITMAP_AUTO_DS, NULL, | ||
1208 | &auto_ds); | ||
1209 | if (ret) | ||
1210 | return -1; | ||
1211 | } | ||
1212 | |||
1213 | /* Send cmd to FW to enable/disable 11D function */ | ||
1214 | state_11d = ENABLE_11D; | ||
1215 | ret = mwifiex_prepare_cmd(priv, HostCmd_CMD_802_11_SNMP_MIB, | ||
1216 | HostCmd_ACT_GEN_SET, DOT11D_I, | ||
1217 | NULL, &state_11d); | ||
1218 | if (ret) | ||
1219 | dev_err(priv->adapter->dev, "11D: failed to enable 11D\n"); | ||
1220 | |||
1221 | /* set last_init_cmd */ | ||
1222 | priv->adapter->last_init_cmd = HostCmd_CMD_802_11_SNMP_MIB; | ||
1223 | ret = -EINPROGRESS; | ||
1224 | |||
1225 | return ret; | ||
1226 | } | ||
diff --git a/drivers/net/wireless/mwifiex/sta_cmdresp.c b/drivers/net/wireless/mwifiex/sta_cmdresp.c new file mode 100644 index 000000000000..ae960ddf2bd4 --- /dev/null +++ b/drivers/net/wireless/mwifiex/sta_cmdresp.c | |||
@@ -0,0 +1,986 @@ | |||
1 | /* | ||
2 | * Marvell Wireless LAN device driver: station command response handling | ||
3 | * | ||
4 | * Copyright (C) 2011, Marvell International Ltd. | ||
5 | * | ||
6 | * This software file (the "File") is distributed by Marvell International | ||
7 | * Ltd. under the terms of the GNU General Public License Version 2, June 1991 | ||
8 | * (the "License"). You may use, redistribute and/or modify this File in | ||
9 | * accordance with the terms and conditions of the License, a copy of which | ||
10 | * is available by writing to the Free Software Foundation, Inc., | ||
11 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the | ||
12 | * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. | ||
13 | * | ||
14 | * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE | ||
15 | * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE | ||
16 | * ARE EXPRESSLY DISCLAIMED. The License provides additional details about | ||
17 | * this warranty disclaimer. | ||
18 | */ | ||
19 | |||
20 | #include "decl.h" | ||
21 | #include "ioctl.h" | ||
22 | #include "util.h" | ||
23 | #include "fw.h" | ||
24 | #include "main.h" | ||
25 | #include "wmm.h" | ||
26 | #include "11n.h" | ||
27 | |||
28 | |||
29 | /* | ||
30 | * This function handles the command response error case. | ||
31 | * | ||
32 | * For scan response error, the function cancels all the pending | ||
33 | * scan commands and generates an event to inform the applications | ||
34 | * of the scan completion. | ||
35 | * | ||
36 | * For Power Save command failure, we do not retry enter PS | ||
37 | * command in case of Ad-hoc mode. | ||
38 | * | ||
39 | * For all other response errors, the current command buffer is freed | ||
40 | * and returned to the free command queue. | ||
41 | */ | ||
42 | static void | ||
43 | mwifiex_process_cmdresp_error(struct mwifiex_private *priv, | ||
44 | struct host_cmd_ds_command *resp, | ||
45 | struct mwifiex_wait_queue *wq_buf) | ||
46 | { | ||
47 | struct cmd_ctrl_node *cmd_node = NULL, *tmp_node = NULL; | ||
48 | struct mwifiex_adapter *adapter = priv->adapter; | ||
49 | unsigned long flags; | ||
50 | |||
51 | dev_err(adapter->dev, "CMD_RESP: cmd %#x error, result=%#x\n", | ||
52 | resp->command, resp->result); | ||
53 | if (wq_buf) | ||
54 | wq_buf->status = MWIFIEX_ERROR_FW_CMDRESP; | ||
55 | |||
56 | switch (le16_to_cpu(resp->command)) { | ||
57 | case HostCmd_CMD_802_11_PS_MODE_ENH: | ||
58 | { | ||
59 | struct host_cmd_ds_802_11_ps_mode_enh *pm = | ||
60 | &resp->params.psmode_enh; | ||
61 | dev_err(adapter->dev, "PS_MODE_ENH cmd failed: " | ||
62 | "result=0x%x action=0x%X\n", | ||
63 | resp->result, le16_to_cpu(pm->action)); | ||
64 | /* We do not re-try enter-ps command in ad-hoc mode. */ | ||
65 | if (le16_to_cpu(pm->action) == EN_AUTO_PS && | ||
66 | (le16_to_cpu(pm->params.auto_ps.ps_bitmap) & | ||
67 | BITMAP_STA_PS) | ||
68 | && priv->bss_mode == MWIFIEX_BSS_MODE_IBSS) | ||
69 | adapter->ps_mode = | ||
70 | MWIFIEX_802_11_POWER_MODE_CAM; | ||
71 | } | ||
72 | break; | ||
73 | case HostCmd_CMD_802_11_SCAN: | ||
74 | /* Cancel all pending scan command */ | ||
75 | spin_lock_irqsave(&adapter->scan_pending_q_lock, flags); | ||
76 | list_for_each_entry_safe(cmd_node, tmp_node, | ||
77 | &adapter->scan_pending_q, list) { | ||
78 | list_del(&cmd_node->list); | ||
79 | spin_unlock_irqrestore(&adapter->scan_pending_q_lock, | ||
80 | flags); | ||
81 | mwifiex_insert_cmd_to_free_q(adapter, cmd_node); | ||
82 | spin_lock_irqsave(&adapter->scan_pending_q_lock, flags); | ||
83 | } | ||
84 | spin_unlock_irqrestore(&adapter->scan_pending_q_lock, flags); | ||
85 | |||
86 | spin_lock_irqsave(&adapter->mwifiex_cmd_lock, flags); | ||
87 | adapter->scan_processing = false; | ||
88 | spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, flags); | ||
89 | if (priv->report_scan_result) | ||
90 | priv->report_scan_result = false; | ||
91 | if (priv->scan_pending_on_block) { | ||
92 | priv->scan_pending_on_block = false; | ||
93 | up(&priv->async_sem); | ||
94 | } | ||
95 | break; | ||
96 | |||
97 | case HostCmd_CMD_MAC_CONTROL: | ||
98 | break; | ||
99 | |||
100 | default: | ||
101 | break; | ||
102 | } | ||
103 | /* Handling errors here */ | ||
104 | mwifiex_insert_cmd_to_free_q(adapter, adapter->curr_cmd); | ||
105 | |||
106 | spin_lock_irqsave(&adapter->mwifiex_cmd_lock, flags); | ||
107 | adapter->curr_cmd = NULL; | ||
108 | spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, flags); | ||
109 | |||
110 | return; | ||
111 | } | ||
112 | |||
113 | /* | ||
114 | * This function handles the command response of get RSSI info. | ||
115 | * | ||
116 | * Handling includes changing the header fields into CPU format | ||
117 | * and saving the following parameters in driver - | ||
118 | * - Last data and beacon RSSI value | ||
119 | * - Average data and beacon RSSI value | ||
120 | * - Last data and beacon NF value | ||
121 | * - Average data and beacon NF value | ||
122 | * | ||
123 | * The parameters are send to the application as well, along with | ||
124 | * calculated SNR values. | ||
125 | */ | ||
126 | static int mwifiex_ret_802_11_rssi_info(struct mwifiex_private *priv, | ||
127 | struct host_cmd_ds_command *resp, | ||
128 | void *data_buf) | ||
129 | { | ||
130 | struct host_cmd_ds_802_11_rssi_info_rsp *rssi_info_rsp = | ||
131 | &resp->params.rssi_info_rsp; | ||
132 | struct mwifiex_ds_get_signal *signal = NULL; | ||
133 | |||
134 | priv->data_rssi_last = le16_to_cpu(rssi_info_rsp->data_rssi_last); | ||
135 | priv->data_nf_last = le16_to_cpu(rssi_info_rsp->data_nf_last); | ||
136 | |||
137 | priv->data_rssi_avg = le16_to_cpu(rssi_info_rsp->data_rssi_avg); | ||
138 | priv->data_nf_avg = le16_to_cpu(rssi_info_rsp->data_nf_avg); | ||
139 | |||
140 | priv->bcn_rssi_last = le16_to_cpu(rssi_info_rsp->bcn_rssi_last); | ||
141 | priv->bcn_nf_last = le16_to_cpu(rssi_info_rsp->bcn_nf_last); | ||
142 | |||
143 | priv->bcn_rssi_avg = le16_to_cpu(rssi_info_rsp->bcn_rssi_avg); | ||
144 | priv->bcn_nf_avg = le16_to_cpu(rssi_info_rsp->bcn_nf_avg); | ||
145 | |||
146 | /* Need to indicate IOCTL complete */ | ||
147 | if (data_buf) { | ||
148 | signal = (struct mwifiex_ds_get_signal *) data_buf; | ||
149 | memset(signal, 0, sizeof(struct mwifiex_ds_get_signal)); | ||
150 | |||
151 | signal->selector = ALL_RSSI_INFO_MASK; | ||
152 | |||
153 | /* RSSI */ | ||
154 | signal->bcn_rssi_last = priv->bcn_rssi_last; | ||
155 | signal->bcn_rssi_avg = priv->bcn_rssi_avg; | ||
156 | signal->data_rssi_last = priv->data_rssi_last; | ||
157 | signal->data_rssi_avg = priv->data_rssi_avg; | ||
158 | |||
159 | /* SNR */ | ||
160 | signal->bcn_snr_last = | ||
161 | CAL_SNR(priv->bcn_rssi_last, priv->bcn_nf_last); | ||
162 | signal->bcn_snr_avg = | ||
163 | CAL_SNR(priv->bcn_rssi_avg, priv->bcn_nf_avg); | ||
164 | signal->data_snr_last = | ||
165 | CAL_SNR(priv->data_rssi_last, priv->data_nf_last); | ||
166 | signal->data_snr_avg = | ||
167 | CAL_SNR(priv->data_rssi_avg, priv->data_nf_avg); | ||
168 | |||
169 | /* NF */ | ||
170 | signal->bcn_nf_last = priv->bcn_nf_last; | ||
171 | signal->bcn_nf_avg = priv->bcn_nf_avg; | ||
172 | signal->data_nf_last = priv->data_nf_last; | ||
173 | signal->data_nf_avg = priv->data_nf_avg; | ||
174 | } | ||
175 | |||
176 | return 0; | ||
177 | } | ||
178 | |||
179 | /* | ||
180 | * This function handles the command response of set/get SNMP | ||
181 | * MIB parameters. | ||
182 | * | ||
183 | * Handling includes changing the header fields into CPU format | ||
184 | * and saving the parameter in driver. | ||
185 | * | ||
186 | * The following parameters are supported - | ||
187 | * - Fragmentation threshold | ||
188 | * - RTS threshold | ||
189 | * - Short retry limit | ||
190 | */ | ||
191 | static int mwifiex_ret_802_11_snmp_mib(struct mwifiex_private *priv, | ||
192 | struct host_cmd_ds_command *resp, | ||
193 | void *data_buf) | ||
194 | { | ||
195 | struct host_cmd_ds_802_11_snmp_mib *smib = &resp->params.smib; | ||
196 | u16 oid = le16_to_cpu(smib->oid); | ||
197 | u16 query_type = le16_to_cpu(smib->query_type); | ||
198 | u32 ul_temp; | ||
199 | |||
200 | dev_dbg(priv->adapter->dev, "info: SNMP_RESP: oid value = %#x," | ||
201 | " query_type = %#x, buf size = %#x\n", | ||
202 | oid, query_type, le16_to_cpu(smib->buf_size)); | ||
203 | if (query_type == HostCmd_ACT_GEN_GET) { | ||
204 | ul_temp = le16_to_cpu(*((__le16 *) (smib->value))); | ||
205 | if (data_buf) | ||
206 | *(u32 *)data_buf = ul_temp; | ||
207 | switch (oid) { | ||
208 | case FRAG_THRESH_I: | ||
209 | dev_dbg(priv->adapter->dev, | ||
210 | "info: SNMP_RESP: FragThsd =%u\n", ul_temp); | ||
211 | break; | ||
212 | case RTS_THRESH_I: | ||
213 | dev_dbg(priv->adapter->dev, | ||
214 | "info: SNMP_RESP: RTSThsd =%u\n", ul_temp); | ||
215 | break; | ||
216 | case SHORT_RETRY_LIM_I: | ||
217 | dev_dbg(priv->adapter->dev, | ||
218 | "info: SNMP_RESP: TxRetryCount=%u\n", ul_temp); | ||
219 | break; | ||
220 | default: | ||
221 | break; | ||
222 | } | ||
223 | } | ||
224 | |||
225 | return 0; | ||
226 | } | ||
227 | |||
228 | /* | ||
229 | * This function handles the command response of get log request | ||
230 | * | ||
231 | * Handling includes changing the header fields into CPU format | ||
232 | * and sending the received parameters to application. | ||
233 | */ | ||
234 | static int mwifiex_ret_get_log(struct mwifiex_private *priv, | ||
235 | struct host_cmd_ds_command *resp, | ||
236 | void *data_buf) | ||
237 | { | ||
238 | struct host_cmd_ds_802_11_get_log *get_log = | ||
239 | (struct host_cmd_ds_802_11_get_log *) &resp->params.get_log; | ||
240 | struct mwifiex_ds_get_stats *stats = NULL; | ||
241 | |||
242 | if (data_buf) { | ||
243 | stats = (struct mwifiex_ds_get_stats *) data_buf; | ||
244 | stats->mcast_tx_frame = le32_to_cpu(get_log->mcast_tx_frame); | ||
245 | stats->failed = le32_to_cpu(get_log->failed); | ||
246 | stats->retry = le32_to_cpu(get_log->retry); | ||
247 | stats->multi_retry = le32_to_cpu(get_log->multi_retry); | ||
248 | stats->frame_dup = le32_to_cpu(get_log->frame_dup); | ||
249 | stats->rts_success = le32_to_cpu(get_log->rts_success); | ||
250 | stats->rts_failure = le32_to_cpu(get_log->rts_failure); | ||
251 | stats->ack_failure = le32_to_cpu(get_log->ack_failure); | ||
252 | stats->rx_frag = le32_to_cpu(get_log->rx_frag); | ||
253 | stats->mcast_rx_frame = le32_to_cpu(get_log->mcast_rx_frame); | ||
254 | stats->fcs_error = le32_to_cpu(get_log->fcs_error); | ||
255 | stats->tx_frame = le32_to_cpu(get_log->tx_frame); | ||
256 | stats->wep_icv_error[0] = | ||
257 | le32_to_cpu(get_log->wep_icv_err_cnt[0]); | ||
258 | stats->wep_icv_error[1] = | ||
259 | le32_to_cpu(get_log->wep_icv_err_cnt[1]); | ||
260 | stats->wep_icv_error[2] = | ||
261 | le32_to_cpu(get_log->wep_icv_err_cnt[2]); | ||
262 | stats->wep_icv_error[3] = | ||
263 | le32_to_cpu(get_log->wep_icv_err_cnt[3]); | ||
264 | } | ||
265 | |||
266 | return 0; | ||
267 | } | ||
268 | |||
269 | /* | ||
270 | * This function handles the command response of set/get Tx rate | ||
271 | * configurations. | ||
272 | * | ||
273 | * Handling includes changing the header fields into CPU format | ||
274 | * and saving the following parameters in driver - | ||
275 | * - DSSS rate bitmap | ||
276 | * - OFDM rate bitmap | ||
277 | * - HT MCS rate bitmaps | ||
278 | * | ||
279 | * Based on the new rate bitmaps, the function re-evaluates if | ||
280 | * auto data rate has been activated. If not, it sends another | ||
281 | * query to the firmware to get the current Tx data rate and updates | ||
282 | * the driver value. | ||
283 | */ | ||
284 | static int mwifiex_ret_tx_rate_cfg(struct mwifiex_private *priv, | ||
285 | struct host_cmd_ds_command *resp, | ||
286 | void *data_buf) | ||
287 | { | ||
288 | struct mwifiex_adapter *adapter = priv->adapter; | ||
289 | struct mwifiex_rate_cfg *ds_rate = NULL; | ||
290 | struct host_cmd_ds_tx_rate_cfg *rate_cfg = &resp->params.tx_rate_cfg; | ||
291 | struct mwifiex_rate_scope *rate_scope; | ||
292 | struct mwifiex_ie_types_header *head = NULL; | ||
293 | u16 tlv, tlv_buf_len; | ||
294 | u8 *tlv_buf; | ||
295 | u32 i; | ||
296 | int ret = 0; | ||
297 | |||
298 | tlv_buf = (u8 *) ((u8 *) rate_cfg) + | ||
299 | sizeof(struct host_cmd_ds_tx_rate_cfg); | ||
300 | tlv_buf_len = *(u16 *) (tlv_buf + sizeof(u16)); | ||
301 | |||
302 | while (tlv_buf && tlv_buf_len > 0) { | ||
303 | tlv = (*tlv_buf); | ||
304 | tlv = tlv | (*(tlv_buf + 1) << 8); | ||
305 | |||
306 | switch (tlv) { | ||
307 | case TLV_TYPE_RATE_SCOPE: | ||
308 | rate_scope = (struct mwifiex_rate_scope *) tlv_buf; | ||
309 | priv->bitmap_rates[0] = | ||
310 | le16_to_cpu(rate_scope->hr_dsss_rate_bitmap); | ||
311 | priv->bitmap_rates[1] = | ||
312 | le16_to_cpu(rate_scope->ofdm_rate_bitmap); | ||
313 | for (i = 0; | ||
314 | i < | ||
315 | sizeof(rate_scope->ht_mcs_rate_bitmap) / | ||
316 | sizeof(u16); i++) | ||
317 | priv->bitmap_rates[2 + i] = | ||
318 | le16_to_cpu(rate_scope-> | ||
319 | ht_mcs_rate_bitmap[i]); | ||
320 | break; | ||
321 | /* Add RATE_DROP tlv here */ | ||
322 | } | ||
323 | |||
324 | head = (struct mwifiex_ie_types_header *) tlv_buf; | ||
325 | tlv_buf += le16_to_cpu(head->len) + sizeof(*head); | ||
326 | tlv_buf_len -= le16_to_cpu(head->len); | ||
327 | } | ||
328 | |||
329 | priv->is_data_rate_auto = mwifiex_is_rate_auto(priv); | ||
330 | |||
331 | if (priv->is_data_rate_auto) | ||
332 | priv->data_rate = 0; | ||
333 | else | ||
334 | ret = mwifiex_prepare_cmd(priv, | ||
335 | HostCmd_CMD_802_11_TX_RATE_QUERY, | ||
336 | HostCmd_ACT_GEN_GET, 0, NULL, NULL); | ||
337 | |||
338 | if (data_buf) { | ||
339 | ds_rate = (struct mwifiex_rate_cfg *) data_buf; | ||
340 | if (le16_to_cpu(rate_cfg->action) == HostCmd_ACT_GEN_GET) { | ||
341 | if (priv->is_data_rate_auto) { | ||
342 | ds_rate->is_rate_auto = 1; | ||
343 | } else { | ||
344 | ds_rate->rate = | ||
345 | mwifiex_get_rate_index(adapter, | ||
346 | priv-> | ||
347 | bitmap_rates, | ||
348 | sizeof(priv-> | ||
349 | bitmap_rates)); | ||
350 | if (ds_rate->rate >= | ||
351 | MWIFIEX_RATE_BITMAP_OFDM0 | ||
352 | && ds_rate->rate <= | ||
353 | MWIFIEX_RATE_BITMAP_OFDM7) | ||
354 | ds_rate->rate -= | ||
355 | (MWIFIEX_RATE_BITMAP_OFDM0 - | ||
356 | MWIFIEX_RATE_INDEX_OFDM0); | ||
357 | if (ds_rate->rate >= | ||
358 | MWIFIEX_RATE_BITMAP_MCS0 | ||
359 | && ds_rate->rate <= | ||
360 | MWIFIEX_RATE_BITMAP_MCS127) | ||
361 | ds_rate->rate -= | ||
362 | (MWIFIEX_RATE_BITMAP_MCS0 - | ||
363 | MWIFIEX_RATE_INDEX_MCS0); | ||
364 | } | ||
365 | } | ||
366 | } | ||
367 | |||
368 | return ret; | ||
369 | } | ||
370 | |||
371 | /* | ||
372 | * This function handles the command response of get Tx power level. | ||
373 | * | ||
374 | * Handling includes saving the maximum and minimum Tx power levels | ||
375 | * in driver, as well as sending the values to user. | ||
376 | */ | ||
377 | static int mwifiex_get_power_level(struct mwifiex_private *priv, void *data_buf) | ||
378 | { | ||
379 | int length = -1, max_power = -1, min_power = -1; | ||
380 | struct mwifiex_types_power_group *pg_tlv_hdr = NULL; | ||
381 | struct mwifiex_power_group *pg = NULL; | ||
382 | |||
383 | if (data_buf) { | ||
384 | pg_tlv_hdr = | ||
385 | (struct mwifiex_types_power_group *) ((u8 *) data_buf | ||
386 | + sizeof(struct host_cmd_ds_txpwr_cfg)); | ||
387 | pg = (struct mwifiex_power_group *) ((u8 *) pg_tlv_hdr + | ||
388 | sizeof(struct mwifiex_types_power_group)); | ||
389 | length = pg_tlv_hdr->length; | ||
390 | if (length > 0) { | ||
391 | max_power = pg->power_max; | ||
392 | min_power = pg->power_min; | ||
393 | length -= sizeof(struct mwifiex_power_group); | ||
394 | } | ||
395 | while (length) { | ||
396 | pg++; | ||
397 | if (max_power < pg->power_max) | ||
398 | max_power = pg->power_max; | ||
399 | |||
400 | if (min_power > pg->power_min) | ||
401 | min_power = pg->power_min; | ||
402 | |||
403 | length -= sizeof(struct mwifiex_power_group); | ||
404 | } | ||
405 | if (pg_tlv_hdr->length > 0) { | ||
406 | priv->min_tx_power_level = (u8) min_power; | ||
407 | priv->max_tx_power_level = (u8) max_power; | ||
408 | } | ||
409 | } else { | ||
410 | return -1; | ||
411 | } | ||
412 | |||
413 | return 0; | ||
414 | } | ||
415 | |||
416 | /* | ||
417 | * This function handles the command response of set/get Tx power | ||
418 | * configurations. | ||
419 | * | ||
420 | * Handling includes changing the header fields into CPU format | ||
421 | * and saving the current Tx power level in driver. | ||
422 | */ | ||
423 | static int mwifiex_ret_tx_power_cfg(struct mwifiex_private *priv, | ||
424 | struct host_cmd_ds_command *resp, | ||
425 | void *data_buf) | ||
426 | { | ||
427 | struct mwifiex_adapter *adapter = priv->adapter; | ||
428 | struct host_cmd_ds_txpwr_cfg *txp_cfg = &resp->params.txp_cfg; | ||
429 | struct mwifiex_types_power_group *pg_tlv_hdr = NULL; | ||
430 | struct mwifiex_power_group *pg = NULL; | ||
431 | u16 action = le16_to_cpu(txp_cfg->action); | ||
432 | |||
433 | switch (action) { | ||
434 | case HostCmd_ACT_GEN_GET: | ||
435 | { | ||
436 | pg_tlv_hdr = | ||
437 | (struct mwifiex_types_power_group *) ((u8 *) | ||
438 | txp_cfg + | ||
439 | sizeof | ||
440 | (struct | ||
441 | host_cmd_ds_txpwr_cfg)); | ||
442 | pg = (struct mwifiex_power_group *) ((u8 *) | ||
443 | pg_tlv_hdr + | ||
444 | sizeof(struct | ||
445 | mwifiex_types_power_group)); | ||
446 | if (adapter->hw_status == | ||
447 | MWIFIEX_HW_STATUS_INITIALIZING) | ||
448 | mwifiex_get_power_level(priv, txp_cfg); | ||
449 | priv->tx_power_level = (u16) pg->power_min; | ||
450 | break; | ||
451 | } | ||
452 | case HostCmd_ACT_GEN_SET: | ||
453 | if (le32_to_cpu(txp_cfg->mode)) { | ||
454 | pg_tlv_hdr = | ||
455 | (struct mwifiex_types_power_group *) ((u8 *) | ||
456 | txp_cfg + | ||
457 | sizeof | ||
458 | (struct | ||
459 | host_cmd_ds_txpwr_cfg)); | ||
460 | pg = (struct mwifiex_power_group *) ((u8 *) pg_tlv_hdr | ||
461 | + | ||
462 | sizeof(struct | ||
463 | mwifiex_types_power_group)); | ||
464 | if (pg->power_max == pg->power_min) | ||
465 | priv->tx_power_level = (u16) pg->power_min; | ||
466 | } | ||
467 | break; | ||
468 | default: | ||
469 | dev_err(adapter->dev, "CMD_RESP: unknown cmd action %d\n", | ||
470 | action); | ||
471 | return 0; | ||
472 | } | ||
473 | dev_dbg(adapter->dev, | ||
474 | "info: Current TxPower Level = %d, Max Power=%d, Min Power=%d\n", | ||
475 | priv->tx_power_level, priv->max_tx_power_level, | ||
476 | priv->min_tx_power_level); | ||
477 | |||
478 | return 0; | ||
479 | } | ||
480 | |||
481 | /* | ||
482 | * This function handles the command response of set/get MAC address. | ||
483 | * | ||
484 | * Handling includes saving the MAC address in driver. | ||
485 | */ | ||
486 | static int mwifiex_ret_802_11_mac_address(struct mwifiex_private *priv, | ||
487 | struct host_cmd_ds_command *resp) | ||
488 | { | ||
489 | struct host_cmd_ds_802_11_mac_address *cmd_mac_addr = | ||
490 | &resp->params.mac_addr; | ||
491 | |||
492 | memcpy(priv->curr_addr, cmd_mac_addr->mac_addr, ETH_ALEN); | ||
493 | |||
494 | dev_dbg(priv->adapter->dev, | ||
495 | "info: set mac address: %pM\n", priv->curr_addr); | ||
496 | |||
497 | return 0; | ||
498 | } | ||
499 | |||
500 | /* | ||
501 | * This function handles the command response of set/get MAC multicast | ||
502 | * address. | ||
503 | */ | ||
504 | static int mwifiex_ret_mac_multicast_adr(struct mwifiex_private *priv, | ||
505 | struct host_cmd_ds_command *resp) | ||
506 | { | ||
507 | return 0; | ||
508 | } | ||
509 | |||
510 | /* | ||
511 | * This function handles the command response of get Tx rate query. | ||
512 | * | ||
513 | * Handling includes changing the header fields into CPU format | ||
514 | * and saving the Tx rate and HT information parameters in driver. | ||
515 | * | ||
516 | * Both rate configuration and current data rate can be retrieved | ||
517 | * with this request. | ||
518 | */ | ||
519 | static int mwifiex_ret_802_11_tx_rate_query(struct mwifiex_private *priv, | ||
520 | struct host_cmd_ds_command *resp) | ||
521 | { | ||
522 | struct mwifiex_adapter *adapter = priv->adapter; | ||
523 | |||
524 | priv->tx_rate = resp->params.tx_rate.tx_rate; | ||
525 | priv->tx_htinfo = resp->params.tx_rate.ht_info; | ||
526 | if (!priv->is_data_rate_auto) | ||
527 | priv->data_rate = | ||
528 | mwifiex_index_to_data_rate(adapter, priv->tx_rate, | ||
529 | priv->tx_htinfo); | ||
530 | |||
531 | return 0; | ||
532 | } | ||
533 | |||
534 | /* | ||
535 | * This function handles the command response of a deauthenticate | ||
536 | * command. | ||
537 | * | ||
538 | * If the deauthenticated MAC matches the current BSS MAC, the connection | ||
539 | * state is reset. | ||
540 | */ | ||
541 | static int mwifiex_ret_802_11_deauthenticate(struct mwifiex_private *priv, | ||
542 | struct host_cmd_ds_command *resp) | ||
543 | { | ||
544 | struct mwifiex_adapter *adapter = priv->adapter; | ||
545 | |||
546 | adapter->dbg.num_cmd_deauth++; | ||
547 | if (!memcmp(resp->params.deauth.mac_addr, | ||
548 | &priv->curr_bss_params.bss_descriptor.mac_address, | ||
549 | sizeof(resp->params.deauth.mac_addr))) | ||
550 | mwifiex_reset_connect_state(priv); | ||
551 | |||
552 | return 0; | ||
553 | } | ||
554 | |||
555 | /* | ||
556 | * This function handles the command response of ad-hoc stop. | ||
557 | * | ||
558 | * The function resets the connection state in driver. | ||
559 | */ | ||
560 | static int mwifiex_ret_802_11_ad_hoc_stop(struct mwifiex_private *priv, | ||
561 | struct host_cmd_ds_command *resp) | ||
562 | { | ||
563 | mwifiex_reset_connect_state(priv); | ||
564 | return 0; | ||
565 | } | ||
566 | |||
567 | /* | ||
568 | * This function handles the command response of set/get key material. | ||
569 | * | ||
570 | * Handling includes updating the driver parameters to reflect the | ||
571 | * changes. | ||
572 | */ | ||
573 | static int mwifiex_ret_802_11_key_material(struct mwifiex_private *priv, | ||
574 | struct host_cmd_ds_command *resp) | ||
575 | { | ||
576 | struct host_cmd_ds_802_11_key_material *key = | ||
577 | &resp->params.key_material; | ||
578 | |||
579 | if (le16_to_cpu(key->action) == HostCmd_ACT_GEN_SET) { | ||
580 | if ((le16_to_cpu(key->key_param_set.key_info) & | ||
581 | KEY_INFO_TKIP_MCAST)) { | ||
582 | dev_dbg(priv->adapter->dev, "info: key: GTK is set\n"); | ||
583 | priv->wpa_is_gtk_set = true; | ||
584 | priv->scan_block = false; | ||
585 | } | ||
586 | } | ||
587 | |||
588 | memset(priv->aes_key.key_param_set.key, 0, | ||
589 | sizeof(key->key_param_set.key)); | ||
590 | priv->aes_key.key_param_set.key_len = key->key_param_set.key_len; | ||
591 | memcpy(priv->aes_key.key_param_set.key, key->key_param_set.key, | ||
592 | le16_to_cpu(priv->aes_key.key_param_set.key_len)); | ||
593 | |||
594 | return 0; | ||
595 | } | ||
596 | |||
597 | /* | ||
598 | * This function handles the command response of get 11d domain information. | ||
599 | */ | ||
600 | static int mwifiex_ret_802_11d_domain_info(struct mwifiex_private *priv, | ||
601 | struct host_cmd_ds_command *resp) | ||
602 | { | ||
603 | struct host_cmd_ds_802_11d_domain_info_rsp *domain_info = | ||
604 | &resp->params.domain_info_resp; | ||
605 | struct mwifiex_ietypes_domain_param_set *domain = &domain_info->domain; | ||
606 | u16 action = le16_to_cpu(domain_info->action); | ||
607 | u8 no_of_triplet = 0; | ||
608 | |||
609 | no_of_triplet = (u8) ((le16_to_cpu(domain->header.len) - | ||
610 | IEEE80211_COUNTRY_STRING_LEN) / | ||
611 | sizeof(struct ieee80211_country_ie_triplet)); | ||
612 | |||
613 | dev_dbg(priv->adapter->dev, "info: 11D Domain Info Resp:" | ||
614 | " no_of_triplet=%d\n", no_of_triplet); | ||
615 | |||
616 | if (no_of_triplet > MWIFIEX_MAX_TRIPLET_802_11D) { | ||
617 | dev_warn(priv->adapter->dev, | ||
618 | "11D: invalid number of triplets %d " | ||
619 | "returned!!\n", no_of_triplet); | ||
620 | return -1; | ||
621 | } | ||
622 | |||
623 | switch (action) { | ||
624 | case HostCmd_ACT_GEN_SET: /* Proc Set Action */ | ||
625 | break; | ||
626 | case HostCmd_ACT_GEN_GET: | ||
627 | break; | ||
628 | default: | ||
629 | dev_err(priv->adapter->dev, | ||
630 | "11D: invalid action:%d\n", domain_info->action); | ||
631 | return -1; | ||
632 | } | ||
633 | |||
634 | return 0; | ||
635 | } | ||
636 | |||
637 | /* | ||
638 | * This function handles the command response of get RF channel. | ||
639 | * | ||
640 | * Handling includes changing the header fields into CPU format | ||
641 | * and saving the new channel in driver. | ||
642 | */ | ||
643 | static int mwifiex_ret_802_11_rf_channel(struct mwifiex_private *priv, | ||
644 | struct host_cmd_ds_command *resp, | ||
645 | void *data_buf) | ||
646 | { | ||
647 | struct host_cmd_ds_802_11_rf_channel *rf_channel = | ||
648 | &resp->params.rf_channel; | ||
649 | u16 new_channel = le16_to_cpu(rf_channel->current_channel); | ||
650 | |||
651 | if (priv->curr_bss_params.bss_descriptor.channel != new_channel) { | ||
652 | dev_dbg(priv->adapter->dev, "cmd: Channel Switch: %d to %d\n", | ||
653 | priv->curr_bss_params.bss_descriptor.channel, | ||
654 | new_channel); | ||
655 | /* Update the channel again */ | ||
656 | priv->curr_bss_params.bss_descriptor.channel = new_channel; | ||
657 | } | ||
658 | if (data_buf) | ||
659 | *((u16 *)data_buf) = new_channel; | ||
660 | |||
661 | return 0; | ||
662 | } | ||
663 | |||
664 | /* | ||
665 | * This function handles the command response of get extended version. | ||
666 | * | ||
667 | * Handling includes forming the extended version string and sending it | ||
668 | * to application. | ||
669 | */ | ||
670 | static int mwifiex_ret_ver_ext(struct mwifiex_private *priv, | ||
671 | struct host_cmd_ds_command *resp, | ||
672 | void *data_buf) | ||
673 | { | ||
674 | struct host_cmd_ds_version_ext *ver_ext = &resp->params.verext; | ||
675 | struct host_cmd_ds_version_ext *version_ext = NULL; | ||
676 | |||
677 | if (data_buf) { | ||
678 | version_ext = (struct host_cmd_ds_version_ext *)data_buf; | ||
679 | version_ext->version_str_sel = ver_ext->version_str_sel; | ||
680 | memcpy(version_ext->version_str, ver_ext->version_str, | ||
681 | sizeof(char) * 128); | ||
682 | memcpy(priv->version_str, ver_ext->version_str, 128); | ||
683 | } | ||
684 | return 0; | ||
685 | } | ||
686 | |||
687 | /* | ||
688 | * This function handles the command response of register access. | ||
689 | * | ||
690 | * The register value and offset are returned to the user. For EEPROM | ||
691 | * access, the byte count is also returned. | ||
692 | */ | ||
693 | static int mwifiex_ret_reg_access(u16 type, struct host_cmd_ds_command *resp, | ||
694 | void *data_buf) | ||
695 | { | ||
696 | struct mwifiex_ds_reg_rw *reg_rw = NULL; | ||
697 | struct mwifiex_ds_read_eeprom *eeprom = NULL; | ||
698 | |||
699 | if (data_buf) { | ||
700 | reg_rw = (struct mwifiex_ds_reg_rw *) data_buf; | ||
701 | eeprom = (struct mwifiex_ds_read_eeprom *) data_buf; | ||
702 | switch (type) { | ||
703 | case HostCmd_CMD_MAC_REG_ACCESS: | ||
704 | { | ||
705 | struct host_cmd_ds_mac_reg_access *reg; | ||
706 | reg = (struct host_cmd_ds_mac_reg_access *) | ||
707 | &resp->params.mac_reg; | ||
708 | reg_rw->offset = cpu_to_le32( | ||
709 | (u32) le16_to_cpu(reg->offset)); | ||
710 | reg_rw->value = reg->value; | ||
711 | break; | ||
712 | } | ||
713 | case HostCmd_CMD_BBP_REG_ACCESS: | ||
714 | { | ||
715 | struct host_cmd_ds_bbp_reg_access *reg; | ||
716 | reg = (struct host_cmd_ds_bbp_reg_access *) | ||
717 | &resp->params.bbp_reg; | ||
718 | reg_rw->offset = cpu_to_le32( | ||
719 | (u32) le16_to_cpu(reg->offset)); | ||
720 | reg_rw->value = cpu_to_le32((u32) reg->value); | ||
721 | break; | ||
722 | } | ||
723 | |||
724 | case HostCmd_CMD_RF_REG_ACCESS: | ||
725 | { | ||
726 | struct host_cmd_ds_rf_reg_access *reg; | ||
727 | reg = (struct host_cmd_ds_rf_reg_access *) | ||
728 | &resp->params.rf_reg; | ||
729 | reg_rw->offset = cpu_to_le32( | ||
730 | (u32) le16_to_cpu(reg->offset)); | ||
731 | reg_rw->value = cpu_to_le32((u32) reg->value); | ||
732 | break; | ||
733 | } | ||
734 | case HostCmd_CMD_PMIC_REG_ACCESS: | ||
735 | { | ||
736 | struct host_cmd_ds_pmic_reg_access *reg; | ||
737 | reg = (struct host_cmd_ds_pmic_reg_access *) | ||
738 | &resp->params.pmic_reg; | ||
739 | reg_rw->offset = cpu_to_le32( | ||
740 | (u32) le16_to_cpu(reg->offset)); | ||
741 | reg_rw->value = cpu_to_le32((u32) reg->value); | ||
742 | break; | ||
743 | } | ||
744 | case HostCmd_CMD_CAU_REG_ACCESS: | ||
745 | { | ||
746 | struct host_cmd_ds_rf_reg_access *reg; | ||
747 | reg = (struct host_cmd_ds_rf_reg_access *) | ||
748 | &resp->params.rf_reg; | ||
749 | reg_rw->offset = cpu_to_le32( | ||
750 | (u32) le16_to_cpu(reg->offset)); | ||
751 | reg_rw->value = cpu_to_le32((u32) reg->value); | ||
752 | break; | ||
753 | } | ||
754 | case HostCmd_CMD_802_11_EEPROM_ACCESS: | ||
755 | { | ||
756 | struct host_cmd_ds_802_11_eeprom_access | ||
757 | *cmd_eeprom = | ||
758 | (struct host_cmd_ds_802_11_eeprom_access | ||
759 | *) &resp->params.eeprom; | ||
760 | pr_debug("info: EEPROM read len=%x\n", | ||
761 | cmd_eeprom->byte_count); | ||
762 | if (le16_to_cpu(eeprom->byte_count) < | ||
763 | le16_to_cpu( | ||
764 | cmd_eeprom->byte_count)) { | ||
765 | eeprom->byte_count = cpu_to_le16(0); | ||
766 | pr_debug("info: EEPROM read " | ||
767 | "length is too big\n"); | ||
768 | return -1; | ||
769 | } | ||
770 | eeprom->offset = cmd_eeprom->offset; | ||
771 | eeprom->byte_count = cmd_eeprom->byte_count; | ||
772 | if (le16_to_cpu(eeprom->byte_count) > 0) | ||
773 | memcpy(&eeprom->value, | ||
774 | &cmd_eeprom->value, | ||
775 | le16_to_cpu(eeprom->byte_count)); | ||
776 | |||
777 | break; | ||
778 | } | ||
779 | default: | ||
780 | return -1; | ||
781 | } | ||
782 | } | ||
783 | return 0; | ||
784 | } | ||
785 | |||
786 | /* | ||
787 | * This function handles the command response of get IBSS coalescing status. | ||
788 | * | ||
789 | * If the received BSSID is different than the current one, the current BSSID, | ||
790 | * beacon interval, ATIM window and ERP information are updated, along with | ||
791 | * changing the ad-hoc state accordingly. | ||
792 | */ | ||
793 | static int mwifiex_ret_ibss_coalescing_status(struct mwifiex_private *priv, | ||
794 | struct host_cmd_ds_command *resp) | ||
795 | { | ||
796 | struct host_cmd_ds_802_11_ibss_status *ibss_coal_resp = | ||
797 | &(resp->params.ibss_coalescing); | ||
798 | u8 zero_mac[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 }; | ||
799 | |||
800 | if (le16_to_cpu(ibss_coal_resp->action) == HostCmd_ACT_GEN_SET) | ||
801 | return 0; | ||
802 | |||
803 | dev_dbg(priv->adapter->dev, | ||
804 | "info: new BSSID %pM\n", ibss_coal_resp->bssid); | ||
805 | |||
806 | /* If rsp has NULL BSSID, Just return..... No Action */ | ||
807 | if (!memcmp(ibss_coal_resp->bssid, zero_mac, ETH_ALEN)) { | ||
808 | dev_warn(priv->adapter->dev, "new BSSID is NULL\n"); | ||
809 | return 0; | ||
810 | } | ||
811 | |||
812 | /* If BSSID is diff, modify current BSS parameters */ | ||
813 | if (memcmp(priv->curr_bss_params.bss_descriptor.mac_address, | ||
814 | ibss_coal_resp->bssid, ETH_ALEN)) { | ||
815 | /* BSSID */ | ||
816 | memcpy(priv->curr_bss_params.bss_descriptor.mac_address, | ||
817 | ibss_coal_resp->bssid, ETH_ALEN); | ||
818 | |||
819 | /* Beacon Interval */ | ||
820 | priv->curr_bss_params.bss_descriptor.beacon_period | ||
821 | = le16_to_cpu(ibss_coal_resp->beacon_interval); | ||
822 | |||
823 | /* ERP Information */ | ||
824 | priv->curr_bss_params.bss_descriptor.erp_flags = | ||
825 | (u8) le16_to_cpu(ibss_coal_resp->use_g_rate_protect); | ||
826 | |||
827 | priv->adhoc_state = ADHOC_COALESCED; | ||
828 | } | ||
829 | |||
830 | return 0; | ||
831 | } | ||
832 | |||
833 | /* | ||
834 | * This function handles the command responses. | ||
835 | * | ||
836 | * This is a generic function, which calls command specific | ||
837 | * response handlers based on the command ID. | ||
838 | */ | ||
839 | int mwifiex_process_sta_cmdresp(struct mwifiex_private *priv, | ||
840 | u16 cmdresp_no, void *cmd_buf, void *wq_buf) | ||
841 | { | ||
842 | int ret = 0; | ||
843 | struct mwifiex_adapter *adapter = priv->adapter; | ||
844 | struct host_cmd_ds_command *resp = | ||
845 | (struct host_cmd_ds_command *) cmd_buf; | ||
846 | struct mwifiex_wait_queue *wait_queue = | ||
847 | (struct mwifiex_wait_queue *) wq_buf; | ||
848 | void *data_buf = adapter->curr_cmd->data_buf; | ||
849 | |||
850 | /* If the command is not successful, cleanup and return failure */ | ||
851 | if (resp->result != HostCmd_RESULT_OK) { | ||
852 | mwifiex_process_cmdresp_error(priv, resp, wait_queue); | ||
853 | return -1; | ||
854 | } | ||
855 | /* Command successful, handle response */ | ||
856 | switch (cmdresp_no) { | ||
857 | case HostCmd_CMD_GET_HW_SPEC: | ||
858 | ret = mwifiex_ret_get_hw_spec(priv, resp); | ||
859 | break; | ||
860 | case HostCmd_CMD_MAC_CONTROL: | ||
861 | break; | ||
862 | case HostCmd_CMD_802_11_MAC_ADDRESS: | ||
863 | ret = mwifiex_ret_802_11_mac_address(priv, resp); | ||
864 | break; | ||
865 | case HostCmd_CMD_MAC_MULTICAST_ADR: | ||
866 | ret = mwifiex_ret_mac_multicast_adr(priv, resp); | ||
867 | break; | ||
868 | case HostCmd_CMD_TX_RATE_CFG: | ||
869 | ret = mwifiex_ret_tx_rate_cfg(priv, resp, data_buf); | ||
870 | break; | ||
871 | case HostCmd_CMD_802_11_SCAN: | ||
872 | ret = mwifiex_ret_802_11_scan(priv, resp, wait_queue); | ||
873 | wait_queue = NULL; | ||
874 | adapter->curr_cmd->wq_buf = NULL; | ||
875 | break; | ||
876 | case HostCmd_CMD_802_11_BG_SCAN_QUERY: | ||
877 | ret = mwifiex_ret_802_11_scan(priv, resp, wait_queue); | ||
878 | dev_dbg(adapter->dev, | ||
879 | "info: CMD_RESP: BG_SCAN result is ready!\n"); | ||
880 | break; | ||
881 | case HostCmd_CMD_TXPWR_CFG: | ||
882 | ret = mwifiex_ret_tx_power_cfg(priv, resp, data_buf); | ||
883 | break; | ||
884 | case HostCmd_CMD_802_11_PS_MODE_ENH: | ||
885 | ret = mwifiex_ret_enh_power_mode(priv, resp, data_buf); | ||
886 | break; | ||
887 | case HostCmd_CMD_802_11_HS_CFG_ENH: | ||
888 | ret = mwifiex_ret_802_11_hs_cfg(priv, resp); | ||
889 | break; | ||
890 | case HostCmd_CMD_802_11_ASSOCIATE: | ||
891 | ret = mwifiex_ret_802_11_associate(priv, resp, wait_queue); | ||
892 | break; | ||
893 | case HostCmd_CMD_802_11_DEAUTHENTICATE: | ||
894 | ret = mwifiex_ret_802_11_deauthenticate(priv, resp); | ||
895 | break; | ||
896 | case HostCmd_CMD_802_11_AD_HOC_START: | ||
897 | case HostCmd_CMD_802_11_AD_HOC_JOIN: | ||
898 | ret = mwifiex_ret_802_11_ad_hoc(priv, resp, wait_queue); | ||
899 | break; | ||
900 | case HostCmd_CMD_802_11_AD_HOC_STOP: | ||
901 | ret = mwifiex_ret_802_11_ad_hoc_stop(priv, resp); | ||
902 | break; | ||
903 | case HostCmd_CMD_802_11_GET_LOG: | ||
904 | ret = mwifiex_ret_get_log(priv, resp, data_buf); | ||
905 | break; | ||
906 | case HostCmd_CMD_RSSI_INFO: | ||
907 | ret = mwifiex_ret_802_11_rssi_info(priv, resp, data_buf); | ||
908 | break; | ||
909 | case HostCmd_CMD_802_11_SNMP_MIB: | ||
910 | ret = mwifiex_ret_802_11_snmp_mib(priv, resp, data_buf); | ||
911 | break; | ||
912 | case HostCmd_CMD_802_11_TX_RATE_QUERY: | ||
913 | ret = mwifiex_ret_802_11_tx_rate_query(priv, resp); | ||
914 | break; | ||
915 | case HostCmd_CMD_802_11_RF_CHANNEL: | ||
916 | ret = mwifiex_ret_802_11_rf_channel(priv, resp, data_buf); | ||
917 | break; | ||
918 | case HostCmd_CMD_VERSION_EXT: | ||
919 | ret = mwifiex_ret_ver_ext(priv, resp, data_buf); | ||
920 | break; | ||
921 | case HostCmd_CMD_FUNC_INIT: | ||
922 | case HostCmd_CMD_FUNC_SHUTDOWN: | ||
923 | break; | ||
924 | case HostCmd_CMD_802_11_KEY_MATERIAL: | ||
925 | ret = mwifiex_ret_802_11_key_material(priv, resp); | ||
926 | break; | ||
927 | case HostCmd_CMD_802_11D_DOMAIN_INFO: | ||
928 | ret = mwifiex_ret_802_11d_domain_info(priv, resp); | ||
929 | break; | ||
930 | case HostCmd_CMD_11N_ADDBA_REQ: | ||
931 | ret = mwifiex_ret_11n_addba_req(priv, resp); | ||
932 | break; | ||
933 | case HostCmd_CMD_11N_DELBA: | ||
934 | ret = mwifiex_ret_11n_delba(priv, resp); | ||
935 | break; | ||
936 | case HostCmd_CMD_11N_ADDBA_RSP: | ||
937 | ret = mwifiex_ret_11n_addba_resp(priv, resp); | ||
938 | break; | ||
939 | case HostCmd_CMD_RECONFIGURE_TX_BUFF: | ||
940 | adapter->tx_buf_size = (u16) le16_to_cpu(resp->params. | ||
941 | tx_buf.buff_size); | ||
942 | adapter->tx_buf_size = (adapter->tx_buf_size / | ||
943 | MWIFIEX_SDIO_BLOCK_SIZE) * | ||
944 | MWIFIEX_SDIO_BLOCK_SIZE; | ||
945 | adapter->curr_tx_buf_size = adapter->tx_buf_size; | ||
946 | dev_dbg(adapter->dev, | ||
947 | "cmd: max_tx_buf_size=%d, tx_buf_size=%d\n", | ||
948 | adapter->max_tx_buf_size, adapter->tx_buf_size); | ||
949 | |||
950 | if (adapter->if_ops.update_mp_end_port) | ||
951 | adapter->if_ops.update_mp_end_port(adapter, | ||
952 | le16_to_cpu(resp-> | ||
953 | params. | ||
954 | tx_buf. | ||
955 | mp_end_port)); | ||
956 | break; | ||
957 | case HostCmd_CMD_AMSDU_AGGR_CTRL: | ||
958 | ret = mwifiex_ret_amsdu_aggr_ctrl(priv, resp, data_buf); | ||
959 | break; | ||
960 | case HostCmd_CMD_WMM_GET_STATUS: | ||
961 | ret = mwifiex_ret_wmm_get_status(priv, resp); | ||
962 | break; | ||
963 | case HostCmd_CMD_802_11_IBSS_COALESCING_STATUS: | ||
964 | ret = mwifiex_ret_ibss_coalescing_status(priv, resp); | ||
965 | break; | ||
966 | case HostCmd_CMD_MAC_REG_ACCESS: | ||
967 | case HostCmd_CMD_BBP_REG_ACCESS: | ||
968 | case HostCmd_CMD_RF_REG_ACCESS: | ||
969 | case HostCmd_CMD_PMIC_REG_ACCESS: | ||
970 | case HostCmd_CMD_CAU_REG_ACCESS: | ||
971 | case HostCmd_CMD_802_11_EEPROM_ACCESS: | ||
972 | ret = mwifiex_ret_reg_access(cmdresp_no, resp, data_buf); | ||
973 | break; | ||
974 | case HostCmd_CMD_SET_BSS_MODE: | ||
975 | break; | ||
976 | case HostCmd_CMD_11N_CFG: | ||
977 | ret = mwifiex_ret_11n_cfg(priv, resp, data_buf); | ||
978 | break; | ||
979 | default: | ||
980 | dev_err(adapter->dev, "CMD_RESP: unknown cmd response %#x\n", | ||
981 | resp->command); | ||
982 | break; | ||
983 | } | ||
984 | |||
985 | return ret; | ||
986 | } | ||
diff --git a/drivers/net/wireless/mwifiex/sta_event.c b/drivers/net/wireless/mwifiex/sta_event.c new file mode 100644 index 000000000000..d4a5c1fcefc2 --- /dev/null +++ b/drivers/net/wireless/mwifiex/sta_event.c | |||
@@ -0,0 +1,405 @@ | |||
1 | /* | ||
2 | * Marvell Wireless LAN device driver: station event handling | ||
3 | * | ||
4 | * Copyright (C) 2011, Marvell International Ltd. | ||
5 | * | ||
6 | * This software file (the "File") is distributed by Marvell International | ||
7 | * Ltd. under the terms of the GNU General Public License Version 2, June 1991 | ||
8 | * (the "License"). You may use, redistribute and/or modify this File in | ||
9 | * accordance with the terms and conditions of the License, a copy of which | ||
10 | * is available by writing to the Free Software Foundation, Inc., | ||
11 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the | ||
12 | * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. | ||
13 | * | ||
14 | * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE | ||
15 | * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE | ||
16 | * ARE EXPRESSLY DISCLAIMED. The License provides additional details about | ||
17 | * this warranty disclaimer. | ||
18 | */ | ||
19 | |||
20 | #include "decl.h" | ||
21 | #include "ioctl.h" | ||
22 | #include "util.h" | ||
23 | #include "fw.h" | ||
24 | #include "main.h" | ||
25 | #include "wmm.h" | ||
26 | #include "11n.h" | ||
27 | |||
28 | /* | ||
29 | * This function resets the connection state. | ||
30 | * | ||
31 | * The function is invoked after receiving a disconnect event from firmware, | ||
32 | * and performs the following actions - | ||
33 | * - Set media status to disconnected | ||
34 | * - Clean up Tx and Rx packets | ||
35 | * - Resets SNR/NF/RSSI value in driver | ||
36 | * - Resets security configurations in driver | ||
37 | * - Enables auto data rate | ||
38 | * - Saves the previous SSID and BSSID so that they can | ||
39 | * be used for re-association, if required | ||
40 | * - Erases current SSID and BSSID information | ||
41 | * - Sends a disconnect event to upper layers/applications. | ||
42 | */ | ||
43 | void | ||
44 | mwifiex_reset_connect_state(struct mwifiex_private *priv) | ||
45 | { | ||
46 | struct mwifiex_adapter *adapter = priv->adapter; | ||
47 | |||
48 | if (!priv->media_connected) | ||
49 | return; | ||
50 | |||
51 | dev_dbg(adapter->dev, "info: handles disconnect event\n"); | ||
52 | |||
53 | priv->media_connected = false; | ||
54 | |||
55 | priv->scan_block = false; | ||
56 | |||
57 | /* Free Tx and Rx packets, report disconnect to upper layer */ | ||
58 | mwifiex_clean_txrx(priv); | ||
59 | |||
60 | /* Reset SNR/NF/RSSI values */ | ||
61 | priv->data_rssi_last = 0; | ||
62 | priv->data_nf_last = 0; | ||
63 | priv->data_rssi_avg = 0; | ||
64 | priv->data_nf_avg = 0; | ||
65 | priv->bcn_rssi_last = 0; | ||
66 | priv->bcn_nf_last = 0; | ||
67 | priv->bcn_rssi_avg = 0; | ||
68 | priv->bcn_nf_avg = 0; | ||
69 | priv->rxpd_rate = 0; | ||
70 | priv->rxpd_htinfo = 0; | ||
71 | priv->sec_info.wpa_enabled = false; | ||
72 | priv->sec_info.wpa2_enabled = false; | ||
73 | priv->wpa_ie_len = 0; | ||
74 | |||
75 | priv->sec_info.wapi_enabled = false; | ||
76 | priv->wapi_ie_len = 0; | ||
77 | priv->sec_info.wapi_key_on = false; | ||
78 | |||
79 | priv->sec_info.encryption_mode = MWIFIEX_ENCRYPTION_MODE_NONE; | ||
80 | |||
81 | /* Enable auto data rate */ | ||
82 | priv->is_data_rate_auto = true; | ||
83 | priv->data_rate = 0; | ||
84 | |||
85 | if (priv->bss_mode == MWIFIEX_BSS_MODE_IBSS) { | ||
86 | priv->adhoc_state = ADHOC_IDLE; | ||
87 | priv->adhoc_is_link_sensed = false; | ||
88 | } | ||
89 | |||
90 | /* | ||
91 | * Memorize the previous SSID and BSSID so | ||
92 | * it could be used for re-assoc | ||
93 | */ | ||
94 | |||
95 | dev_dbg(adapter->dev, "info: previous SSID=%s, SSID len=%u\n", | ||
96 | priv->prev_ssid.ssid, priv->prev_ssid.ssid_len); | ||
97 | |||
98 | dev_dbg(adapter->dev, "info: current SSID=%s, SSID len=%u\n", | ||
99 | priv->curr_bss_params.bss_descriptor.ssid.ssid, | ||
100 | priv->curr_bss_params.bss_descriptor.ssid.ssid_len); | ||
101 | |||
102 | memcpy(&priv->prev_ssid, | ||
103 | &priv->curr_bss_params.bss_descriptor.ssid, | ||
104 | sizeof(struct mwifiex_802_11_ssid)); | ||
105 | |||
106 | memcpy(priv->prev_bssid, | ||
107 | priv->curr_bss_params.bss_descriptor.mac_address, ETH_ALEN); | ||
108 | |||
109 | /* Need to erase the current SSID and BSSID info */ | ||
110 | memset(&priv->curr_bss_params, 0x00, sizeof(priv->curr_bss_params)); | ||
111 | |||
112 | adapter->tx_lock_flag = false; | ||
113 | adapter->pps_uapsd_mode = false; | ||
114 | |||
115 | if (adapter->num_cmd_timeout && adapter->curr_cmd) | ||
116 | return; | ||
117 | priv->media_connected = false; | ||
118 | if (!priv->disconnect) { | ||
119 | priv->disconnect = 1; | ||
120 | dev_dbg(adapter->dev, "info: successfully disconnected from" | ||
121 | " %pM: reason code %d\n", priv->cfg_bssid, | ||
122 | WLAN_REASON_DEAUTH_LEAVING); | ||
123 | cfg80211_disconnected(priv->netdev, | ||
124 | WLAN_REASON_DEAUTH_LEAVING, NULL, 0, | ||
125 | GFP_KERNEL); | ||
126 | queue_work(priv->workqueue, &priv->cfg_workqueue); | ||
127 | } | ||
128 | if (!netif_queue_stopped(priv->netdev)) | ||
129 | netif_stop_queue(priv->netdev); | ||
130 | if (netif_carrier_ok(priv->netdev)) | ||
131 | netif_carrier_off(priv->netdev); | ||
132 | /* Reset wireless stats signal info */ | ||
133 | priv->w_stats.qual.level = 0; | ||
134 | priv->w_stats.qual.noise = 0; | ||
135 | } | ||
136 | |||
137 | /* | ||
138 | * This function handles events generated by firmware. | ||
139 | * | ||
140 | * This is a generic function and handles all events. | ||
141 | * | ||
142 | * Event specific routines are called by this function based | ||
143 | * upon the generated event cause. | ||
144 | * | ||
145 | * For the following events, the function just forwards them to upper | ||
146 | * layers, optionally recording the change - | ||
147 | * - EVENT_LINK_SENSED | ||
148 | * - EVENT_MIC_ERR_UNICAST | ||
149 | * - EVENT_MIC_ERR_MULTICAST | ||
150 | * - EVENT_PORT_RELEASE | ||
151 | * - EVENT_RSSI_LOW | ||
152 | * - EVENT_SNR_LOW | ||
153 | * - EVENT_MAX_FAIL | ||
154 | * - EVENT_RSSI_HIGH | ||
155 | * - EVENT_SNR_HIGH | ||
156 | * - EVENT_DATA_RSSI_LOW | ||
157 | * - EVENT_DATA_SNR_LOW | ||
158 | * - EVENT_DATA_RSSI_HIGH | ||
159 | * - EVENT_DATA_SNR_HIGH | ||
160 | * - EVENT_LINK_QUALITY | ||
161 | * - EVENT_PRE_BEACON_LOST | ||
162 | * - EVENT_IBSS_COALESCED | ||
163 | * - EVENT_WEP_ICV_ERR | ||
164 | * - EVENT_BW_CHANGE | ||
165 | * - EVENT_HOSTWAKE_STAIE | ||
166 | * | ||
167 | * For the following events, no action is taken - | ||
168 | * - EVENT_MIB_CHANGED | ||
169 | * - EVENT_INIT_DONE | ||
170 | * - EVENT_DUMMY_HOST_WAKEUP_SIGNAL | ||
171 | * | ||
172 | * Rest of the supported events requires driver handling - | ||
173 | * - EVENT_DEAUTHENTICATED | ||
174 | * - EVENT_DISASSOCIATED | ||
175 | * - EVENT_LINK_LOST | ||
176 | * - EVENT_PS_SLEEP | ||
177 | * - EVENT_PS_AWAKE | ||
178 | * - EVENT_DEEP_SLEEP_AWAKE | ||
179 | * - EVENT_HS_ACT_REQ | ||
180 | * - EVENT_ADHOC_BCN_LOST | ||
181 | * - EVENT_BG_SCAN_REPORT | ||
182 | * - EVENT_WMM_STATUS_CHANGE | ||
183 | * - EVENT_ADDBA | ||
184 | * - EVENT_DELBA | ||
185 | * - EVENT_BA_STREAM_TIEMOUT | ||
186 | * - EVENT_AMSDU_AGGR_CTRL | ||
187 | */ | ||
188 | int mwifiex_process_sta_event(struct mwifiex_private *priv) | ||
189 | { | ||
190 | struct mwifiex_adapter *adapter = priv->adapter; | ||
191 | int ret = 0; | ||
192 | u32 eventcause = adapter->event_cause; | ||
193 | |||
194 | switch (eventcause) { | ||
195 | case EVENT_DUMMY_HOST_WAKEUP_SIGNAL: | ||
196 | dev_err(adapter->dev, "invalid EVENT: DUMMY_HOST_WAKEUP_SIGNAL," | ||
197 | " ignoring it\n"); | ||
198 | break; | ||
199 | case EVENT_LINK_SENSED: | ||
200 | dev_dbg(adapter->dev, "event: LINK_SENSED\n"); | ||
201 | if (!netif_carrier_ok(priv->netdev)) | ||
202 | netif_carrier_on(priv->netdev); | ||
203 | if (netif_queue_stopped(priv->netdev)) | ||
204 | netif_wake_queue(priv->netdev); | ||
205 | break; | ||
206 | |||
207 | case EVENT_DEAUTHENTICATED: | ||
208 | dev_dbg(adapter->dev, "event: Deauthenticated\n"); | ||
209 | adapter->dbg.num_event_deauth++; | ||
210 | if (priv->media_connected) | ||
211 | mwifiex_reset_connect_state(priv); | ||
212 | break; | ||
213 | |||
214 | case EVENT_DISASSOCIATED: | ||
215 | dev_dbg(adapter->dev, "event: Disassociated\n"); | ||
216 | adapter->dbg.num_event_disassoc++; | ||
217 | if (priv->media_connected) | ||
218 | mwifiex_reset_connect_state(priv); | ||
219 | break; | ||
220 | |||
221 | case EVENT_LINK_LOST: | ||
222 | dev_dbg(adapter->dev, "event: Link lost\n"); | ||
223 | adapter->dbg.num_event_link_lost++; | ||
224 | if (priv->media_connected) | ||
225 | mwifiex_reset_connect_state(priv); | ||
226 | break; | ||
227 | |||
228 | case EVENT_PS_SLEEP: | ||
229 | dev_dbg(adapter->dev, "info: EVENT: SLEEP\n"); | ||
230 | |||
231 | adapter->ps_state = PS_STATE_PRE_SLEEP; | ||
232 | |||
233 | mwifiex_check_ps_cond(adapter); | ||
234 | break; | ||
235 | |||
236 | case EVENT_PS_AWAKE: | ||
237 | dev_dbg(adapter->dev, "info: EVENT: AWAKE\n"); | ||
238 | if (!adapter->pps_uapsd_mode && | ||
239 | priv->media_connected && | ||
240 | adapter->sleep_period.period) { | ||
241 | adapter->pps_uapsd_mode = true; | ||
242 | dev_dbg(adapter->dev, | ||
243 | "event: PPS/UAPSD mode activated\n"); | ||
244 | } | ||
245 | adapter->tx_lock_flag = false; | ||
246 | if (adapter->pps_uapsd_mode && adapter->gen_null_pkt) { | ||
247 | if (mwifiex_check_last_packet_indication(priv)) { | ||
248 | if (!adapter->data_sent) { | ||
249 | if (!mwifiex_send_null_packet(priv, | ||
250 | MWIFIEX_TxPD_POWER_MGMT_NULL_PACKET | ||
251 | | | ||
252 | MWIFIEX_TxPD_POWER_MGMT_LAST_PACKET)) | ||
253 | adapter->ps_state = | ||
254 | PS_STATE_SLEEP; | ||
255 | return 0; | ||
256 | } | ||
257 | } | ||
258 | } | ||
259 | adapter->ps_state = PS_STATE_AWAKE; | ||
260 | adapter->pm_wakeup_card_req = false; | ||
261 | adapter->pm_wakeup_fw_try = false; | ||
262 | |||
263 | break; | ||
264 | |||
265 | case EVENT_DEEP_SLEEP_AWAKE: | ||
266 | adapter->if_ops.wakeup_complete(adapter); | ||
267 | dev_dbg(adapter->dev, "event: DS_AWAKE\n"); | ||
268 | if (adapter->is_deep_sleep) | ||
269 | adapter->is_deep_sleep = false; | ||
270 | break; | ||
271 | |||
272 | case EVENT_HS_ACT_REQ: | ||
273 | dev_dbg(adapter->dev, "event: HS_ACT_REQ\n"); | ||
274 | ret = mwifiex_prepare_cmd(priv, HostCmd_CMD_802_11_HS_CFG_ENH, | ||
275 | 0, 0, NULL, NULL); | ||
276 | break; | ||
277 | |||
278 | case EVENT_MIC_ERR_UNICAST: | ||
279 | dev_dbg(adapter->dev, "event: UNICAST MIC ERROR\n"); | ||
280 | break; | ||
281 | |||
282 | case EVENT_MIC_ERR_MULTICAST: | ||
283 | dev_dbg(adapter->dev, "event: MULTICAST MIC ERROR\n"); | ||
284 | break; | ||
285 | case EVENT_MIB_CHANGED: | ||
286 | case EVENT_INIT_DONE: | ||
287 | break; | ||
288 | |||
289 | case EVENT_ADHOC_BCN_LOST: | ||
290 | dev_dbg(adapter->dev, "event: ADHOC_BCN_LOST\n"); | ||
291 | priv->adhoc_is_link_sensed = false; | ||
292 | mwifiex_clean_txrx(priv); | ||
293 | if (!netif_queue_stopped(priv->netdev)) | ||
294 | netif_stop_queue(priv->netdev); | ||
295 | if (netif_carrier_ok(priv->netdev)) | ||
296 | netif_carrier_off(priv->netdev); | ||
297 | break; | ||
298 | |||
299 | case EVENT_BG_SCAN_REPORT: | ||
300 | dev_dbg(adapter->dev, "event: BGS_REPORT\n"); | ||
301 | /* Clear the previous scan result */ | ||
302 | memset(adapter->scan_table, 0x00, | ||
303 | sizeof(struct mwifiex_bssdescriptor) * IW_MAX_AP); | ||
304 | adapter->num_in_scan_table = 0; | ||
305 | adapter->bcn_buf_end = adapter->bcn_buf; | ||
306 | ret = mwifiex_prepare_cmd(priv, | ||
307 | HostCmd_CMD_802_11_BG_SCAN_QUERY, | ||
308 | HostCmd_ACT_GEN_GET, 0, NULL, NULL); | ||
309 | break; | ||
310 | |||
311 | case EVENT_PORT_RELEASE: | ||
312 | dev_dbg(adapter->dev, "event: PORT RELEASE\n"); | ||
313 | break; | ||
314 | |||
315 | case EVENT_WMM_STATUS_CHANGE: | ||
316 | dev_dbg(adapter->dev, "event: WMM status changed\n"); | ||
317 | ret = mwifiex_prepare_cmd(priv, HostCmd_CMD_WMM_GET_STATUS, | ||
318 | 0, 0, NULL, NULL); | ||
319 | break; | ||
320 | |||
321 | case EVENT_RSSI_LOW: | ||
322 | dev_dbg(adapter->dev, "event: Beacon RSSI_LOW\n"); | ||
323 | break; | ||
324 | case EVENT_SNR_LOW: | ||
325 | dev_dbg(adapter->dev, "event: Beacon SNR_LOW\n"); | ||
326 | break; | ||
327 | case EVENT_MAX_FAIL: | ||
328 | dev_dbg(adapter->dev, "event: MAX_FAIL\n"); | ||
329 | break; | ||
330 | case EVENT_RSSI_HIGH: | ||
331 | dev_dbg(adapter->dev, "event: Beacon RSSI_HIGH\n"); | ||
332 | break; | ||
333 | case EVENT_SNR_HIGH: | ||
334 | dev_dbg(adapter->dev, "event: Beacon SNR_HIGH\n"); | ||
335 | break; | ||
336 | case EVENT_DATA_RSSI_LOW: | ||
337 | dev_dbg(adapter->dev, "event: Data RSSI_LOW\n"); | ||
338 | break; | ||
339 | case EVENT_DATA_SNR_LOW: | ||
340 | dev_dbg(adapter->dev, "event: Data SNR_LOW\n"); | ||
341 | break; | ||
342 | case EVENT_DATA_RSSI_HIGH: | ||
343 | dev_dbg(adapter->dev, "event: Data RSSI_HIGH\n"); | ||
344 | break; | ||
345 | case EVENT_DATA_SNR_HIGH: | ||
346 | dev_dbg(adapter->dev, "event: Data SNR_HIGH\n"); | ||
347 | break; | ||
348 | case EVENT_LINK_QUALITY: | ||
349 | dev_dbg(adapter->dev, "event: Link Quality\n"); | ||
350 | break; | ||
351 | case EVENT_PRE_BEACON_LOST: | ||
352 | dev_dbg(adapter->dev, "event: Pre-Beacon Lost\n"); | ||
353 | break; | ||
354 | case EVENT_IBSS_COALESCED: | ||
355 | dev_dbg(adapter->dev, "event: IBSS_COALESCED\n"); | ||
356 | ret = mwifiex_prepare_cmd(priv, | ||
357 | HostCmd_CMD_802_11_IBSS_COALESCING_STATUS, | ||
358 | HostCmd_ACT_GEN_GET, 0, NULL, NULL); | ||
359 | break; | ||
360 | case EVENT_ADDBA: | ||
361 | dev_dbg(adapter->dev, "event: ADDBA Request\n"); | ||
362 | mwifiex_prepare_cmd(priv, HostCmd_CMD_11N_ADDBA_RSP, | ||
363 | HostCmd_ACT_GEN_SET, 0, NULL, | ||
364 | adapter->event_body); | ||
365 | break; | ||
366 | case EVENT_DELBA: | ||
367 | dev_dbg(adapter->dev, "event: DELBA Request\n"); | ||
368 | mwifiex_11n_delete_ba_stream(priv, adapter->event_body); | ||
369 | break; | ||
370 | case EVENT_BA_STREAM_TIEMOUT: | ||
371 | dev_dbg(adapter->dev, "event: BA Stream timeout\n"); | ||
372 | mwifiex_11n_ba_stream_timeout(priv, | ||
373 | (struct host_cmd_ds_11n_batimeout | ||
374 | *) | ||
375 | adapter->event_body); | ||
376 | break; | ||
377 | case EVENT_AMSDU_AGGR_CTRL: | ||
378 | dev_dbg(adapter->dev, "event: AMSDU_AGGR_CTRL %d\n", | ||
379 | *(u16 *) adapter->event_body); | ||
380 | adapter->tx_buf_size = | ||
381 | min(adapter->curr_tx_buf_size, | ||
382 | le16_to_cpu(*(__le16 *) adapter->event_body)); | ||
383 | dev_dbg(adapter->dev, "event: tx_buf_size %d\n", | ||
384 | adapter->tx_buf_size); | ||
385 | break; | ||
386 | |||
387 | case EVENT_WEP_ICV_ERR: | ||
388 | dev_dbg(adapter->dev, "event: WEP ICV error\n"); | ||
389 | break; | ||
390 | |||
391 | case EVENT_BW_CHANGE: | ||
392 | dev_dbg(adapter->dev, "event: BW Change\n"); | ||
393 | break; | ||
394 | |||
395 | case EVENT_HOSTWAKE_STAIE: | ||
396 | dev_dbg(adapter->dev, "event: HOSTWAKE_STAIE %d\n", eventcause); | ||
397 | break; | ||
398 | default: | ||
399 | dev_dbg(adapter->dev, "event: unknown event id: %#x\n", | ||
400 | eventcause); | ||
401 | break; | ||
402 | } | ||
403 | |||
404 | return ret; | ||
405 | } | ||
diff --git a/drivers/net/wireless/mwifiex/sta_ioctl.c b/drivers/net/wireless/mwifiex/sta_ioctl.c new file mode 100644 index 000000000000..665a519b1403 --- /dev/null +++ b/drivers/net/wireless/mwifiex/sta_ioctl.c | |||
@@ -0,0 +1,2478 @@ | |||
1 | /* | ||
2 | * Marvell Wireless LAN device driver: functions for station ioctl | ||
3 | * | ||
4 | * Copyright (C) 2011, Marvell International Ltd. | ||
5 | * | ||
6 | * This software file (the "File") is distributed by Marvell International | ||
7 | * Ltd. under the terms of the GNU General Public License Version 2, June 1991 | ||
8 | * (the "License"). You may use, redistribute and/or modify this File in | ||
9 | * accordance with the terms and conditions of the License, a copy of which | ||
10 | * is available by writing to the Free Software Foundation, Inc., | ||
11 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the | ||
12 | * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. | ||
13 | * | ||
14 | * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE | ||
15 | * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE | ||
16 | * ARE EXPRESSLY DISCLAIMED. The License provides additional details about | ||
17 | * this warranty disclaimer. | ||
18 | */ | ||
19 | |||
20 | #include "decl.h" | ||
21 | #include "ioctl.h" | ||
22 | #include "util.h" | ||
23 | #include "fw.h" | ||
24 | #include "main.h" | ||
25 | #include "wmm.h" | ||
26 | #include "11n.h" | ||
27 | #include "cfg80211.h" | ||
28 | |||
29 | /* | ||
30 | * Copies the multicast address list from device to driver. | ||
31 | * | ||
32 | * This function does not validate the destination memory for | ||
33 | * size, and the calling function must ensure enough memory is | ||
34 | * available. | ||
35 | */ | ||
36 | static int | ||
37 | mwifiex_copy_mcast_addr(struct mwifiex_multicast_list *mlist, | ||
38 | struct net_device *dev) | ||
39 | { | ||
40 | int i = 0; | ||
41 | struct netdev_hw_addr *ha; | ||
42 | |||
43 | netdev_for_each_mc_addr(ha, dev) | ||
44 | memcpy(&mlist->mac_list[i++], ha->addr, ETH_ALEN); | ||
45 | |||
46 | return i; | ||
47 | } | ||
48 | |||
49 | /* | ||
50 | * Allocate and fills a wait queue with proper parameters. | ||
51 | * | ||
52 | * This function needs to be called before an IOCTL request can be made. | ||
53 | * It can handle the following wait options: | ||
54 | * MWIFIEX_NO_WAIT - Waiting is disabled | ||
55 | * MWIFIEX_IOCTL_WAIT - Waiting is done on IOCTL wait queue | ||
56 | * MWIFIEX_CMD_WAIT - Waiting is done on command wait queue | ||
57 | * MWIFIEX_WSTATS_WAIT - Waiting is done on stats wait queue | ||
58 | */ | ||
59 | struct mwifiex_wait_queue * | ||
60 | mwifiex_alloc_fill_wait_queue(struct mwifiex_private *priv, | ||
61 | u8 wait_option) | ||
62 | { | ||
63 | struct mwifiex_wait_queue *wait = NULL; | ||
64 | |||
65 | wait = (struct mwifiex_wait_queue *) | ||
66 | kzalloc(sizeof(struct mwifiex_wait_queue), GFP_ATOMIC); | ||
67 | if (!wait) { | ||
68 | dev_err(priv->adapter->dev, "%s: fail to alloc buffer\n", | ||
69 | __func__); | ||
70 | return wait; | ||
71 | } | ||
72 | |||
73 | wait->bss_index = priv->bss_index; | ||
74 | |||
75 | switch (wait_option) { | ||
76 | case MWIFIEX_NO_WAIT: | ||
77 | wait->enabled = 0; | ||
78 | break; | ||
79 | case MWIFIEX_IOCTL_WAIT: | ||
80 | priv->ioctl_wait_q_woken = false; | ||
81 | wait->start_time = jiffies; | ||
82 | wait->wait = &priv->ioctl_wait_q; | ||
83 | wait->condition = &priv->ioctl_wait_q_woken; | ||
84 | wait->enabled = 1; | ||
85 | break; | ||
86 | case MWIFIEX_CMD_WAIT: | ||
87 | priv->cmd_wait_q_woken = false; | ||
88 | wait->start_time = jiffies; | ||
89 | wait->wait = &priv->cmd_wait_q; | ||
90 | wait->condition = &priv->cmd_wait_q_woken; | ||
91 | wait->enabled = 1; | ||
92 | break; | ||
93 | case MWIFIEX_WSTATS_WAIT: | ||
94 | priv->w_stats_wait_q_woken = false; | ||
95 | wait->start_time = jiffies; | ||
96 | wait->wait = &priv->w_stats_wait_q; | ||
97 | wait->condition = &priv->w_stats_wait_q_woken; | ||
98 | wait->enabled = 1; | ||
99 | break; | ||
100 | } | ||
101 | |||
102 | return wait; | ||
103 | } | ||
104 | |||
105 | /* | ||
106 | * Wait queue completion handler. | ||
107 | * | ||
108 | * This function waits on a particular wait queue. | ||
109 | * For NO_WAIT option, it returns immediately. It also cancels the | ||
110 | * pending IOCTL request after waking up, in case of errors. | ||
111 | */ | ||
112 | static void | ||
113 | mwifiex_wait_ioctl_complete(struct mwifiex_private *priv, | ||
114 | struct mwifiex_wait_queue *wait, | ||
115 | u8 wait_option) | ||
116 | { | ||
117 | bool cancel_flag = false; | ||
118 | |||
119 | switch (wait_option) { | ||
120 | case MWIFIEX_NO_WAIT: | ||
121 | break; | ||
122 | case MWIFIEX_IOCTL_WAIT: | ||
123 | wait_event_interruptible(priv->ioctl_wait_q, | ||
124 | priv->ioctl_wait_q_woken); | ||
125 | if (!priv->ioctl_wait_q_woken) | ||
126 | cancel_flag = true; | ||
127 | break; | ||
128 | case MWIFIEX_CMD_WAIT: | ||
129 | wait_event_interruptible(priv->cmd_wait_q, | ||
130 | priv->cmd_wait_q_woken); | ||
131 | if (!priv->cmd_wait_q_woken) | ||
132 | cancel_flag = true; | ||
133 | break; | ||
134 | case MWIFIEX_WSTATS_WAIT: | ||
135 | wait_event_interruptible(priv->w_stats_wait_q, | ||
136 | priv->w_stats_wait_q_woken); | ||
137 | if (!priv->w_stats_wait_q_woken) | ||
138 | cancel_flag = true; | ||
139 | break; | ||
140 | } | ||
141 | if (cancel_flag) { | ||
142 | mwifiex_cancel_pending_ioctl(priv->adapter, wait); | ||
143 | dev_dbg(priv->adapter->dev, "cmd: IOCTL cancel: wait=%p, wait_option=%d\n", | ||
144 | wait, wait_option); | ||
145 | } | ||
146 | |||
147 | return; | ||
148 | } | ||
149 | |||
150 | /* | ||
151 | * The function waits for the request to complete and issues the | ||
152 | * completion handler, if required. | ||
153 | */ | ||
154 | int mwifiex_request_ioctl(struct mwifiex_private *priv, | ||
155 | struct mwifiex_wait_queue *wait, | ||
156 | int status, u8 wait_option) | ||
157 | { | ||
158 | switch (status) { | ||
159 | case -EINPROGRESS: | ||
160 | dev_dbg(priv->adapter->dev, "cmd: IOCTL pending: wait=%p, wait_option=%d\n", | ||
161 | wait, wait_option); | ||
162 | atomic_inc(&priv->adapter->ioctl_pending); | ||
163 | /* Status pending, wake up main process */ | ||
164 | queue_work(priv->adapter->workqueue, &priv->adapter->main_work); | ||
165 | |||
166 | /* Wait for completion */ | ||
167 | if (wait_option) { | ||
168 | mwifiex_wait_ioctl_complete(priv, wait, wait_option); | ||
169 | status = wait->status; | ||
170 | } | ||
171 | break; | ||
172 | case 0: | ||
173 | case -1: | ||
174 | case -EBUSY: | ||
175 | default: | ||
176 | break; | ||
177 | } | ||
178 | return status; | ||
179 | } | ||
180 | EXPORT_SYMBOL_GPL(mwifiex_request_ioctl); | ||
181 | |||
182 | /* | ||
183 | * IOCTL request handler to set/get MAC address. | ||
184 | * | ||
185 | * This function prepares the correct firmware command and | ||
186 | * issues it to get the extended version information. | ||
187 | */ | ||
188 | static int mwifiex_bss_ioctl_mac_address(struct mwifiex_private *priv, | ||
189 | struct mwifiex_wait_queue *wait, | ||
190 | u8 action, u8 *mac) | ||
191 | { | ||
192 | int ret = 0; | ||
193 | |||
194 | if ((action == HostCmd_ACT_GEN_GET) && mac) { | ||
195 | memcpy(mac, priv->curr_addr, ETH_ALEN); | ||
196 | return 0; | ||
197 | } | ||
198 | |||
199 | /* Send request to firmware */ | ||
200 | ret = mwifiex_prepare_cmd(priv, HostCmd_CMD_802_11_MAC_ADDRESS, | ||
201 | action, 0, wait, mac); | ||
202 | if (!ret) | ||
203 | ret = -EINPROGRESS; | ||
204 | |||
205 | return ret; | ||
206 | } | ||
207 | |||
208 | /* | ||
209 | * Sends IOCTL request to set MAC address. | ||
210 | * | ||
211 | * This function allocates the IOCTL request buffer, fills it | ||
212 | * with requisite parameters and calls the IOCTL handler. | ||
213 | */ | ||
214 | int mwifiex_request_set_mac_address(struct mwifiex_private *priv) | ||
215 | { | ||
216 | struct mwifiex_wait_queue *wait = NULL; | ||
217 | int status = 0; | ||
218 | u8 wait_option = MWIFIEX_CMD_WAIT; | ||
219 | |||
220 | /* Allocate wait buffer */ | ||
221 | wait = mwifiex_alloc_fill_wait_queue(priv, wait_option); | ||
222 | if (!wait) | ||
223 | return -ENOMEM; | ||
224 | |||
225 | status = mwifiex_bss_ioctl_mac_address(priv, wait, HostCmd_ACT_GEN_SET, | ||
226 | NULL); | ||
227 | |||
228 | status = mwifiex_request_ioctl(priv, wait, status, wait_option); | ||
229 | if (!status) | ||
230 | memcpy(priv->netdev->dev_addr, priv->curr_addr, ETH_ALEN); | ||
231 | else | ||
232 | dev_err(priv->adapter->dev, "set mac address failed: status=%d" | ||
233 | " error_code=%#x\n", status, wait->status); | ||
234 | |||
235 | kfree(wait); | ||
236 | return status; | ||
237 | } | ||
238 | |||
239 | /* | ||
240 | * IOCTL request handler to set multicast list. | ||
241 | * | ||
242 | * This function prepares the correct firmware command and | ||
243 | * issues it to set the multicast list. | ||
244 | * | ||
245 | * This function can be used to enable promiscuous mode, or enable all | ||
246 | * multicast packets, or to enable selective multicast. | ||
247 | */ | ||
248 | static int | ||
249 | mwifiex_bss_ioctl_multicast_list(struct mwifiex_private *priv, | ||
250 | struct mwifiex_wait_queue *wait, | ||
251 | u16 action, | ||
252 | struct mwifiex_multicast_list *mcast_list) | ||
253 | { | ||
254 | int ret = 0; | ||
255 | u16 old_pkt_filter; | ||
256 | |||
257 | old_pkt_filter = priv->curr_pkt_filter; | ||
258 | if (action == HostCmd_ACT_GEN_GET) | ||
259 | return -1; | ||
260 | |||
261 | if (mcast_list->mode == MWIFIEX_PROMISC_MODE) { | ||
262 | dev_dbg(priv->adapter->dev, "info: Enable Promiscuous mode\n"); | ||
263 | priv->curr_pkt_filter |= HostCmd_ACT_MAC_PROMISCUOUS_ENABLE; | ||
264 | priv->curr_pkt_filter &= | ||
265 | ~HostCmd_ACT_MAC_ALL_MULTICAST_ENABLE; | ||
266 | } else { | ||
267 | /* Multicast */ | ||
268 | priv->curr_pkt_filter &= ~HostCmd_ACT_MAC_PROMISCUOUS_ENABLE; | ||
269 | if (mcast_list->mode == MWIFIEX_MULTICAST_MODE) { | ||
270 | dev_dbg(priv->adapter->dev, | ||
271 | "info: Enabling All Multicast!\n"); | ||
272 | priv->curr_pkt_filter |= | ||
273 | HostCmd_ACT_MAC_ALL_MULTICAST_ENABLE; | ||
274 | } else { | ||
275 | priv->curr_pkt_filter &= | ||
276 | ~HostCmd_ACT_MAC_ALL_MULTICAST_ENABLE; | ||
277 | if (mcast_list->num_multicast_addr) { | ||
278 | dev_dbg(priv->adapter->dev, | ||
279 | "info: Set multicast list=%d\n", | ||
280 | mcast_list->num_multicast_addr); | ||
281 | /* Set multicast addresses to firmware */ | ||
282 | if (old_pkt_filter == priv->curr_pkt_filter) { | ||
283 | /* Send request to firmware */ | ||
284 | ret = mwifiex_prepare_cmd(priv, | ||
285 | HostCmd_CMD_MAC_MULTICAST_ADR, | ||
286 | action, 0, wait, mcast_list); | ||
287 | if (!ret) | ||
288 | ret = -EINPROGRESS; | ||
289 | } else { | ||
290 | /* Send request to firmware */ | ||
291 | ret = mwifiex_prepare_cmd(priv, | ||
292 | HostCmd_CMD_MAC_MULTICAST_ADR, | ||
293 | action, 0, NULL, | ||
294 | mcast_list); | ||
295 | } | ||
296 | } | ||
297 | } | ||
298 | } | ||
299 | dev_dbg(priv->adapter->dev, | ||
300 | "info: old_pkt_filter=%#x, curr_pkt_filter=%#x\n", | ||
301 | old_pkt_filter, priv->curr_pkt_filter); | ||
302 | if (old_pkt_filter != priv->curr_pkt_filter) { | ||
303 | ret = mwifiex_prepare_cmd(priv, HostCmd_CMD_MAC_CONTROL, action, | ||
304 | 0, wait, &priv->curr_pkt_filter); | ||
305 | if (!ret) | ||
306 | ret = -EINPROGRESS; | ||
307 | } | ||
308 | |||
309 | return ret; | ||
310 | } | ||
311 | |||
312 | /* | ||
313 | * Sends IOCTL request to set multicast list. | ||
314 | * | ||
315 | * This function allocates the IOCTL request buffer, fills it | ||
316 | * with requisite parameters and calls the IOCTL handler. | ||
317 | */ | ||
318 | void | ||
319 | mwifiex_request_set_multicast_list(struct mwifiex_private *priv, | ||
320 | struct net_device *dev) | ||
321 | { | ||
322 | struct mwifiex_wait_queue *wait = NULL; | ||
323 | struct mwifiex_multicast_list mcast_list; | ||
324 | u8 wait_option = MWIFIEX_NO_WAIT; | ||
325 | int status = 0; | ||
326 | |||
327 | /* Allocate wait buffer */ | ||
328 | wait = mwifiex_alloc_fill_wait_queue(priv, wait_option); | ||
329 | if (!wait) | ||
330 | return; | ||
331 | |||
332 | if (dev->flags & IFF_PROMISC) { | ||
333 | mcast_list.mode = MWIFIEX_PROMISC_MODE; | ||
334 | } else if (dev->flags & IFF_ALLMULTI || | ||
335 | netdev_mc_count(dev) > MWIFIEX_MAX_MULTICAST_LIST_SIZE) { | ||
336 | mcast_list.mode = MWIFIEX_ALL_MULTI_MODE; | ||
337 | } else { | ||
338 | mcast_list.mode = MWIFIEX_MULTICAST_MODE; | ||
339 | if (netdev_mc_count(dev)) | ||
340 | mcast_list.num_multicast_addr = | ||
341 | mwifiex_copy_mcast_addr(&mcast_list, dev); | ||
342 | } | ||
343 | status = mwifiex_bss_ioctl_multicast_list(priv, wait, | ||
344 | HostCmd_ACT_GEN_SET, | ||
345 | &mcast_list); | ||
346 | |||
347 | status = mwifiex_request_ioctl(priv, wait, status, wait_option); | ||
348 | if (wait && status != -EINPROGRESS) | ||
349 | kfree(wait); | ||
350 | |||
351 | return; | ||
352 | } | ||
353 | |||
354 | /* | ||
355 | * IOCTL request handler to disconnect from a BSS/IBSS. | ||
356 | */ | ||
357 | static int mwifiex_bss_ioctl_stop(struct mwifiex_private *priv, | ||
358 | struct mwifiex_wait_queue *wait, u8 *mac) | ||
359 | { | ||
360 | return mwifiex_deauthenticate(priv, wait, mac); | ||
361 | } | ||
362 | |||
363 | /* | ||
364 | * Sends IOCTL request to disconnect from a BSS. | ||
365 | * | ||
366 | * This function allocates the IOCTL request buffer, fills it | ||
367 | * with requisite parameters and calls the IOCTL handler. | ||
368 | */ | ||
369 | int mwifiex_disconnect(struct mwifiex_private *priv, u8 wait_option, u8 *mac) | ||
370 | { | ||
371 | struct mwifiex_wait_queue *wait = NULL; | ||
372 | int status = 0; | ||
373 | |||
374 | /* Allocate wait buffer */ | ||
375 | wait = mwifiex_alloc_fill_wait_queue(priv, wait_option); | ||
376 | if (!wait) | ||
377 | return -ENOMEM; | ||
378 | |||
379 | status = mwifiex_bss_ioctl_stop(priv, wait, mac); | ||
380 | |||
381 | status = mwifiex_request_ioctl(priv, wait, status, wait_option); | ||
382 | |||
383 | kfree(wait); | ||
384 | return status; | ||
385 | } | ||
386 | EXPORT_SYMBOL_GPL(mwifiex_disconnect); | ||
387 | |||
388 | /* | ||
389 | * IOCTL request handler to join a BSS/IBSS. | ||
390 | * | ||
391 | * In Ad-Hoc mode, the IBSS is created if not found in scan list. | ||
392 | * In both Ad-Hoc and infra mode, an deauthentication is performed | ||
393 | * first. | ||
394 | */ | ||
395 | static int mwifiex_bss_ioctl_start(struct mwifiex_private *priv, | ||
396 | struct mwifiex_wait_queue *wait, | ||
397 | struct mwifiex_ssid_bssid *ssid_bssid) | ||
398 | { | ||
399 | int ret = 0; | ||
400 | struct mwifiex_adapter *adapter = priv->adapter; | ||
401 | s32 i = -1; | ||
402 | |||
403 | priv->scan_block = false; | ||
404 | if (!ssid_bssid) | ||
405 | return -1; | ||
406 | |||
407 | if (priv->bss_mode == MWIFIEX_BSS_MODE_INFRA) { | ||
408 | /* Infra mode */ | ||
409 | ret = mwifiex_deauthenticate(priv, NULL, NULL); | ||
410 | if (ret) | ||
411 | return ret; | ||
412 | |||
413 | /* Search for the requested SSID in the scan table */ | ||
414 | if (ssid_bssid->ssid.ssid_len) | ||
415 | i = mwifiex_find_ssid_in_list(priv, &ssid_bssid->ssid, | ||
416 | NULL, MWIFIEX_BSS_MODE_INFRA); | ||
417 | else | ||
418 | i = mwifiex_find_bssid_in_list(priv, | ||
419 | (u8 *) &ssid_bssid->bssid, | ||
420 | MWIFIEX_BSS_MODE_INFRA); | ||
421 | if (i < 0) | ||
422 | return -1; | ||
423 | |||
424 | dev_dbg(adapter->dev, | ||
425 | "info: SSID found in scan list ... associating...\n"); | ||
426 | |||
427 | /* Clear any past association response stored for | ||
428 | * application retrieval */ | ||
429 | priv->assoc_rsp_size = 0; | ||
430 | ret = mwifiex_associate(priv, wait, &adapter->scan_table[i]); | ||
431 | if (ret) | ||
432 | return ret; | ||
433 | } else { | ||
434 | /* Adhoc mode */ | ||
435 | /* If the requested SSID matches current SSID, return */ | ||
436 | if (ssid_bssid->ssid.ssid_len && | ||
437 | (!mwifiex_ssid_cmp | ||
438 | (&priv->curr_bss_params.bss_descriptor.ssid, | ||
439 | &ssid_bssid->ssid))) | ||
440 | return 0; | ||
441 | |||
442 | /* Exit Adhoc mode first */ | ||
443 | dev_dbg(adapter->dev, "info: Sending Adhoc Stop\n"); | ||
444 | ret = mwifiex_deauthenticate(priv, NULL, NULL); | ||
445 | if (ret) | ||
446 | return ret; | ||
447 | |||
448 | priv->adhoc_is_link_sensed = false; | ||
449 | |||
450 | /* Search for the requested network in the scan table */ | ||
451 | if (ssid_bssid->ssid.ssid_len) | ||
452 | i = mwifiex_find_ssid_in_list(priv, | ||
453 | &ssid_bssid->ssid, NULL, | ||
454 | MWIFIEX_BSS_MODE_IBSS); | ||
455 | else | ||
456 | i = mwifiex_find_bssid_in_list(priv, | ||
457 | (u8 *)&ssid_bssid->bssid, | ||
458 | MWIFIEX_BSS_MODE_IBSS); | ||
459 | |||
460 | if (i >= 0) { | ||
461 | dev_dbg(adapter->dev, "info: network found in scan" | ||
462 | " list. Joining...\n"); | ||
463 | ret = mwifiex_adhoc_join(priv, wait, | ||
464 | &adapter->scan_table[i]); | ||
465 | if (ret) | ||
466 | return ret; | ||
467 | } else { /* i >= 0 */ | ||
468 | dev_dbg(adapter->dev, "info: Network not found in " | ||
469 | "the list, creating adhoc with ssid = %s\n", | ||
470 | ssid_bssid->ssid.ssid); | ||
471 | ret = mwifiex_adhoc_start(priv, wait, | ||
472 | &ssid_bssid->ssid); | ||
473 | if (ret) | ||
474 | return ret; | ||
475 | } | ||
476 | } | ||
477 | |||
478 | if (!ret) | ||
479 | ret = -EINPROGRESS; | ||
480 | |||
481 | return ret; | ||
482 | } | ||
483 | |||
484 | /* | ||
485 | * Sends IOCTL request to connect with a BSS. | ||
486 | * | ||
487 | * This function allocates the IOCTL request buffer, fills it | ||
488 | * with requisite parameters and calls the IOCTL handler. | ||
489 | */ | ||
490 | int mwifiex_bss_start(struct mwifiex_private *priv, u8 wait_option, | ||
491 | struct mwifiex_ssid_bssid *ssid_bssid) | ||
492 | { | ||
493 | struct mwifiex_wait_queue *wait = NULL; | ||
494 | struct mwifiex_ssid_bssid tmp_ssid_bssid; | ||
495 | int status = 0; | ||
496 | |||
497 | /* Stop the O.S. TX queue if needed */ | ||
498 | if (!netif_queue_stopped(priv->netdev)) | ||
499 | netif_stop_queue(priv->netdev); | ||
500 | |||
501 | /* Allocate wait buffer */ | ||
502 | wait = mwifiex_alloc_fill_wait_queue(priv, wait_option); | ||
503 | if (!wait) | ||
504 | return -ENOMEM; | ||
505 | |||
506 | if (ssid_bssid) | ||
507 | memcpy(&tmp_ssid_bssid, ssid_bssid, | ||
508 | sizeof(struct mwifiex_ssid_bssid)); | ||
509 | status = mwifiex_bss_ioctl_start(priv, wait, &tmp_ssid_bssid); | ||
510 | |||
511 | status = mwifiex_request_ioctl(priv, wait, status, wait_option); | ||
512 | |||
513 | kfree(wait); | ||
514 | return status; | ||
515 | } | ||
516 | |||
517 | /* | ||
518 | * IOCTL request handler to set host sleep configuration. | ||
519 | * | ||
520 | * This function prepares the correct firmware command and | ||
521 | * issues it. | ||
522 | */ | ||
523 | static int | ||
524 | mwifiex_pm_ioctl_hs_cfg(struct mwifiex_private *priv, | ||
525 | struct mwifiex_wait_queue *wait, | ||
526 | u16 action, struct mwifiex_ds_hs_cfg *hs_cfg) | ||
527 | { | ||
528 | struct mwifiex_adapter *adapter = priv->adapter; | ||
529 | int status = 0; | ||
530 | u32 prev_cond = 0; | ||
531 | |||
532 | switch (action) { | ||
533 | case HostCmd_ACT_GEN_SET: | ||
534 | if (adapter->pps_uapsd_mode) { | ||
535 | dev_dbg(adapter->dev, "info: Host Sleep IOCTL" | ||
536 | " is blocked in UAPSD/PPS mode\n"); | ||
537 | status = -1; | ||
538 | break; | ||
539 | } | ||
540 | if (hs_cfg->is_invoke_hostcmd) { | ||
541 | if (hs_cfg->conditions == HOST_SLEEP_CFG_CANCEL) { | ||
542 | if (!adapter->is_hs_configured) | ||
543 | /* Already cancelled */ | ||
544 | break; | ||
545 | /* Save previous condition */ | ||
546 | prev_cond = le32_to_cpu(adapter->hs_cfg | ||
547 | .conditions); | ||
548 | adapter->hs_cfg.conditions = | ||
549 | cpu_to_le32(hs_cfg->conditions); | ||
550 | } else if (hs_cfg->conditions) { | ||
551 | adapter->hs_cfg.conditions = | ||
552 | cpu_to_le32(hs_cfg->conditions); | ||
553 | adapter->hs_cfg.gpio = (u8)hs_cfg->gpio; | ||
554 | if (hs_cfg->gap) | ||
555 | adapter->hs_cfg.gap = (u8)hs_cfg->gap; | ||
556 | } else if (adapter->hs_cfg.conditions == | ||
557 | cpu_to_le32( | ||
558 | HOST_SLEEP_CFG_CANCEL)) { | ||
559 | /* Return failure if no parameters for HS | ||
560 | enable */ | ||
561 | status = -1; | ||
562 | break; | ||
563 | } | ||
564 | status = mwifiex_prepare_cmd(priv, | ||
565 | HostCmd_CMD_802_11_HS_CFG_ENH, | ||
566 | HostCmd_ACT_GEN_SET, | ||
567 | 0, wait, &adapter->hs_cfg); | ||
568 | if (!status) | ||
569 | status = -EINPROGRESS; | ||
570 | if (hs_cfg->conditions == HOST_SLEEP_CFG_CANCEL) | ||
571 | /* Restore previous condition */ | ||
572 | adapter->hs_cfg.conditions = | ||
573 | cpu_to_le32(prev_cond); | ||
574 | } else { | ||
575 | adapter->hs_cfg.conditions = | ||
576 | cpu_to_le32(hs_cfg->conditions); | ||
577 | adapter->hs_cfg.gpio = (u8)hs_cfg->gpio; | ||
578 | adapter->hs_cfg.gap = (u8)hs_cfg->gap; | ||
579 | } | ||
580 | break; | ||
581 | case HostCmd_ACT_GEN_GET: | ||
582 | hs_cfg->conditions = le32_to_cpu(adapter->hs_cfg.conditions); | ||
583 | hs_cfg->gpio = adapter->hs_cfg.gpio; | ||
584 | hs_cfg->gap = adapter->hs_cfg.gap; | ||
585 | break; | ||
586 | default: | ||
587 | status = -1; | ||
588 | break; | ||
589 | } | ||
590 | |||
591 | return status; | ||
592 | } | ||
593 | |||
594 | /* | ||
595 | * Sends IOCTL request to set Host Sleep parameters. | ||
596 | * | ||
597 | * This function allocates the IOCTL request buffer, fills it | ||
598 | * with requisite parameters and calls the IOCTL handler. | ||
599 | */ | ||
600 | int mwifiex_set_hs_params(struct mwifiex_private *priv, u16 action, | ||
601 | u8 wait_option, | ||
602 | struct mwifiex_ds_hs_cfg *hscfg) | ||
603 | { | ||
604 | int ret = 0; | ||
605 | struct mwifiex_wait_queue *wait = NULL; | ||
606 | |||
607 | if (!hscfg) | ||
608 | return -ENOMEM; | ||
609 | |||
610 | /* Allocate wait buffer */ | ||
611 | wait = mwifiex_alloc_fill_wait_queue(priv, wait_option); | ||
612 | if (!wait) | ||
613 | return -ENOMEM; | ||
614 | |||
615 | ret = mwifiex_pm_ioctl_hs_cfg(priv, wait, action, hscfg); | ||
616 | |||
617 | ret = mwifiex_request_ioctl(priv, wait, ret, wait_option); | ||
618 | |||
619 | if (wait && (ret != -EINPROGRESS)) | ||
620 | kfree(wait); | ||
621 | return ret; | ||
622 | } | ||
623 | |||
624 | /* | ||
625 | * Sends IOCTL request to cancel the existing Host Sleep configuration. | ||
626 | * | ||
627 | * This function allocates the IOCTL request buffer, fills it | ||
628 | * with requisite parameters and calls the IOCTL handler. | ||
629 | */ | ||
630 | int mwifiex_cancel_hs(struct mwifiex_private *priv, u8 wait_option) | ||
631 | { | ||
632 | int ret = 0; | ||
633 | struct mwifiex_ds_hs_cfg hscfg; | ||
634 | |||
635 | /* Cancel Host Sleep */ | ||
636 | hscfg.conditions = HOST_SLEEP_CFG_CANCEL; | ||
637 | hscfg.is_invoke_hostcmd = true; | ||
638 | ret = mwifiex_set_hs_params(priv, HostCmd_ACT_GEN_SET, | ||
639 | wait_option, &hscfg); | ||
640 | |||
641 | return ret; | ||
642 | } | ||
643 | EXPORT_SYMBOL_GPL(mwifiex_cancel_hs); | ||
644 | |||
645 | /* | ||
646 | * Sends IOCTL request to cancel the existing Host Sleep configuration. | ||
647 | * | ||
648 | * This function allocates the IOCTL request buffer, fills it | ||
649 | * with requisite parameters and calls the IOCTL handler. | ||
650 | */ | ||
651 | int mwifiex_enable_hs(struct mwifiex_adapter *adapter) | ||
652 | { | ||
653 | struct mwifiex_ds_hs_cfg hscfg; | ||
654 | |||
655 | if (adapter->hs_activated) { | ||
656 | dev_dbg(adapter->dev, "cmd: HS Already actived\n"); | ||
657 | return true; | ||
658 | } | ||
659 | |||
660 | /* Enable Host Sleep */ | ||
661 | adapter->hs_activate_wait_q_woken = false; | ||
662 | |||
663 | memset(&hscfg, 0, sizeof(struct mwifiex_hs_config_param)); | ||
664 | hscfg.is_invoke_hostcmd = true; | ||
665 | |||
666 | if (mwifiex_set_hs_params(mwifiex_get_priv(adapter, | ||
667 | MWIFIEX_BSS_ROLE_STA), | ||
668 | HostCmd_ACT_GEN_SET, | ||
669 | MWIFIEX_IOCTL_WAIT, &hscfg)) { | ||
670 | dev_err(adapter->dev, "IOCTL request HS enable failed\n"); | ||
671 | return false; | ||
672 | } | ||
673 | |||
674 | wait_event_interruptible(adapter->hs_activate_wait_q, | ||
675 | adapter->hs_activate_wait_q_woken); | ||
676 | |||
677 | return true; | ||
678 | } | ||
679 | EXPORT_SYMBOL_GPL(mwifiex_enable_hs); | ||
680 | |||
681 | /* | ||
682 | * IOCTL request handler to get signal information. | ||
683 | * | ||
684 | * This function prepares the correct firmware command and | ||
685 | * issues it to get the signal (RSSI) information. | ||
686 | * | ||
687 | * This only works in the connected mode. | ||
688 | */ | ||
689 | static int mwifiex_get_info_signal(struct mwifiex_private *priv, | ||
690 | struct mwifiex_wait_queue *wait, | ||
691 | struct mwifiex_ds_get_signal *signal) | ||
692 | { | ||
693 | int ret = 0; | ||
694 | |||
695 | if (!wait) { | ||
696 | dev_err(priv->adapter->dev, "WAIT information is not present\n"); | ||
697 | return -1; | ||
698 | } | ||
699 | |||
700 | /* Signal info can be obtained only if connected */ | ||
701 | if (!priv->media_connected) { | ||
702 | dev_dbg(priv->adapter->dev, | ||
703 | "info: Can not get signal in disconnected state\n"); | ||
704 | return -1; | ||
705 | } | ||
706 | |||
707 | /* Send request to firmware */ | ||
708 | ret = mwifiex_prepare_cmd(priv, HostCmd_CMD_RSSI_INFO, | ||
709 | HostCmd_ACT_GEN_GET, 0, wait, signal); | ||
710 | |||
711 | if (!ret) | ||
712 | ret = -EINPROGRESS; | ||
713 | |||
714 | return ret; | ||
715 | } | ||
716 | |||
717 | /* | ||
718 | * IOCTL request handler to get statistics. | ||
719 | * | ||
720 | * This function prepares the correct firmware command and | ||
721 | * issues it to get the statistics (RSSI) information. | ||
722 | */ | ||
723 | static int mwifiex_get_info_stats(struct mwifiex_private *priv, | ||
724 | struct mwifiex_wait_queue *wait, | ||
725 | struct mwifiex_ds_get_stats *log) | ||
726 | { | ||
727 | int ret = 0; | ||
728 | |||
729 | if (!wait) { | ||
730 | dev_err(priv->adapter->dev, "MWIFIEX IOCTL information is not present\n"); | ||
731 | return -1; | ||
732 | } | ||
733 | |||
734 | /* Send request to firmware */ | ||
735 | ret = mwifiex_prepare_cmd(priv, HostCmd_CMD_802_11_GET_LOG, | ||
736 | HostCmd_ACT_GEN_GET, 0, wait, log); | ||
737 | |||
738 | if (!ret) | ||
739 | ret = -EINPROGRESS; | ||
740 | |||
741 | return ret; | ||
742 | } | ||
743 | |||
744 | /* | ||
745 | * IOCTL request handler to get BSS information. | ||
746 | * | ||
747 | * This function collates the information from different driver structures | ||
748 | * to send to the user. | ||
749 | */ | ||
750 | int mwifiex_get_bss_info(struct mwifiex_private *priv, | ||
751 | struct mwifiex_bss_info *info) | ||
752 | { | ||
753 | struct mwifiex_adapter *adapter = priv->adapter; | ||
754 | struct mwifiex_bssdescriptor *bss_desc; | ||
755 | s32 tbl_idx = 0; | ||
756 | |||
757 | if (!info) | ||
758 | return -1; | ||
759 | |||
760 | /* Get current BSS info */ | ||
761 | bss_desc = &priv->curr_bss_params.bss_descriptor; | ||
762 | |||
763 | /* BSS mode */ | ||
764 | info->bss_mode = priv->bss_mode; | ||
765 | |||
766 | /* SSID */ | ||
767 | memcpy(&info->ssid, &bss_desc->ssid, | ||
768 | sizeof(struct mwifiex_802_11_ssid)); | ||
769 | |||
770 | /* BSSID */ | ||
771 | memcpy(&info->bssid, &bss_desc->mac_address, ETH_ALEN); | ||
772 | |||
773 | /* Channel */ | ||
774 | info->bss_chan = bss_desc->channel; | ||
775 | |||
776 | /* Region code */ | ||
777 | info->region_code = adapter->region_code; | ||
778 | |||
779 | /* Scan table index if connected */ | ||
780 | info->scan_table_idx = 0; | ||
781 | if (priv->media_connected) { | ||
782 | tbl_idx = | ||
783 | mwifiex_find_ssid_in_list(priv, &bss_desc->ssid, | ||
784 | bss_desc->mac_address, | ||
785 | priv->bss_mode); | ||
786 | if (tbl_idx >= 0) | ||
787 | info->scan_table_idx = tbl_idx; | ||
788 | } | ||
789 | |||
790 | /* Connection status */ | ||
791 | info->media_connected = priv->media_connected; | ||
792 | |||
793 | /* Radio status */ | ||
794 | info->radio_on = adapter->radio_on; | ||
795 | |||
796 | /* Tx power information */ | ||
797 | info->max_power_level = priv->max_tx_power_level; | ||
798 | info->min_power_level = priv->min_tx_power_level; | ||
799 | |||
800 | /* AdHoc state */ | ||
801 | info->adhoc_state = priv->adhoc_state; | ||
802 | |||
803 | /* Last beacon NF */ | ||
804 | info->bcn_nf_last = priv->bcn_nf_last; | ||
805 | |||
806 | /* wep status */ | ||
807 | if (priv->sec_info.wep_status == MWIFIEX_802_11_WEP_ENABLED) | ||
808 | info->wep_status = true; | ||
809 | else | ||
810 | info->wep_status = false; | ||
811 | |||
812 | info->is_hs_configured = adapter->is_hs_configured; | ||
813 | info->is_deep_sleep = adapter->is_deep_sleep; | ||
814 | |||
815 | return 0; | ||
816 | } | ||
817 | |||
818 | /* | ||
819 | * IOCTL request handler to get extended version information. | ||
820 | * | ||
821 | * This function prepares the correct firmware command and | ||
822 | * issues it to get the extended version information. | ||
823 | */ | ||
824 | static int mwifiex_get_info_ver_ext(struct mwifiex_private *priv, | ||
825 | struct mwifiex_wait_queue *wait, | ||
826 | struct mwifiex_ver_ext *ver_ext) | ||
827 | { | ||
828 | int ret = 0; | ||
829 | |||
830 | /* Send request to firmware */ | ||
831 | ret = mwifiex_prepare_cmd(priv, HostCmd_CMD_VERSION_EXT, | ||
832 | HostCmd_ACT_GEN_GET, 0, wait, ver_ext); | ||
833 | if (!ret) | ||
834 | ret = -EINPROGRESS; | ||
835 | |||
836 | return ret; | ||
837 | } | ||
838 | |||
839 | /* | ||
840 | * IOCTL request handler to set/get SNMP MIB parameters. | ||
841 | * | ||
842 | * This function prepares the correct firmware command and | ||
843 | * issues it. | ||
844 | * | ||
845 | * Currently the following parameters are supported - | ||
846 | * Set/get RTS Threshold | ||
847 | * Set/get fragmentation threshold | ||
848 | * Set/get retry count | ||
849 | */ | ||
850 | int mwifiex_snmp_mib_ioctl(struct mwifiex_private *priv, | ||
851 | struct mwifiex_wait_queue *wait, | ||
852 | u32 cmd_oid, u16 action, u32 *value) | ||
853 | { | ||
854 | int ret = 0; | ||
855 | |||
856 | if (!value) | ||
857 | return -1; | ||
858 | |||
859 | /* Send request to firmware */ | ||
860 | ret = mwifiex_prepare_cmd(priv, HostCmd_CMD_802_11_SNMP_MIB, | ||
861 | action, cmd_oid, wait, value); | ||
862 | |||
863 | if (!ret) | ||
864 | ret = -EINPROGRESS; | ||
865 | |||
866 | return ret; | ||
867 | } | ||
868 | |||
869 | /* | ||
870 | * IOCTL request handler to set/get band configurations. | ||
871 | * | ||
872 | * For SET operation, it performs extra checks to make sure the Ad-Hoc | ||
873 | * band and channel are compatible. Otherwise it returns an error. | ||
874 | * | ||
875 | * For GET operation, this function retrieves the following information - | ||
876 | * - Infra bands | ||
877 | * - Ad-hoc band | ||
878 | * - Ad-hoc channel | ||
879 | * - Secondary channel offset | ||
880 | */ | ||
881 | int mwifiex_radio_ioctl_band_cfg(struct mwifiex_private *priv, | ||
882 | u16 action, | ||
883 | struct mwifiex_ds_band_cfg *radio_cfg) | ||
884 | { | ||
885 | struct mwifiex_adapter *adapter = priv->adapter; | ||
886 | u8 infra_band = 0; | ||
887 | u8 adhoc_band = 0; | ||
888 | u32 adhoc_channel = 0; | ||
889 | |||
890 | if (action == HostCmd_ACT_GEN_GET) { | ||
891 | /* Infra Bands */ | ||
892 | radio_cfg->config_bands = adapter->config_bands; | ||
893 | /* Adhoc Band */ | ||
894 | radio_cfg->adhoc_start_band = adapter->adhoc_start_band; | ||
895 | /* Adhoc channel */ | ||
896 | radio_cfg->adhoc_channel = priv->adhoc_channel; | ||
897 | /* Secondary channel offset */ | ||
898 | radio_cfg->sec_chan_offset = adapter->chan_offset; | ||
899 | return 0; | ||
900 | } | ||
901 | |||
902 | /* For action = SET */ | ||
903 | infra_band = (u8) radio_cfg->config_bands; | ||
904 | adhoc_band = (u8) radio_cfg->adhoc_start_band; | ||
905 | adhoc_channel = radio_cfg->adhoc_channel; | ||
906 | |||
907 | /* SET Infra band */ | ||
908 | if ((infra_band | adapter->fw_bands) & ~adapter->fw_bands) | ||
909 | return -1; | ||
910 | |||
911 | adapter->config_bands = infra_band; | ||
912 | |||
913 | /* SET Ad-hoc Band */ | ||
914 | if ((adhoc_band | adapter->fw_bands) & ~adapter->fw_bands) | ||
915 | return -1; | ||
916 | |||
917 | if (adhoc_band) | ||
918 | adapter->adhoc_start_band = adhoc_band; | ||
919 | adapter->chan_offset = (u8) radio_cfg->sec_chan_offset; | ||
920 | /* | ||
921 | * If no adhoc_channel is supplied verify if the existing adhoc | ||
922 | * channel compiles with new adhoc_band | ||
923 | */ | ||
924 | if (!adhoc_channel) { | ||
925 | if (!mwifiex_get_cfp_by_band_and_channel_from_cfg80211 | ||
926 | (priv, adapter->adhoc_start_band, | ||
927 | priv->adhoc_channel)) { | ||
928 | /* Pass back the default channel */ | ||
929 | radio_cfg->adhoc_channel = DEFAULT_AD_HOC_CHANNEL; | ||
930 | if ((adapter->adhoc_start_band & BAND_A) | ||
931 | || (adapter->adhoc_start_band & BAND_AN)) | ||
932 | radio_cfg->adhoc_channel = | ||
933 | DEFAULT_AD_HOC_CHANNEL_A; | ||
934 | } | ||
935 | } else { /* Retrurn error if adhoc_band and | ||
936 | adhoc_channel combination is invalid */ | ||
937 | if (!mwifiex_get_cfp_by_band_and_channel_from_cfg80211 | ||
938 | (priv, adapter->adhoc_start_band, (u16) adhoc_channel)) | ||
939 | return -1; | ||
940 | priv->adhoc_channel = (u8) adhoc_channel; | ||
941 | } | ||
942 | if ((adhoc_band & BAND_GN) || (adhoc_band & BAND_AN)) | ||
943 | adapter->adhoc_11n_enabled = true; | ||
944 | else | ||
945 | adapter->adhoc_11n_enabled = false; | ||
946 | |||
947 | return 0; | ||
948 | } | ||
949 | |||
950 | /* | ||
951 | * IOCTL request handler to set/get active channel. | ||
952 | * | ||
953 | * This function performs validity checking on channel/frequency | ||
954 | * compatibility and returns failure if not valid. | ||
955 | */ | ||
956 | int mwifiex_bss_ioctl_channel(struct mwifiex_private *priv, u16 action, | ||
957 | struct mwifiex_chan_freq_power *chan) | ||
958 | { | ||
959 | struct mwifiex_adapter *adapter = priv->adapter; | ||
960 | struct mwifiex_chan_freq_power *cfp = NULL; | ||
961 | |||
962 | if (!chan) | ||
963 | return -1; | ||
964 | |||
965 | if (action == HostCmd_ACT_GEN_GET) { | ||
966 | cfp = mwifiex_get_cfp_by_band_and_channel_from_cfg80211(priv, | ||
967 | priv->curr_bss_params.band, | ||
968 | (u16) priv->curr_bss_params.bss_descriptor. | ||
969 | channel); | ||
970 | chan->channel = cfp->channel; | ||
971 | chan->freq = cfp->freq; | ||
972 | |||
973 | return 0; | ||
974 | } | ||
975 | if (!chan->channel && !chan->freq) | ||
976 | return -1; | ||
977 | if (adapter->adhoc_start_band & BAND_AN) | ||
978 | adapter->adhoc_start_band = BAND_G | BAND_B | BAND_GN; | ||
979 | else if (adapter->adhoc_start_band & BAND_A) | ||
980 | adapter->adhoc_start_band = BAND_G | BAND_B; | ||
981 | if (chan->channel) { | ||
982 | if (chan->channel <= MAX_CHANNEL_BAND_BG) | ||
983 | cfp = mwifiex_get_cfp_by_band_and_channel_from_cfg80211 | ||
984 | (priv, 0, (u16) chan->channel); | ||
985 | if (!cfp) { | ||
986 | cfp = mwifiex_get_cfp_by_band_and_channel_from_cfg80211 | ||
987 | (priv, BAND_A, (u16) chan->channel); | ||
988 | if (cfp) { | ||
989 | if (adapter->adhoc_11n_enabled) | ||
990 | adapter->adhoc_start_band = BAND_A | ||
991 | | BAND_AN; | ||
992 | else | ||
993 | adapter->adhoc_start_band = BAND_A; | ||
994 | } | ||
995 | } | ||
996 | } else { | ||
997 | if (chan->freq <= MAX_FREQUENCY_BAND_BG) | ||
998 | cfp = mwifiex_get_cfp_by_band_and_freq_from_cfg80211( | ||
999 | priv, 0, chan->freq); | ||
1000 | if (!cfp) { | ||
1001 | cfp = mwifiex_get_cfp_by_band_and_freq_from_cfg80211 | ||
1002 | (priv, BAND_A, chan->freq); | ||
1003 | if (cfp) { | ||
1004 | if (adapter->adhoc_11n_enabled) | ||
1005 | adapter->adhoc_start_band = BAND_A | ||
1006 | | BAND_AN; | ||
1007 | else | ||
1008 | adapter->adhoc_start_band = BAND_A; | ||
1009 | } | ||
1010 | } | ||
1011 | } | ||
1012 | if (!cfp || !cfp->channel) { | ||
1013 | dev_err(adapter->dev, "invalid channel/freq\n"); | ||
1014 | return -1; | ||
1015 | } | ||
1016 | priv->adhoc_channel = (u8) cfp->channel; | ||
1017 | chan->channel = cfp->channel; | ||
1018 | chan->freq = cfp->freq; | ||
1019 | |||
1020 | return 0; | ||
1021 | } | ||
1022 | |||
1023 | /* | ||
1024 | * IOCTL request handler to set/get BSS mode. | ||
1025 | * | ||
1026 | * This function prepares the correct firmware command and | ||
1027 | * issues it to set or get the BSS mode. | ||
1028 | * | ||
1029 | * In case the mode is changed, a deauthentication is performed | ||
1030 | * first by the function automatically. | ||
1031 | */ | ||
1032 | int mwifiex_bss_ioctl_mode(struct mwifiex_private *priv, | ||
1033 | struct mwifiex_wait_queue *wait, | ||
1034 | u16 action, int *mode) | ||
1035 | { | ||
1036 | int ret = 0; | ||
1037 | |||
1038 | if (!mode) | ||
1039 | return -1; | ||
1040 | |||
1041 | if (action == HostCmd_ACT_GEN_GET) { | ||
1042 | *mode = priv->bss_mode; | ||
1043 | return 0; | ||
1044 | } | ||
1045 | |||
1046 | if ((priv->bss_mode == *mode) || (*mode == MWIFIEX_BSS_MODE_AUTO)) { | ||
1047 | dev_dbg(priv->adapter->dev, | ||
1048 | "info: Already set to required mode! No change!\n"); | ||
1049 | priv->bss_mode = *mode; | ||
1050 | return 0; | ||
1051 | } | ||
1052 | |||
1053 | ret = mwifiex_deauthenticate(priv, wait, NULL); | ||
1054 | |||
1055 | priv->sec_info.authentication_mode = MWIFIEX_AUTH_MODE_OPEN; | ||
1056 | priv->bss_mode = *mode; | ||
1057 | if (priv->bss_mode != MWIFIEX_BSS_MODE_AUTO) { | ||
1058 | ret = mwifiex_prepare_cmd(priv, HostCmd_CMD_SET_BSS_MODE, | ||
1059 | HostCmd_ACT_GEN_SET, 0, wait, NULL); | ||
1060 | if (!ret) | ||
1061 | ret = -EINPROGRESS; | ||
1062 | } | ||
1063 | |||
1064 | return ret; | ||
1065 | } | ||
1066 | |||
1067 | /* | ||
1068 | * IOCTL request handler to set/get Ad-Hoc channel. | ||
1069 | * | ||
1070 | * This function prepares the correct firmware command and | ||
1071 | * issues it to set or get the ad-hoc channel. | ||
1072 | */ | ||
1073 | static int mwifiex_bss_ioctl_ibss_channel(struct mwifiex_private *priv, | ||
1074 | struct mwifiex_wait_queue *wait, | ||
1075 | u16 action, u16 *channel) | ||
1076 | { | ||
1077 | int ret = 0; | ||
1078 | |||
1079 | if (action == HostCmd_ACT_GEN_GET) { | ||
1080 | if (!priv->media_connected) { | ||
1081 | *channel = priv->adhoc_channel; | ||
1082 | return ret; | ||
1083 | } | ||
1084 | } else { | ||
1085 | priv->adhoc_channel = (u8) *channel; | ||
1086 | } | ||
1087 | |||
1088 | /* Send request to firmware */ | ||
1089 | ret = mwifiex_prepare_cmd(priv, HostCmd_CMD_802_11_RF_CHANNEL, | ||
1090 | action, 0, wait, channel); | ||
1091 | if (!ret) | ||
1092 | ret = -EINPROGRESS; | ||
1093 | |||
1094 | return ret; | ||
1095 | } | ||
1096 | |||
1097 | /* | ||
1098 | * IOCTL request handler to find a particular BSS. | ||
1099 | * | ||
1100 | * The BSS can be searched with either a BSSID or a SSID. If none of | ||
1101 | * these are provided, just the best BSS (best RSSI) is returned. | ||
1102 | */ | ||
1103 | int mwifiex_bss_ioctl_find_bss(struct mwifiex_private *priv, | ||
1104 | struct mwifiex_wait_queue *wait, | ||
1105 | struct mwifiex_ssid_bssid *ssid_bssid) | ||
1106 | { | ||
1107 | struct mwifiex_adapter *adapter = priv->adapter; | ||
1108 | int ret = 0; | ||
1109 | struct mwifiex_bssdescriptor *bss_desc; | ||
1110 | u8 zero_mac[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 }; | ||
1111 | u8 mac[ETH_ALEN]; | ||
1112 | int i = 0; | ||
1113 | |||
1114 | if (memcmp(ssid_bssid->bssid, zero_mac, sizeof(zero_mac))) { | ||
1115 | i = mwifiex_find_bssid_in_list(priv, | ||
1116 | (u8 *) ssid_bssid->bssid, | ||
1117 | priv->bss_mode); | ||
1118 | if (i < 0) { | ||
1119 | memcpy(mac, ssid_bssid->bssid, sizeof(mac)); | ||
1120 | dev_err(adapter->dev, "cannot find bssid %pM\n", mac); | ||
1121 | return -1; | ||
1122 | } | ||
1123 | bss_desc = &adapter->scan_table[i]; | ||
1124 | memcpy(&ssid_bssid->ssid, &bss_desc->ssid, | ||
1125 | sizeof(struct mwifiex_802_11_ssid)); | ||
1126 | } else if (ssid_bssid->ssid.ssid_len) { | ||
1127 | i = mwifiex_find_ssid_in_list(priv, &ssid_bssid->ssid, NULL, | ||
1128 | priv->bss_mode); | ||
1129 | if (i < 0) { | ||
1130 | dev_err(adapter->dev, "cannot find ssid %s\n", | ||
1131 | ssid_bssid->ssid.ssid); | ||
1132 | return -1; | ||
1133 | } | ||
1134 | bss_desc = &adapter->scan_table[i]; | ||
1135 | memcpy(ssid_bssid->bssid, bss_desc->mac_address, ETH_ALEN); | ||
1136 | } else { | ||
1137 | ret = mwifiex_find_best_network(priv, ssid_bssid); | ||
1138 | } | ||
1139 | |||
1140 | return ret; | ||
1141 | } | ||
1142 | |||
1143 | /* | ||
1144 | * IOCTL request handler to change Ad-Hoc channel. | ||
1145 | * | ||
1146 | * This function allocates the IOCTL request buffer, fills it | ||
1147 | * with requisite parameters and calls the IOCTL handler. | ||
1148 | * | ||
1149 | * The function follows the following steps to perform the change - | ||
1150 | * - Get current IBSS information | ||
1151 | * - Get current channel | ||
1152 | * - If no change is required, return | ||
1153 | * - If not connected, change channel and return | ||
1154 | * - If connected, | ||
1155 | * - Disconnect | ||
1156 | * - Change channel | ||
1157 | * - Perform specific SSID scan with same SSID | ||
1158 | * - Start/Join the IBSS | ||
1159 | */ | ||
1160 | int | ||
1161 | mwifiex_drv_change_adhoc_chan(struct mwifiex_private *priv, int channel) | ||
1162 | { | ||
1163 | int ret = 0; | ||
1164 | int status = 0; | ||
1165 | struct mwifiex_bss_info bss_info; | ||
1166 | struct mwifiex_wait_queue *wait = NULL; | ||
1167 | u8 wait_option = MWIFIEX_IOCTL_WAIT; | ||
1168 | struct mwifiex_ssid_bssid ssid_bssid; | ||
1169 | u16 curr_chan = 0; | ||
1170 | |||
1171 | memset(&bss_info, 0, sizeof(bss_info)); | ||
1172 | |||
1173 | /* Get BSS information */ | ||
1174 | if (mwifiex_get_bss_info(priv, &bss_info)) | ||
1175 | return -1; | ||
1176 | |||
1177 | /* Allocate wait buffer */ | ||
1178 | wait = mwifiex_alloc_fill_wait_queue(priv, wait_option); | ||
1179 | if (!wait) | ||
1180 | return -ENOMEM; | ||
1181 | |||
1182 | /* Get current channel */ | ||
1183 | status = mwifiex_bss_ioctl_ibss_channel(priv, wait, HostCmd_ACT_GEN_GET, | ||
1184 | &curr_chan); | ||
1185 | |||
1186 | if (mwifiex_request_ioctl(priv, wait, status, wait_option)) { | ||
1187 | ret = -1; | ||
1188 | goto done; | ||
1189 | } | ||
1190 | if (curr_chan == channel) { | ||
1191 | ret = 0; | ||
1192 | goto done; | ||
1193 | } | ||
1194 | dev_dbg(priv->adapter->dev, "cmd: updating channel from %d to %d\n", | ||
1195 | curr_chan, channel); | ||
1196 | |||
1197 | if (!bss_info.media_connected) { | ||
1198 | ret = 0; | ||
1199 | goto done; | ||
1200 | } | ||
1201 | |||
1202 | /* Do disonnect */ | ||
1203 | memset(&ssid_bssid, 0, ETH_ALEN); | ||
1204 | status = mwifiex_bss_ioctl_stop(priv, wait, ssid_bssid.bssid); | ||
1205 | |||
1206 | if (mwifiex_request_ioctl(priv, wait, status, wait_option)) { | ||
1207 | ret = -1; | ||
1208 | goto done; | ||
1209 | } | ||
1210 | |||
1211 | status = mwifiex_bss_ioctl_ibss_channel(priv, wait, HostCmd_ACT_GEN_SET, | ||
1212 | (u16 *) &channel); | ||
1213 | |||
1214 | if (mwifiex_request_ioctl(priv, wait, status, wait_option)) { | ||
1215 | ret = -1; | ||
1216 | goto done; | ||
1217 | } | ||
1218 | |||
1219 | /* Do specific SSID scanning */ | ||
1220 | if (mwifiex_request_scan(priv, wait_option, &bss_info.ssid)) { | ||
1221 | ret = -1; | ||
1222 | goto done; | ||
1223 | } | ||
1224 | /* Start/Join Adhoc network */ | ||
1225 | memset(&ssid_bssid, 0, sizeof(struct mwifiex_ssid_bssid)); | ||
1226 | memcpy(&ssid_bssid.ssid, &bss_info.ssid, | ||
1227 | sizeof(struct mwifiex_802_11_ssid)); | ||
1228 | |||
1229 | status = mwifiex_bss_ioctl_start(priv, wait, &ssid_bssid); | ||
1230 | |||
1231 | if (mwifiex_request_ioctl(priv, wait, status, wait_option)) | ||
1232 | ret = -1; | ||
1233 | |||
1234 | done: | ||
1235 | kfree(wait); | ||
1236 | return ret; | ||
1237 | } | ||
1238 | |||
1239 | /* | ||
1240 | * IOCTL request handler to get current driver mode. | ||
1241 | * | ||
1242 | * This function allocates the IOCTL request buffer, fills it | ||
1243 | * with requisite parameters and calls the IOCTL handler. | ||
1244 | */ | ||
1245 | int | ||
1246 | mwifiex_drv_get_mode(struct mwifiex_private *priv, u8 wait_option) | ||
1247 | { | ||
1248 | struct mwifiex_wait_queue *wait = NULL; | ||
1249 | int status = 0; | ||
1250 | int mode = -1; | ||
1251 | |||
1252 | /* Allocate wait buffer */ | ||
1253 | wait = mwifiex_alloc_fill_wait_queue(priv, wait_option); | ||
1254 | if (!wait) | ||
1255 | return -1; | ||
1256 | |||
1257 | status = mwifiex_bss_ioctl_mode(priv, wait, HostCmd_ACT_GEN_GET, &mode); | ||
1258 | |||
1259 | status = mwifiex_request_ioctl(priv, wait, status, wait_option); | ||
1260 | |||
1261 | if (wait && (status != -EINPROGRESS)) | ||
1262 | kfree(wait); | ||
1263 | return mode; | ||
1264 | } | ||
1265 | |||
1266 | /* | ||
1267 | * IOCTL request handler to get rate. | ||
1268 | * | ||
1269 | * This function prepares the correct firmware command and | ||
1270 | * issues it to get the current rate if it is connected, | ||
1271 | * otherwise, the function returns the lowest supported rate | ||
1272 | * for the band. | ||
1273 | */ | ||
1274 | static int mwifiex_rate_ioctl_get_rate_value(struct mwifiex_private *priv, | ||
1275 | struct mwifiex_wait_queue *wait, | ||
1276 | struct mwifiex_rate_cfg *rate_cfg) | ||
1277 | { | ||
1278 | struct mwifiex_adapter *adapter = priv->adapter; | ||
1279 | int ret = 0; | ||
1280 | |||
1281 | rate_cfg->is_rate_auto = priv->is_data_rate_auto; | ||
1282 | if (!priv->media_connected) { | ||
1283 | switch (adapter->config_bands) { | ||
1284 | case BAND_B: | ||
1285 | /* Return the lowest supported rate for B band */ | ||
1286 | rate_cfg->rate = supported_rates_b[0] & 0x7f; | ||
1287 | break; | ||
1288 | case BAND_G: | ||
1289 | case BAND_G | BAND_GN: | ||
1290 | /* Return the lowest supported rate for G band */ | ||
1291 | rate_cfg->rate = supported_rates_g[0] & 0x7f; | ||
1292 | break; | ||
1293 | case BAND_B | BAND_G: | ||
1294 | case BAND_A | BAND_B | BAND_G: | ||
1295 | case BAND_A | BAND_B: | ||
1296 | case BAND_A | BAND_B | BAND_G | BAND_AN | BAND_GN: | ||
1297 | case BAND_B | BAND_G | BAND_GN: | ||
1298 | /* Return the lowest supported rate for BG band */ | ||
1299 | rate_cfg->rate = supported_rates_bg[0] & 0x7f; | ||
1300 | break; | ||
1301 | case BAND_A: | ||
1302 | case BAND_A | BAND_G: | ||
1303 | case BAND_A | BAND_G | BAND_AN | BAND_GN: | ||
1304 | case BAND_A | BAND_AN: | ||
1305 | /* Return the lowest supported rate for A band */ | ||
1306 | rate_cfg->rate = supported_rates_a[0] & 0x7f; | ||
1307 | break; | ||
1308 | case BAND_GN: | ||
1309 | /* Return the lowest supported rate for N band */ | ||
1310 | rate_cfg->rate = supported_rates_n[0] & 0x7f; | ||
1311 | break; | ||
1312 | default: | ||
1313 | dev_warn(adapter->dev, "invalid band %#x\n", | ||
1314 | adapter->config_bands); | ||
1315 | break; | ||
1316 | } | ||
1317 | } else { | ||
1318 | /* Send request to firmware */ | ||
1319 | ret = mwifiex_prepare_cmd(priv, | ||
1320 | HostCmd_CMD_802_11_TX_RATE_QUERY, | ||
1321 | HostCmd_ACT_GEN_GET, 0, wait, NULL); | ||
1322 | if (!ret) | ||
1323 | ret = -EINPROGRESS; | ||
1324 | } | ||
1325 | |||
1326 | return ret; | ||
1327 | } | ||
1328 | |||
1329 | /* | ||
1330 | * IOCTL request handler to set rate. | ||
1331 | * | ||
1332 | * This function prepares the correct firmware command and | ||
1333 | * issues it to set the current rate. | ||
1334 | * | ||
1335 | * The function also performs validation checking on the supplied value. | ||
1336 | */ | ||
1337 | static int mwifiex_rate_ioctl_set_rate_value(struct mwifiex_private *priv, | ||
1338 | struct mwifiex_wait_queue *wait, | ||
1339 | struct mwifiex_rate_cfg *rate_cfg) | ||
1340 | { | ||
1341 | u8 rates[MWIFIEX_SUPPORTED_RATES]; | ||
1342 | u8 *rate = NULL; | ||
1343 | int rate_index = 0; | ||
1344 | u16 bitmap_rates[MAX_BITMAP_RATES_SIZE]; | ||
1345 | u32 i = 0; | ||
1346 | int ret = 0; | ||
1347 | struct mwifiex_adapter *adapter = priv->adapter; | ||
1348 | |||
1349 | if (rate_cfg->is_rate_auto) { | ||
1350 | memset(bitmap_rates, 0, sizeof(bitmap_rates)); | ||
1351 | /* Support all HR/DSSS rates */ | ||
1352 | bitmap_rates[0] = 0x000F; | ||
1353 | /* Support all OFDM rates */ | ||
1354 | bitmap_rates[1] = 0x00FF; | ||
1355 | /* Support all HT-MCSs rate */ | ||
1356 | for (i = 0; i < ARRAY_SIZE(priv->bitmap_rates) - 3; i++) | ||
1357 | bitmap_rates[i + 2] = 0xFFFF; | ||
1358 | bitmap_rates[9] = 0x3FFF; | ||
1359 | } else { | ||
1360 | memset(rates, 0, sizeof(rates)); | ||
1361 | mwifiex_get_active_data_rates(priv, rates); | ||
1362 | rate = rates; | ||
1363 | for (i = 0; (rate[i] && i < MWIFIEX_SUPPORTED_RATES); i++) { | ||
1364 | dev_dbg(adapter->dev, "info: rate=%#x wanted=%#x\n", | ||
1365 | rate[i], rate_cfg->rate); | ||
1366 | if ((rate[i] & 0x7f) == (rate_cfg->rate & 0x7f)) | ||
1367 | break; | ||
1368 | } | ||
1369 | if (!rate[i] || (i == MWIFIEX_SUPPORTED_RATES)) { | ||
1370 | dev_err(adapter->dev, "fixed data rate %#x is out " | ||
1371 | "of range\n", rate_cfg->rate); | ||
1372 | return -1; | ||
1373 | } | ||
1374 | memset(bitmap_rates, 0, sizeof(bitmap_rates)); | ||
1375 | |||
1376 | rate_index = | ||
1377 | mwifiex_data_rate_to_index(adapter, rate_cfg->rate); | ||
1378 | |||
1379 | /* Only allow b/g rates to be set */ | ||
1380 | if (rate_index >= MWIFIEX_RATE_INDEX_HRDSSS0 && | ||
1381 | rate_index <= MWIFIEX_RATE_INDEX_HRDSSS3) { | ||
1382 | bitmap_rates[0] = 1 << rate_index; | ||
1383 | } else { | ||
1384 | rate_index -= 1; /* There is a 0x00 in the table */ | ||
1385 | if (rate_index >= MWIFIEX_RATE_INDEX_OFDM0 && | ||
1386 | rate_index <= MWIFIEX_RATE_INDEX_OFDM7) | ||
1387 | bitmap_rates[1] = 1 << (rate_index - | ||
1388 | MWIFIEX_RATE_INDEX_OFDM0); | ||
1389 | } | ||
1390 | } | ||
1391 | |||
1392 | /* Send request to firmware */ | ||
1393 | ret = mwifiex_prepare_cmd(priv, HostCmd_CMD_TX_RATE_CFG, | ||
1394 | HostCmd_ACT_GEN_SET, 0, wait, bitmap_rates); | ||
1395 | if (!ret) | ||
1396 | ret = -EINPROGRESS; | ||
1397 | |||
1398 | return ret; | ||
1399 | } | ||
1400 | |||
1401 | /* | ||
1402 | * IOCTL request handler to set/get rate. | ||
1403 | * | ||
1404 | * This function can be used to set/get either the rate value or the | ||
1405 | * rate index. | ||
1406 | */ | ||
1407 | static int mwifiex_rate_ioctl_cfg(struct mwifiex_private *priv, | ||
1408 | struct mwifiex_wait_queue *wait, | ||
1409 | struct mwifiex_rate_cfg *rate_cfg) | ||
1410 | { | ||
1411 | int status = 0; | ||
1412 | |||
1413 | if (!rate_cfg) | ||
1414 | return -1; | ||
1415 | |||
1416 | if (rate_cfg->action == HostCmd_ACT_GEN_GET) | ||
1417 | status = mwifiex_rate_ioctl_get_rate_value( | ||
1418 | priv, wait, rate_cfg); | ||
1419 | else | ||
1420 | status = mwifiex_rate_ioctl_set_rate_value( | ||
1421 | priv, wait, rate_cfg); | ||
1422 | |||
1423 | return status; | ||
1424 | } | ||
1425 | |||
1426 | /* | ||
1427 | * Sends IOCTL request to get the data rate. | ||
1428 | * | ||
1429 | * This function allocates the IOCTL request buffer, fills it | ||
1430 | * with requisite parameters and calls the IOCTL handler. | ||
1431 | */ | ||
1432 | int mwifiex_drv_get_data_rate(struct mwifiex_private *priv, | ||
1433 | struct mwifiex_rate_cfg *rate) | ||
1434 | { | ||
1435 | int ret = 0; | ||
1436 | struct mwifiex_wait_queue *wait = NULL; | ||
1437 | u8 wait_option = MWIFIEX_IOCTL_WAIT; | ||
1438 | |||
1439 | /* Allocate wait buffer */ | ||
1440 | wait = mwifiex_alloc_fill_wait_queue(priv, wait_option); | ||
1441 | if (!wait) | ||
1442 | return -ENOMEM; | ||
1443 | |||
1444 | memset(rate, 0, sizeof(struct mwifiex_rate_cfg)); | ||
1445 | rate->action = HostCmd_ACT_GEN_GET; | ||
1446 | ret = mwifiex_rate_ioctl_cfg(priv, wait, rate); | ||
1447 | |||
1448 | ret = mwifiex_request_ioctl(priv, wait, ret, wait_option); | ||
1449 | if (!ret) { | ||
1450 | if (rate && rate->is_rate_auto) | ||
1451 | rate->rate = mwifiex_index_to_data_rate(priv->adapter, | ||
1452 | priv->tx_rate, priv->tx_htinfo); | ||
1453 | else if (rate) | ||
1454 | rate->rate = priv->data_rate; | ||
1455 | } else { | ||
1456 | ret = -1; | ||
1457 | } | ||
1458 | |||
1459 | kfree(wait); | ||
1460 | return ret; | ||
1461 | } | ||
1462 | |||
1463 | /* | ||
1464 | * IOCTL request handler to set tx power configuration. | ||
1465 | * | ||
1466 | * This function prepares the correct firmware command and | ||
1467 | * issues it. | ||
1468 | * | ||
1469 | * For non-auto power mode, all the following power groups are set - | ||
1470 | * - Modulation class HR/DSSS | ||
1471 | * - Modulation class OFDM | ||
1472 | * - Modulation class HTBW20 | ||
1473 | * - Modulation class HTBW40 | ||
1474 | */ | ||
1475 | static int mwifiex_power_ioctl_set_power(struct mwifiex_private *priv, | ||
1476 | struct mwifiex_wait_queue *wait, | ||
1477 | struct mwifiex_power_cfg *power_cfg) | ||
1478 | { | ||
1479 | int ret = 0; | ||
1480 | struct host_cmd_ds_txpwr_cfg *txp_cfg = NULL; | ||
1481 | struct mwifiex_types_power_group *pg_tlv = NULL; | ||
1482 | struct mwifiex_power_group *pg = NULL; | ||
1483 | u8 *buf = NULL; | ||
1484 | u16 dbm = 0; | ||
1485 | |||
1486 | if (!power_cfg->is_power_auto) { | ||
1487 | dbm = (u16) power_cfg->power_level; | ||
1488 | if ((dbm < priv->min_tx_power_level) || | ||
1489 | (dbm > priv->max_tx_power_level)) { | ||
1490 | dev_err(priv->adapter->dev, "txpower value %d dBm" | ||
1491 | " is out of range (%d dBm-%d dBm)\n", | ||
1492 | dbm, priv->min_tx_power_level, | ||
1493 | priv->max_tx_power_level); | ||
1494 | return -1; | ||
1495 | } | ||
1496 | } | ||
1497 | buf = kzalloc(MWIFIEX_SIZE_OF_CMD_BUFFER, GFP_KERNEL); | ||
1498 | if (!buf) { | ||
1499 | dev_err(priv->adapter->dev, "%s: failed to alloc cmd buffer\n", | ||
1500 | __func__); | ||
1501 | return -1; | ||
1502 | } | ||
1503 | |||
1504 | txp_cfg = (struct host_cmd_ds_txpwr_cfg *) buf; | ||
1505 | txp_cfg->action = cpu_to_le16(HostCmd_ACT_GEN_SET); | ||
1506 | if (!power_cfg->is_power_auto) { | ||
1507 | txp_cfg->mode = cpu_to_le32(1); | ||
1508 | pg_tlv = (struct mwifiex_types_power_group *) (buf + | ||
1509 | sizeof(struct host_cmd_ds_txpwr_cfg)); | ||
1510 | pg_tlv->type = TLV_TYPE_POWER_GROUP; | ||
1511 | pg_tlv->length = 4 * sizeof(struct mwifiex_power_group); | ||
1512 | pg = (struct mwifiex_power_group *) (buf + | ||
1513 | sizeof(struct host_cmd_ds_txpwr_cfg) + | ||
1514 | sizeof(struct mwifiex_types_power_group)); | ||
1515 | /* Power group for modulation class HR/DSSS */ | ||
1516 | pg->first_rate_code = 0x00; | ||
1517 | pg->last_rate_code = 0x03; | ||
1518 | pg->modulation_class = MOD_CLASS_HR_DSSS; | ||
1519 | pg->power_step = 0; | ||
1520 | pg->power_min = (s8) dbm; | ||
1521 | pg->power_max = (s8) dbm; | ||
1522 | pg++; | ||
1523 | /* Power group for modulation class OFDM */ | ||
1524 | pg->first_rate_code = 0x00; | ||
1525 | pg->last_rate_code = 0x07; | ||
1526 | pg->modulation_class = MOD_CLASS_OFDM; | ||
1527 | pg->power_step = 0; | ||
1528 | pg->power_min = (s8) dbm; | ||
1529 | pg->power_max = (s8) dbm; | ||
1530 | pg++; | ||
1531 | /* Power group for modulation class HTBW20 */ | ||
1532 | pg->first_rate_code = 0x00; | ||
1533 | pg->last_rate_code = 0x20; | ||
1534 | pg->modulation_class = MOD_CLASS_HT; | ||
1535 | pg->power_step = 0; | ||
1536 | pg->power_min = (s8) dbm; | ||
1537 | pg->power_max = (s8) dbm; | ||
1538 | pg->ht_bandwidth = HT_BW_20; | ||
1539 | pg++; | ||
1540 | /* Power group for modulation class HTBW40 */ | ||
1541 | pg->first_rate_code = 0x00; | ||
1542 | pg->last_rate_code = 0x20; | ||
1543 | pg->modulation_class = MOD_CLASS_HT; | ||
1544 | pg->power_step = 0; | ||
1545 | pg->power_min = (s8) dbm; | ||
1546 | pg->power_max = (s8) dbm; | ||
1547 | pg->ht_bandwidth = HT_BW_40; | ||
1548 | } | ||
1549 | /* Send request to firmware */ | ||
1550 | ret = mwifiex_prepare_cmd(priv, HostCmd_CMD_TXPWR_CFG, | ||
1551 | HostCmd_ACT_GEN_SET, 0, wait, buf); | ||
1552 | if (!ret) | ||
1553 | ret = -EINPROGRESS; | ||
1554 | kfree(buf); | ||
1555 | |||
1556 | return ret; | ||
1557 | } | ||
1558 | |||
1559 | /* | ||
1560 | * IOCTL request handler to get power save mode. | ||
1561 | * | ||
1562 | * This function prepares the correct firmware command and | ||
1563 | * issues it. | ||
1564 | */ | ||
1565 | static int mwifiex_pm_ioctl_ps_mode(struct mwifiex_private *priv, | ||
1566 | struct mwifiex_wait_queue *wait, | ||
1567 | u32 *ps_mode, u16 action) | ||
1568 | { | ||
1569 | int ret = 0; | ||
1570 | struct mwifiex_adapter *adapter = priv->adapter; | ||
1571 | u16 sub_cmd; | ||
1572 | |||
1573 | if (action == HostCmd_ACT_GEN_SET) { | ||
1574 | if (*ps_mode) | ||
1575 | adapter->ps_mode = MWIFIEX_802_11_POWER_MODE_PSP; | ||
1576 | else | ||
1577 | adapter->ps_mode = MWIFIEX_802_11_POWER_MODE_CAM; | ||
1578 | sub_cmd = (*ps_mode) ? EN_AUTO_PS : DIS_AUTO_PS; | ||
1579 | ret = mwifiex_prepare_cmd(priv, HostCmd_CMD_802_11_PS_MODE_ENH, | ||
1580 | sub_cmd, BITMAP_STA_PS, wait, NULL); | ||
1581 | if ((!ret) && (sub_cmd == DIS_AUTO_PS)) | ||
1582 | ret = mwifiex_prepare_cmd(priv, | ||
1583 | HostCmd_CMD_802_11_PS_MODE_ENH, GET_PS, | ||
1584 | 0, NULL, NULL); | ||
1585 | } else { | ||
1586 | ret = mwifiex_prepare_cmd(priv, HostCmd_CMD_802_11_PS_MODE_ENH, | ||
1587 | GET_PS, 0, wait, NULL); | ||
1588 | } | ||
1589 | |||
1590 | if (!ret) | ||
1591 | ret = -EINPROGRESS; | ||
1592 | |||
1593 | return ret; | ||
1594 | } | ||
1595 | |||
1596 | /* | ||
1597 | * IOCTL request handler to set/reset WPA IE. | ||
1598 | * | ||
1599 | * The supplied WPA IE is treated as a opaque buffer. Only the first field | ||
1600 | * is checked to determine WPA version. If buffer length is zero, the existing | ||
1601 | * WPA IE is reset. | ||
1602 | */ | ||
1603 | static int mwifiex_set_wpa_ie_helper(struct mwifiex_private *priv, | ||
1604 | u8 *ie_data_ptr, u16 ie_len) | ||
1605 | { | ||
1606 | if (ie_len) { | ||
1607 | if (ie_len > sizeof(priv->wpa_ie)) { | ||
1608 | dev_err(priv->adapter->dev, | ||
1609 | "failed to copy WPA IE, too big\n"); | ||
1610 | return -1; | ||
1611 | } | ||
1612 | memcpy(priv->wpa_ie, ie_data_ptr, ie_len); | ||
1613 | priv->wpa_ie_len = (u8) ie_len; | ||
1614 | dev_dbg(priv->adapter->dev, "cmd: Set Wpa_ie_len=%d IE=%#x\n", | ||
1615 | priv->wpa_ie_len, priv->wpa_ie[0]); | ||
1616 | |||
1617 | if (priv->wpa_ie[0] == WLAN_EID_WPA) { | ||
1618 | priv->sec_info.wpa_enabled = true; | ||
1619 | } else if (priv->wpa_ie[0] == WLAN_EID_RSN) { | ||
1620 | priv->sec_info.wpa2_enabled = true; | ||
1621 | } else { | ||
1622 | priv->sec_info.wpa_enabled = false; | ||
1623 | priv->sec_info.wpa2_enabled = false; | ||
1624 | } | ||
1625 | } else { | ||
1626 | memset(priv->wpa_ie, 0, sizeof(priv->wpa_ie)); | ||
1627 | priv->wpa_ie_len = 0; | ||
1628 | dev_dbg(priv->adapter->dev, "info: reset wpa_ie_len=%d IE=%#x\n", | ||
1629 | priv->wpa_ie_len, priv->wpa_ie[0]); | ||
1630 | priv->sec_info.wpa_enabled = false; | ||
1631 | priv->sec_info.wpa2_enabled = false; | ||
1632 | } | ||
1633 | |||
1634 | return 0; | ||
1635 | } | ||
1636 | |||
1637 | /* | ||
1638 | * IOCTL request handler to set/reset WAPI IE. | ||
1639 | * | ||
1640 | * The supplied WAPI IE is treated as a opaque buffer. Only the first field | ||
1641 | * is checked to internally enable WAPI. If buffer length is zero, the existing | ||
1642 | * WAPI IE is reset. | ||
1643 | */ | ||
1644 | static int mwifiex_set_wapi_ie(struct mwifiex_private *priv, | ||
1645 | u8 *ie_data_ptr, u16 ie_len) | ||
1646 | { | ||
1647 | if (ie_len) { | ||
1648 | if (ie_len > sizeof(priv->wapi_ie)) { | ||
1649 | dev_dbg(priv->adapter->dev, | ||
1650 | "info: failed to copy WAPI IE, too big\n"); | ||
1651 | return -1; | ||
1652 | } | ||
1653 | memcpy(priv->wapi_ie, ie_data_ptr, ie_len); | ||
1654 | priv->wapi_ie_len = ie_len; | ||
1655 | dev_dbg(priv->adapter->dev, "cmd: Set wapi_ie_len=%d IE=%#x\n", | ||
1656 | priv->wapi_ie_len, priv->wapi_ie[0]); | ||
1657 | |||
1658 | if (priv->wapi_ie[0] == WLAN_EID_BSS_AC_ACCESS_DELAY) | ||
1659 | priv->sec_info.wapi_enabled = true; | ||
1660 | } else { | ||
1661 | memset(priv->wapi_ie, 0, sizeof(priv->wapi_ie)); | ||
1662 | priv->wapi_ie_len = ie_len; | ||
1663 | dev_dbg(priv->adapter->dev, | ||
1664 | "info: Reset wapi_ie_len=%d IE=%#x\n", | ||
1665 | priv->wapi_ie_len, priv->wapi_ie[0]); | ||
1666 | priv->sec_info.wapi_enabled = false; | ||
1667 | } | ||
1668 | return 0; | ||
1669 | } | ||
1670 | |||
1671 | /* | ||
1672 | * IOCTL request handler to set WAPI key. | ||
1673 | * | ||
1674 | * This function prepares the correct firmware command and | ||
1675 | * issues it. | ||
1676 | */ | ||
1677 | static int mwifiex_sec_ioctl_set_wapi_key(struct mwifiex_adapter *adapter, | ||
1678 | struct mwifiex_wait_queue *wait, | ||
1679 | struct mwifiex_ds_encrypt_key *encrypt_key) | ||
1680 | { | ||
1681 | int ret = 0; | ||
1682 | struct mwifiex_private *priv = adapter->priv[wait->bss_index]; | ||
1683 | |||
1684 | ret = mwifiex_prepare_cmd(priv, HostCmd_CMD_802_11_KEY_MATERIAL, | ||
1685 | HostCmd_ACT_GEN_SET, KEY_INFO_ENABLED, | ||
1686 | wait, encrypt_key); | ||
1687 | if (!ret) | ||
1688 | ret = -EINPROGRESS; | ||
1689 | |||
1690 | return ret; | ||
1691 | } | ||
1692 | |||
1693 | /* | ||
1694 | * IOCTL request handler to set/get authentication mode. | ||
1695 | */ | ||
1696 | static int mwifiex_set_auth_mode(struct mwifiex_private *priv, u32 auth_mode) | ||
1697 | { | ||
1698 | int ret = 0; | ||
1699 | |||
1700 | priv->sec_info.authentication_mode = auth_mode; | ||
1701 | if (priv->sec_info.authentication_mode == MWIFIEX_AUTH_MODE_NETWORKEAP) | ||
1702 | ret = mwifiex_set_wpa_ie_helper(priv, NULL, 0); | ||
1703 | |||
1704 | return ret; | ||
1705 | } | ||
1706 | |||
1707 | /* | ||
1708 | * IOCTL request handler to set WEP network key. | ||
1709 | * | ||
1710 | * This function prepares the correct firmware command and | ||
1711 | * issues it, after validation checks. | ||
1712 | */ | ||
1713 | static int mwifiex_sec_ioctl_set_wep_key(struct mwifiex_adapter *adapter, | ||
1714 | struct mwifiex_wait_queue *wait, | ||
1715 | struct mwifiex_ds_encrypt_key *encrypt_key) | ||
1716 | { | ||
1717 | int ret = 0; | ||
1718 | struct mwifiex_private *priv = adapter->priv[wait->bss_index]; | ||
1719 | struct mwifiex_wep_key *wep_key = NULL; | ||
1720 | int index; | ||
1721 | |||
1722 | if (priv->wep_key_curr_index >= NUM_WEP_KEYS) | ||
1723 | priv->wep_key_curr_index = 0; | ||
1724 | wep_key = &priv->wep_key[priv->wep_key_curr_index]; | ||
1725 | index = encrypt_key->key_index; | ||
1726 | if (encrypt_key->key_disable) { | ||
1727 | priv->sec_info.wep_status = MWIFIEX_802_11_WEP_DISABLED; | ||
1728 | } else if (!encrypt_key->key_len) { | ||
1729 | /* Copy the required key as the current key */ | ||
1730 | wep_key = &priv->wep_key[index]; | ||
1731 | if (!wep_key->key_length) { | ||
1732 | dev_err(adapter->dev, | ||
1733 | "key not set, so cannot enable it\n"); | ||
1734 | return -1; | ||
1735 | } | ||
1736 | priv->wep_key_curr_index = (u16) index; | ||
1737 | priv->sec_info.wep_status = MWIFIEX_802_11_WEP_ENABLED; | ||
1738 | } else { | ||
1739 | wep_key = &priv->wep_key[index]; | ||
1740 | /* Cleanup */ | ||
1741 | memset(wep_key, 0, sizeof(struct mwifiex_wep_key)); | ||
1742 | /* Copy the key in the driver */ | ||
1743 | memcpy(wep_key->key_material, | ||
1744 | encrypt_key->key_material, | ||
1745 | encrypt_key->key_len); | ||
1746 | wep_key->key_index = index; | ||
1747 | wep_key->key_length = encrypt_key->key_len; | ||
1748 | priv->sec_info.wep_status = MWIFIEX_802_11_WEP_ENABLED; | ||
1749 | } | ||
1750 | if (wep_key->key_length) { | ||
1751 | /* Send request to firmware */ | ||
1752 | ret = mwifiex_prepare_cmd(priv, HostCmd_CMD_802_11_KEY_MATERIAL, | ||
1753 | HostCmd_ACT_GEN_SET, 0, NULL, NULL); | ||
1754 | if (ret) | ||
1755 | return ret; | ||
1756 | } | ||
1757 | if (priv->sec_info.wep_status == MWIFIEX_802_11_WEP_ENABLED) | ||
1758 | priv->curr_pkt_filter |= HostCmd_ACT_MAC_WEP_ENABLE; | ||
1759 | else | ||
1760 | priv->curr_pkt_filter &= ~HostCmd_ACT_MAC_WEP_ENABLE; | ||
1761 | |||
1762 | /* Send request to firmware */ | ||
1763 | ret = mwifiex_prepare_cmd(priv, HostCmd_CMD_MAC_CONTROL, | ||
1764 | HostCmd_ACT_GEN_SET, 0, wait, | ||
1765 | &priv->curr_pkt_filter); | ||
1766 | if (!ret) | ||
1767 | ret = -EINPROGRESS; | ||
1768 | |||
1769 | return ret; | ||
1770 | } | ||
1771 | |||
1772 | /* | ||
1773 | * IOCTL request handler to set WPA key. | ||
1774 | * | ||
1775 | * This function prepares the correct firmware command and | ||
1776 | * issues it, after validation checks. | ||
1777 | * | ||
1778 | * Current driver only supports key length of up to 32 bytes. | ||
1779 | * | ||
1780 | * This function can also be used to disable a currently set key. | ||
1781 | */ | ||
1782 | static int mwifiex_sec_ioctl_set_wpa_key(struct mwifiex_adapter *adapter, | ||
1783 | struct mwifiex_wait_queue *wait, | ||
1784 | struct mwifiex_ds_encrypt_key *encrypt_key) | ||
1785 | { | ||
1786 | int ret = 0; | ||
1787 | struct mwifiex_private *priv = adapter->priv[wait->bss_index]; | ||
1788 | u8 remove_key = false; | ||
1789 | struct host_cmd_ds_802_11_key_material *ibss_key; | ||
1790 | |||
1791 | /* Current driver only supports key length of up to 32 bytes */ | ||
1792 | if (encrypt_key->key_len > MWIFIEX_MAX_KEY_LENGTH) { | ||
1793 | dev_err(adapter->dev, "key length too long\n"); | ||
1794 | return -1; | ||
1795 | } | ||
1796 | |||
1797 | if (priv->bss_mode == MWIFIEX_BSS_MODE_IBSS) { | ||
1798 | /* | ||
1799 | * IBSS/WPA-None uses only one key (Group) for both receiving | ||
1800 | * and sending unicast and multicast packets. | ||
1801 | */ | ||
1802 | /* Send the key as PTK to firmware */ | ||
1803 | encrypt_key->key_index = MWIFIEX_KEY_INDEX_UNICAST; | ||
1804 | ret = mwifiex_prepare_cmd(priv, HostCmd_CMD_802_11_KEY_MATERIAL, | ||
1805 | HostCmd_ACT_GEN_SET, KEY_INFO_ENABLED, | ||
1806 | NULL, encrypt_key); | ||
1807 | if (ret) | ||
1808 | return ret; | ||
1809 | |||
1810 | ibss_key = &priv->aes_key; | ||
1811 | memset(ibss_key, 0, | ||
1812 | sizeof(struct host_cmd_ds_802_11_key_material)); | ||
1813 | /* Copy the key in the driver */ | ||
1814 | memcpy(ibss_key->key_param_set.key, encrypt_key->key_material, | ||
1815 | encrypt_key->key_len); | ||
1816 | memcpy(&ibss_key->key_param_set.key_len, &encrypt_key->key_len, | ||
1817 | sizeof(ibss_key->key_param_set.key_len)); | ||
1818 | ibss_key->key_param_set.key_type_id | ||
1819 | = cpu_to_le16(KEY_TYPE_ID_TKIP); | ||
1820 | ibss_key->key_param_set.key_info | ||
1821 | = cpu_to_le16(KEY_INFO_TKIP_ENABLED); | ||
1822 | |||
1823 | /* Send the key as GTK to firmware */ | ||
1824 | encrypt_key->key_index = ~MWIFIEX_KEY_INDEX_UNICAST; | ||
1825 | } | ||
1826 | |||
1827 | if (!encrypt_key->key_index) | ||
1828 | encrypt_key->key_index = MWIFIEX_KEY_INDEX_UNICAST; | ||
1829 | |||
1830 | if (remove_key) | ||
1831 | /* Send request to firmware */ | ||
1832 | ret = mwifiex_prepare_cmd(priv, HostCmd_CMD_802_11_KEY_MATERIAL, | ||
1833 | HostCmd_ACT_GEN_SET, | ||
1834 | !(KEY_INFO_ENABLED), | ||
1835 | wait, encrypt_key); | ||
1836 | else | ||
1837 | /* Send request to firmware */ | ||
1838 | ret = mwifiex_prepare_cmd(priv, HostCmd_CMD_802_11_KEY_MATERIAL, | ||
1839 | HostCmd_ACT_GEN_SET, KEY_INFO_ENABLED, | ||
1840 | wait, encrypt_key); | ||
1841 | |||
1842 | if (!ret) | ||
1843 | ret = -EINPROGRESS; | ||
1844 | |||
1845 | return ret; | ||
1846 | } | ||
1847 | |||
1848 | /* | ||
1849 | * IOCTL request handler to set/get network keys. | ||
1850 | * | ||
1851 | * This is a generic key handling function which supports WEP, WPA | ||
1852 | * and WAPI. | ||
1853 | */ | ||
1854 | static int | ||
1855 | mwifiex_sec_ioctl_encrypt_key(struct mwifiex_private *priv, | ||
1856 | struct mwifiex_wait_queue *wait, | ||
1857 | struct mwifiex_ds_encrypt_key *encrypt_key) | ||
1858 | { | ||
1859 | int status = 0; | ||
1860 | struct mwifiex_adapter *adapter = priv->adapter; | ||
1861 | |||
1862 | if (encrypt_key->is_wapi_key) | ||
1863 | status = mwifiex_sec_ioctl_set_wapi_key(adapter, wait, | ||
1864 | encrypt_key); | ||
1865 | else if (encrypt_key->key_len > WLAN_KEY_LEN_WEP104) | ||
1866 | status = mwifiex_sec_ioctl_set_wpa_key(adapter, wait, | ||
1867 | encrypt_key); | ||
1868 | else | ||
1869 | status = mwifiex_sec_ioctl_set_wep_key(adapter, wait, | ||
1870 | encrypt_key); | ||
1871 | return status; | ||
1872 | } | ||
1873 | |||
1874 | /* | ||
1875 | * This function returns the driver version. | ||
1876 | */ | ||
1877 | int | ||
1878 | mwifiex_drv_get_driver_version(struct mwifiex_adapter *adapter, char *version, | ||
1879 | int max_len) | ||
1880 | { | ||
1881 | union { | ||
1882 | u32 l; | ||
1883 | u8 c[4]; | ||
1884 | } ver; | ||
1885 | char fw_ver[32]; | ||
1886 | |||
1887 | ver.l = adapter->fw_release_number; | ||
1888 | sprintf(fw_ver, "%u.%u.%u.p%u", ver.c[2], ver.c[1], ver.c[0], ver.c[3]); | ||
1889 | |||
1890 | snprintf(version, max_len, driver_version, fw_ver); | ||
1891 | |||
1892 | dev_dbg(adapter->dev, "info: MWIFIEX VERSION: %s\n", version); | ||
1893 | |||
1894 | return 0; | ||
1895 | } | ||
1896 | |||
1897 | /* | ||
1898 | * Sends IOCTL request to set Tx power. It can be set to either auto | ||
1899 | * or a fixed value. | ||
1900 | * | ||
1901 | * This function allocates the IOCTL request buffer, fills it | ||
1902 | * with requisite parameters and calls the IOCTL handler. | ||
1903 | */ | ||
1904 | int | ||
1905 | mwifiex_set_tx_power(struct mwifiex_private *priv, int type, int dbm) | ||
1906 | { | ||
1907 | struct mwifiex_power_cfg power_cfg; | ||
1908 | struct mwifiex_wait_queue *wait = NULL; | ||
1909 | int status = 0; | ||
1910 | int ret = 0; | ||
1911 | |||
1912 | wait = mwifiex_alloc_fill_wait_queue(priv, MWIFIEX_IOCTL_WAIT); | ||
1913 | if (!wait) | ||
1914 | return -ENOMEM; | ||
1915 | |||
1916 | if (type == NL80211_TX_POWER_FIXED) { | ||
1917 | power_cfg.is_power_auto = 0; | ||
1918 | power_cfg.power_level = dbm; | ||
1919 | } else { | ||
1920 | power_cfg.is_power_auto = 1; | ||
1921 | } | ||
1922 | status = mwifiex_power_ioctl_set_power(priv, wait, &power_cfg); | ||
1923 | |||
1924 | ret = mwifiex_request_ioctl(priv, wait, status, MWIFIEX_IOCTL_WAIT); | ||
1925 | |||
1926 | kfree(wait); | ||
1927 | return ret; | ||
1928 | } | ||
1929 | |||
1930 | /* | ||
1931 | * Sends IOCTL request to get scan table. | ||
1932 | * | ||
1933 | * This function allocates the IOCTL request buffer, fills it | ||
1934 | * with requisite parameters and calls the IOCTL handler. | ||
1935 | */ | ||
1936 | int mwifiex_get_scan_table(struct mwifiex_private *priv, u8 wait_option, | ||
1937 | struct mwifiex_scan_resp *scan_resp) | ||
1938 | { | ||
1939 | struct mwifiex_wait_queue *wait = NULL; | ||
1940 | struct mwifiex_scan_resp scan; | ||
1941 | int status = 0; | ||
1942 | |||
1943 | /* Allocate wait buffer */ | ||
1944 | wait = mwifiex_alloc_fill_wait_queue(priv, wait_option); | ||
1945 | if (!wait) | ||
1946 | return -ENOMEM; | ||
1947 | |||
1948 | status = mwifiex_scan_networks(priv, wait, HostCmd_ACT_GEN_GET, | ||
1949 | NULL, &scan); | ||
1950 | |||
1951 | status = mwifiex_request_ioctl(priv, wait, status, wait_option); | ||
1952 | if (!status) { | ||
1953 | if (scan_resp) | ||
1954 | memcpy(scan_resp, &scan, | ||
1955 | sizeof(struct mwifiex_scan_resp)); | ||
1956 | } | ||
1957 | |||
1958 | if (wait && (status != -EINPROGRESS)) | ||
1959 | kfree(wait); | ||
1960 | return status; | ||
1961 | } | ||
1962 | |||
1963 | /* | ||
1964 | * Sends IOCTL request to get signal information. | ||
1965 | * | ||
1966 | * This function allocates the IOCTL request buffer, fills it | ||
1967 | * with requisite parameters and calls the IOCTL handler. | ||
1968 | */ | ||
1969 | int mwifiex_get_signal_info(struct mwifiex_private *priv, u8 wait_option, | ||
1970 | struct mwifiex_ds_get_signal *signal) | ||
1971 | { | ||
1972 | struct mwifiex_ds_get_signal info; | ||
1973 | struct mwifiex_wait_queue *wait = NULL; | ||
1974 | int status = 0; | ||
1975 | |||
1976 | /* Allocate wait buffer */ | ||
1977 | wait = mwifiex_alloc_fill_wait_queue(priv, wait_option); | ||
1978 | if (!wait) | ||
1979 | return -ENOMEM; | ||
1980 | |||
1981 | info.selector = ALL_RSSI_INFO_MASK; | ||
1982 | |||
1983 | status = mwifiex_get_info_signal(priv, wait, &info); | ||
1984 | |||
1985 | status = mwifiex_request_ioctl(priv, wait, status, wait_option); | ||
1986 | if (!status) { | ||
1987 | if (signal) | ||
1988 | memcpy(signal, &info, | ||
1989 | sizeof(struct mwifiex_ds_get_signal)); | ||
1990 | if (info.selector & BCN_RSSI_AVG_MASK) | ||
1991 | priv->w_stats.qual.level = info.bcn_rssi_avg; | ||
1992 | if (info.selector & BCN_NF_AVG_MASK) | ||
1993 | priv->w_stats.qual.noise = info.bcn_nf_avg; | ||
1994 | } | ||
1995 | |||
1996 | if (wait && (status != -EINPROGRESS)) | ||
1997 | kfree(wait); | ||
1998 | return status; | ||
1999 | } | ||
2000 | |||
2001 | /* | ||
2002 | * Sends IOCTL request to set encryption mode. | ||
2003 | * | ||
2004 | * This function allocates the IOCTL request buffer, fills it | ||
2005 | * with requisite parameters and calls the IOCTL handler. | ||
2006 | */ | ||
2007 | static int mwifiex_set_encrypt_mode(struct mwifiex_private *priv, | ||
2008 | u8 wait_option, u32 encrypt_mode) | ||
2009 | { | ||
2010 | priv->sec_info.encryption_mode = encrypt_mode; | ||
2011 | return 0; | ||
2012 | } | ||
2013 | |||
2014 | /* | ||
2015 | * This function set the authentication parameters. It sets both encryption | ||
2016 | * mode and authentication mode, and also enables WPA if required. | ||
2017 | */ | ||
2018 | int | ||
2019 | mwifiex_set_auth(struct mwifiex_private *priv, int encrypt_mode, | ||
2020 | int auth_mode, int wpa_enabled) | ||
2021 | { | ||
2022 | if (mwifiex_set_encrypt_mode(priv, MWIFIEX_IOCTL_WAIT, encrypt_mode)) | ||
2023 | return -EFAULT; | ||
2024 | |||
2025 | if (mwifiex_set_auth_mode(priv, auth_mode)) | ||
2026 | return -EFAULT; | ||
2027 | |||
2028 | return 0; | ||
2029 | } | ||
2030 | |||
2031 | /* | ||
2032 | * Sends IOCTL request to set encoding parameters. | ||
2033 | * | ||
2034 | * This function allocates the IOCTL request buffer, fills it | ||
2035 | * with requisite parameters and calls the IOCTL handler. | ||
2036 | */ | ||
2037 | int mwifiex_set_encode(struct mwifiex_private *priv, const u8 *key, | ||
2038 | int key_len, u8 key_index, int disable) | ||
2039 | { | ||
2040 | struct mwifiex_wait_queue *wait = NULL; | ||
2041 | struct mwifiex_ds_encrypt_key encrypt_key; | ||
2042 | int status = 0; | ||
2043 | int ret = 0; | ||
2044 | |||
2045 | wait = mwifiex_alloc_fill_wait_queue(priv, MWIFIEX_IOCTL_WAIT); | ||
2046 | if (!wait) | ||
2047 | return -ENOMEM; | ||
2048 | |||
2049 | memset(&encrypt_key, 0, sizeof(struct mwifiex_ds_encrypt_key)); | ||
2050 | encrypt_key.key_len = key_len; | ||
2051 | if (!disable) { | ||
2052 | encrypt_key.key_index = key_index; | ||
2053 | if (key_len) | ||
2054 | memcpy(encrypt_key.key_material, key, key_len); | ||
2055 | } else { | ||
2056 | encrypt_key.key_disable = true; | ||
2057 | } | ||
2058 | |||
2059 | status = mwifiex_sec_ioctl_encrypt_key(priv, wait, &encrypt_key); | ||
2060 | |||
2061 | if (mwifiex_request_ioctl(priv, wait, status, MWIFIEX_IOCTL_WAIT)) | ||
2062 | ret = -EFAULT; | ||
2063 | |||
2064 | kfree(wait); | ||
2065 | return ret; | ||
2066 | } | ||
2067 | |||
2068 | /* | ||
2069 | * Sends IOCTL request to set power management parameters. | ||
2070 | * | ||
2071 | * This function allocates the IOCTL request buffer, fills it | ||
2072 | * with requisite parameters and calls the IOCTL handler. | ||
2073 | */ | ||
2074 | int | ||
2075 | mwifiex_drv_set_power(struct mwifiex_private *priv, bool power_on) | ||
2076 | { | ||
2077 | int ret = 0; | ||
2078 | int status = 0; | ||
2079 | struct mwifiex_wait_queue *wait = NULL; | ||
2080 | u32 ps_mode; | ||
2081 | |||
2082 | wait = mwifiex_alloc_fill_wait_queue(priv, MWIFIEX_IOCTL_WAIT); | ||
2083 | if (!wait) | ||
2084 | return -ENOMEM; | ||
2085 | |||
2086 | ps_mode = power_on; | ||
2087 | status = mwifiex_pm_ioctl_ps_mode(priv, wait, &ps_mode, | ||
2088 | HostCmd_ACT_GEN_SET); | ||
2089 | |||
2090 | ret = mwifiex_request_ioctl(priv, wait, status, MWIFIEX_IOCTL_WAIT); | ||
2091 | |||
2092 | kfree(wait); | ||
2093 | return ret; | ||
2094 | } | ||
2095 | |||
2096 | /* | ||
2097 | * Sends IOCTL request to get extended version. | ||
2098 | * | ||
2099 | * This function allocates the IOCTL request buffer, fills it | ||
2100 | * with requisite parameters and calls the IOCTL handler. | ||
2101 | */ | ||
2102 | int | ||
2103 | mwifiex_get_ver_ext(struct mwifiex_private *priv) | ||
2104 | { | ||
2105 | struct mwifiex_ver_ext ver_ext; | ||
2106 | struct mwifiex_wait_queue *wait = NULL; | ||
2107 | int status = 0; | ||
2108 | int ret = 0; | ||
2109 | u8 wait_option = MWIFIEX_IOCTL_WAIT; | ||
2110 | |||
2111 | /* Allocate wait buffer */ | ||
2112 | wait = mwifiex_alloc_fill_wait_queue(priv, wait_option); | ||
2113 | if (!wait) | ||
2114 | return -ENOMEM; | ||
2115 | |||
2116 | /* get fw version */ | ||
2117 | memset(&ver_ext, 0, sizeof(struct host_cmd_ds_version_ext)); | ||
2118 | status = mwifiex_get_info_ver_ext(priv, wait, &ver_ext); | ||
2119 | |||
2120 | ret = mwifiex_request_ioctl(priv, wait, status, wait_option); | ||
2121 | |||
2122 | if (ret) | ||
2123 | ret = -1; | ||
2124 | |||
2125 | kfree(wait); | ||
2126 | return ret; | ||
2127 | } | ||
2128 | |||
2129 | /* | ||
2130 | * Sends IOCTL request to get statistics information. | ||
2131 | * | ||
2132 | * This function allocates the IOCTL request buffer, fills it | ||
2133 | * with requisite parameters and calls the IOCTL handler. | ||
2134 | */ | ||
2135 | int | ||
2136 | mwifiex_get_stats_info(struct mwifiex_private *priv, | ||
2137 | struct mwifiex_ds_get_stats *log) | ||
2138 | { | ||
2139 | int ret = 0; | ||
2140 | int status = 0; | ||
2141 | struct mwifiex_wait_queue *wait = NULL; | ||
2142 | struct mwifiex_ds_get_stats get_log; | ||
2143 | u8 wait_option = MWIFIEX_IOCTL_WAIT; | ||
2144 | |||
2145 | /* Allocate wait buffer */ | ||
2146 | wait = mwifiex_alloc_fill_wait_queue(priv, wait_option); | ||
2147 | if (!wait) | ||
2148 | return -ENOMEM; | ||
2149 | |||
2150 | memset(&get_log, 0, sizeof(struct mwifiex_ds_get_stats)); | ||
2151 | status = mwifiex_get_info_stats(priv, wait, &get_log); | ||
2152 | |||
2153 | /* Send IOCTL request to MWIFIEX */ | ||
2154 | ret = mwifiex_request_ioctl(priv, wait, status, wait_option); | ||
2155 | if (!ret) { | ||
2156 | if (log) | ||
2157 | memcpy(log, &get_log, sizeof(struct | ||
2158 | mwifiex_ds_get_stats)); | ||
2159 | priv->w_stats.discard.fragment = get_log.fcs_error; | ||
2160 | priv->w_stats.discard.retries = get_log.retry; | ||
2161 | priv->w_stats.discard.misc = get_log.ack_failure; | ||
2162 | } | ||
2163 | |||
2164 | kfree(wait); | ||
2165 | return ret; | ||
2166 | } | ||
2167 | |||
2168 | /* | ||
2169 | * IOCTL request handler to read/write register. | ||
2170 | * | ||
2171 | * This function prepares the correct firmware command and | ||
2172 | * issues it. | ||
2173 | * | ||
2174 | * Access to the following registers are supported - | ||
2175 | * - MAC | ||
2176 | * - BBP | ||
2177 | * - RF | ||
2178 | * - PMIC | ||
2179 | * - CAU | ||
2180 | */ | ||
2181 | static int mwifiex_reg_mem_ioctl_reg_rw(struct mwifiex_private *priv, | ||
2182 | struct mwifiex_wait_queue *wait, | ||
2183 | struct mwifiex_ds_reg_rw *reg_rw, | ||
2184 | u16 action) | ||
2185 | { | ||
2186 | int ret = 0; | ||
2187 | u16 cmd_no; | ||
2188 | |||
2189 | switch (le32_to_cpu(reg_rw->type)) { | ||
2190 | case MWIFIEX_REG_MAC: | ||
2191 | cmd_no = HostCmd_CMD_MAC_REG_ACCESS; | ||
2192 | break; | ||
2193 | case MWIFIEX_REG_BBP: | ||
2194 | cmd_no = HostCmd_CMD_BBP_REG_ACCESS; | ||
2195 | break; | ||
2196 | case MWIFIEX_REG_RF: | ||
2197 | cmd_no = HostCmd_CMD_RF_REG_ACCESS; | ||
2198 | break; | ||
2199 | case MWIFIEX_REG_PMIC: | ||
2200 | cmd_no = HostCmd_CMD_PMIC_REG_ACCESS; | ||
2201 | break; | ||
2202 | case MWIFIEX_REG_CAU: | ||
2203 | cmd_no = HostCmd_CMD_CAU_REG_ACCESS; | ||
2204 | break; | ||
2205 | default: | ||
2206 | return -1; | ||
2207 | } | ||
2208 | |||
2209 | /* Send request to firmware */ | ||
2210 | ret = mwifiex_prepare_cmd(priv, cmd_no, action, 0, wait, reg_rw); | ||
2211 | |||
2212 | if (!ret) | ||
2213 | ret = -EINPROGRESS; | ||
2214 | |||
2215 | return ret; | ||
2216 | } | ||
2217 | |||
2218 | /* | ||
2219 | * Sends IOCTL request to write to a register. | ||
2220 | * | ||
2221 | * This function allocates the IOCTL request buffer, fills it | ||
2222 | * with requisite parameters and calls the IOCTL handler. | ||
2223 | */ | ||
2224 | int | ||
2225 | mwifiex_reg_write(struct mwifiex_private *priv, u32 reg_type, | ||
2226 | u32 reg_offset, u32 reg_value) | ||
2227 | { | ||
2228 | int ret = 0; | ||
2229 | int status = 0; | ||
2230 | struct mwifiex_wait_queue *wait = NULL; | ||
2231 | struct mwifiex_ds_reg_rw reg_rw; | ||
2232 | |||
2233 | wait = mwifiex_alloc_fill_wait_queue(priv, MWIFIEX_IOCTL_WAIT); | ||
2234 | if (!wait) | ||
2235 | return -ENOMEM; | ||
2236 | |||
2237 | reg_rw.type = cpu_to_le32(reg_type); | ||
2238 | reg_rw.offset = cpu_to_le32(reg_offset); | ||
2239 | reg_rw.value = cpu_to_le32(reg_value); | ||
2240 | status = mwifiex_reg_mem_ioctl_reg_rw(priv, wait, ®_rw, | ||
2241 | HostCmd_ACT_GEN_SET); | ||
2242 | |||
2243 | ret = mwifiex_request_ioctl(priv, wait, status, MWIFIEX_IOCTL_WAIT); | ||
2244 | |||
2245 | kfree(wait); | ||
2246 | return ret; | ||
2247 | } | ||
2248 | |||
2249 | /* | ||
2250 | * Sends IOCTL request to read from a register. | ||
2251 | * | ||
2252 | * This function allocates the IOCTL request buffer, fills it | ||
2253 | * with requisite parameters and calls the IOCTL handler. | ||
2254 | */ | ||
2255 | int | ||
2256 | mwifiex_reg_read(struct mwifiex_private *priv, u32 reg_type, | ||
2257 | u32 reg_offset, u32 *value) | ||
2258 | { | ||
2259 | int ret = 0; | ||
2260 | int status = 0; | ||
2261 | struct mwifiex_wait_queue *wait = NULL; | ||
2262 | struct mwifiex_ds_reg_rw reg_rw; | ||
2263 | |||
2264 | wait = mwifiex_alloc_fill_wait_queue(priv, MWIFIEX_IOCTL_WAIT); | ||
2265 | if (!wait) | ||
2266 | return -ENOMEM; | ||
2267 | |||
2268 | reg_rw.type = cpu_to_le32(reg_type); | ||
2269 | reg_rw.offset = cpu_to_le32(reg_offset); | ||
2270 | status = mwifiex_reg_mem_ioctl_reg_rw(priv, wait, ®_rw, | ||
2271 | HostCmd_ACT_GEN_GET); | ||
2272 | |||
2273 | ret = mwifiex_request_ioctl(priv, wait, status, MWIFIEX_IOCTL_WAIT); | ||
2274 | if (ret) | ||
2275 | goto done; | ||
2276 | |||
2277 | *value = le32_to_cpu(reg_rw.value); | ||
2278 | |||
2279 | done: | ||
2280 | kfree(wait); | ||
2281 | return ret; | ||
2282 | } | ||
2283 | |||
2284 | /* | ||
2285 | * IOCTL request handler to read EEPROM. | ||
2286 | * | ||
2287 | * This function prepares the correct firmware command and | ||
2288 | * issues it. | ||
2289 | */ | ||
2290 | static int | ||
2291 | mwifiex_reg_mem_ioctl_read_eeprom(struct mwifiex_private *priv, | ||
2292 | struct mwifiex_wait_queue *wait, | ||
2293 | struct mwifiex_ds_read_eeprom *rd_eeprom) | ||
2294 | { | ||
2295 | int ret = 0; | ||
2296 | |||
2297 | /* Send request to firmware */ | ||
2298 | ret = mwifiex_prepare_cmd(priv, HostCmd_CMD_802_11_EEPROM_ACCESS, | ||
2299 | HostCmd_ACT_GEN_GET, 0, wait, rd_eeprom); | ||
2300 | |||
2301 | if (!ret) | ||
2302 | ret = -EINPROGRESS; | ||
2303 | |||
2304 | return ret; | ||
2305 | } | ||
2306 | |||
2307 | /* | ||
2308 | * Sends IOCTL request to read from EEPROM. | ||
2309 | * | ||
2310 | * This function allocates the IOCTL request buffer, fills it | ||
2311 | * with requisite parameters and calls the IOCTL handler. | ||
2312 | */ | ||
2313 | int | ||
2314 | mwifiex_eeprom_read(struct mwifiex_private *priv, u16 offset, u16 bytes, | ||
2315 | u8 *value) | ||
2316 | { | ||
2317 | int ret = 0; | ||
2318 | int status = 0; | ||
2319 | struct mwifiex_wait_queue *wait = NULL; | ||
2320 | struct mwifiex_ds_read_eeprom rd_eeprom; | ||
2321 | |||
2322 | wait = mwifiex_alloc_fill_wait_queue(priv, MWIFIEX_IOCTL_WAIT); | ||
2323 | if (!wait) | ||
2324 | return -ENOMEM; | ||
2325 | |||
2326 | rd_eeprom.offset = cpu_to_le16((u16) offset); | ||
2327 | rd_eeprom.byte_count = cpu_to_le16((u16) bytes); | ||
2328 | status = mwifiex_reg_mem_ioctl_read_eeprom(priv, wait, &rd_eeprom); | ||
2329 | |||
2330 | ret = mwifiex_request_ioctl(priv, wait, status, MWIFIEX_IOCTL_WAIT); | ||
2331 | if (ret) | ||
2332 | goto done; | ||
2333 | |||
2334 | memcpy(value, rd_eeprom.value, MAX_EEPROM_DATA); | ||
2335 | done: | ||
2336 | kfree(wait); | ||
2337 | return ret; | ||
2338 | } | ||
2339 | |||
2340 | /* | ||
2341 | * This function sets a generic IE. In addition to generic IE, it can | ||
2342 | * also handle WPA, WPA2 and WAPI IEs. | ||
2343 | */ | ||
2344 | static int | ||
2345 | mwifiex_set_gen_ie_helper(struct mwifiex_private *priv, u8 *ie_data_ptr, | ||
2346 | u16 ie_len) | ||
2347 | { | ||
2348 | int ret = 0; | ||
2349 | struct ieee_types_vendor_header *pvendor_ie; | ||
2350 | const u8 wpa_oui[] = { 0x00, 0x50, 0xf2, 0x01 }; | ||
2351 | const u8 wps_oui[] = { 0x00, 0x50, 0xf2, 0x04 }; | ||
2352 | |||
2353 | /* If the passed length is zero, reset the buffer */ | ||
2354 | if (!ie_len) { | ||
2355 | priv->gen_ie_buf_len = 0; | ||
2356 | priv->wps.session_enable = false; | ||
2357 | |||
2358 | return 0; | ||
2359 | } else if (!ie_data_ptr) { | ||
2360 | return -1; | ||
2361 | } | ||
2362 | pvendor_ie = (struct ieee_types_vendor_header *) ie_data_ptr; | ||
2363 | /* Test to see if it is a WPA IE, if not, then it is a gen IE */ | ||
2364 | if (((pvendor_ie->element_id == WLAN_EID_WPA) | ||
2365 | && (!memcmp(pvendor_ie->oui, wpa_oui, sizeof(wpa_oui)))) | ||
2366 | || (pvendor_ie->element_id == WLAN_EID_RSN)) { | ||
2367 | |||
2368 | /* IE is a WPA/WPA2 IE so call set_wpa function */ | ||
2369 | ret = mwifiex_set_wpa_ie_helper(priv, ie_data_ptr, ie_len); | ||
2370 | priv->wps.session_enable = false; | ||
2371 | |||
2372 | return ret; | ||
2373 | } else if (pvendor_ie->element_id == WLAN_EID_BSS_AC_ACCESS_DELAY) { | ||
2374 | /* IE is a WAPI IE so call set_wapi function */ | ||
2375 | ret = mwifiex_set_wapi_ie(priv, ie_data_ptr, ie_len); | ||
2376 | |||
2377 | return ret; | ||
2378 | } | ||
2379 | /* | ||
2380 | * Verify that the passed length is not larger than the | ||
2381 | * available space remaining in the buffer | ||
2382 | */ | ||
2383 | if (ie_len < (sizeof(priv->gen_ie_buf) - priv->gen_ie_buf_len)) { | ||
2384 | |||
2385 | /* Test to see if it is a WPS IE, if so, enable | ||
2386 | * wps session flag | ||
2387 | */ | ||
2388 | pvendor_ie = (struct ieee_types_vendor_header *) ie_data_ptr; | ||
2389 | if ((pvendor_ie->element_id == WLAN_EID_VENDOR_SPECIFIC) | ||
2390 | && (!memcmp(pvendor_ie->oui, wps_oui, | ||
2391 | sizeof(wps_oui)))) { | ||
2392 | priv->wps.session_enable = true; | ||
2393 | dev_dbg(priv->adapter->dev, | ||
2394 | "info: WPS Session Enabled.\n"); | ||
2395 | } | ||
2396 | |||
2397 | /* Append the passed data to the end of the | ||
2398 | genIeBuffer */ | ||
2399 | memcpy(priv->gen_ie_buf + priv->gen_ie_buf_len, ie_data_ptr, | ||
2400 | ie_len); | ||
2401 | /* Increment the stored buffer length by the | ||
2402 | size passed */ | ||
2403 | priv->gen_ie_buf_len += ie_len; | ||
2404 | } else { | ||
2405 | /* Passed data does not fit in the remaining | ||
2406 | buffer space */ | ||
2407 | ret = -1; | ||
2408 | } | ||
2409 | |||
2410 | /* Return 0, or -1 for error case */ | ||
2411 | return ret; | ||
2412 | } | ||
2413 | |||
2414 | /* | ||
2415 | * IOCTL request handler to set/get generic IE. | ||
2416 | * | ||
2417 | * In addition to various generic IEs, this function can also be | ||
2418 | * used to set the ARP filter. | ||
2419 | */ | ||
2420 | static int mwifiex_misc_ioctl_gen_ie(struct mwifiex_private *priv, | ||
2421 | struct mwifiex_ds_misc_gen_ie *gen_ie, | ||
2422 | u16 action) | ||
2423 | { | ||
2424 | struct mwifiex_adapter *adapter = priv->adapter; | ||
2425 | |||
2426 | switch (gen_ie->type) { | ||
2427 | case MWIFIEX_IE_TYPE_GEN_IE: | ||
2428 | if (action == HostCmd_ACT_GEN_GET) { | ||
2429 | gen_ie->len = priv->wpa_ie_len; | ||
2430 | memcpy(gen_ie->ie_data, priv->wpa_ie, gen_ie->len); | ||
2431 | } else { | ||
2432 | mwifiex_set_gen_ie_helper(priv, gen_ie->ie_data, | ||
2433 | (u16) gen_ie->len); | ||
2434 | } | ||
2435 | break; | ||
2436 | case MWIFIEX_IE_TYPE_ARP_FILTER: | ||
2437 | memset(adapter->arp_filter, 0, sizeof(adapter->arp_filter)); | ||
2438 | if (gen_ie->len > ARP_FILTER_MAX_BUF_SIZE) { | ||
2439 | adapter->arp_filter_size = 0; | ||
2440 | dev_err(adapter->dev, "invalid ARP filter size\n"); | ||
2441 | return -1; | ||
2442 | } else { | ||
2443 | memcpy(adapter->arp_filter, gen_ie->ie_data, | ||
2444 | gen_ie->len); | ||
2445 | adapter->arp_filter_size = gen_ie->len; | ||
2446 | } | ||
2447 | break; | ||
2448 | default: | ||
2449 | dev_err(adapter->dev, "invalid IE type\n"); | ||
2450 | return -1; | ||
2451 | } | ||
2452 | return 0; | ||
2453 | } | ||
2454 | |||
2455 | /* | ||
2456 | * Sends IOCTL request to set a generic IE. | ||
2457 | * | ||
2458 | * This function allocates the IOCTL request buffer, fills it | ||
2459 | * with requisite parameters and calls the IOCTL handler. | ||
2460 | */ | ||
2461 | int | ||
2462 | mwifiex_set_gen_ie(struct mwifiex_private *priv, u8 *ie, int ie_len) | ||
2463 | { | ||
2464 | struct mwifiex_ds_misc_gen_ie gen_ie; | ||
2465 | int status = 0; | ||
2466 | |||
2467 | if (ie_len > IW_CUSTOM_MAX) | ||
2468 | return -EFAULT; | ||
2469 | |||
2470 | gen_ie.type = MWIFIEX_IE_TYPE_GEN_IE; | ||
2471 | gen_ie.len = ie_len; | ||
2472 | memcpy(gen_ie.ie_data, ie, ie_len); | ||
2473 | status = mwifiex_misc_ioctl_gen_ie(priv, &gen_ie, HostCmd_ACT_GEN_SET); | ||
2474 | if (status) | ||
2475 | return -EFAULT; | ||
2476 | |||
2477 | return 0; | ||
2478 | } | ||
diff --git a/drivers/net/wireless/mwifiex/sta_rx.c b/drivers/net/wireless/mwifiex/sta_rx.c new file mode 100644 index 000000000000..8282679e64fd --- /dev/null +++ b/drivers/net/wireless/mwifiex/sta_rx.c | |||
@@ -0,0 +1,182 @@ | |||
1 | /* | ||
2 | * Marvell Wireless LAN device driver: station RX data handling | ||
3 | * | ||
4 | * Copyright (C) 2011, Marvell International Ltd. | ||
5 | * | ||
6 | * This software file (the "File") is distributed by Marvell International | ||
7 | * Ltd. under the terms of the GNU General Public License Version 2, June 1991 | ||
8 | * (the "License"). You may use, redistribute and/or modify this File in | ||
9 | * accordance with the terms and conditions of the License, a copy of which | ||
10 | * is available by writing to the Free Software Foundation, Inc., | ||
11 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the | ||
12 | * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. | ||
13 | * | ||
14 | * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE | ||
15 | * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE | ||
16 | * ARE EXPRESSLY DISCLAIMED. The License provides additional details about | ||
17 | * this warranty disclaimer. | ||
18 | */ | ||
19 | |||
20 | #include "decl.h" | ||
21 | #include "ioctl.h" | ||
22 | #include "util.h" | ||
23 | #include "fw.h" | ||
24 | #include "main.h" | ||
25 | #include "11n_aggr.h" | ||
26 | #include "11n_rxreorder.h" | ||
27 | |||
28 | /* | ||
29 | * This function processes the received packet and forwards it | ||
30 | * to kernel/upper layer. | ||
31 | * | ||
32 | * This function parses through the received packet and determines | ||
33 | * if it is a debug packet or normal packet. | ||
34 | * | ||
35 | * For non-debug packets, the function chops off unnecessary leading | ||
36 | * header bytes, reconstructs the packet as an ethernet frame or | ||
37 | * 802.2/llc/snap frame as required, and sends it to kernel/upper layer. | ||
38 | * | ||
39 | * The completion callback is called after processing in complete. | ||
40 | */ | ||
41 | int mwifiex_process_rx_packet(struct mwifiex_adapter *adapter, | ||
42 | struct sk_buff *skb) | ||
43 | { | ||
44 | int ret = 0; | ||
45 | struct mwifiex_rxinfo *rx_info = MWIFIEX_SKB_RXCB(skb); | ||
46 | struct mwifiex_private *priv = adapter->priv[rx_info->bss_index]; | ||
47 | struct rx_packet_hdr *rx_pkt_hdr; | ||
48 | struct rxpd *local_rx_pd; | ||
49 | int hdr_chop; | ||
50 | struct ethhdr *eth_hdr; | ||
51 | u8 rfc1042_eth_hdr[ETH_ALEN] = { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 }; | ||
52 | |||
53 | local_rx_pd = (struct rxpd *) (skb->data); | ||
54 | |||
55 | rx_pkt_hdr = (struct rx_packet_hdr *) ((u8 *) local_rx_pd + | ||
56 | local_rx_pd->rx_pkt_offset); | ||
57 | |||
58 | if (!memcmp(&rx_pkt_hdr->rfc1042_hdr, | ||
59 | rfc1042_eth_hdr, sizeof(rfc1042_eth_hdr))) { | ||
60 | /* | ||
61 | * Replace the 803 header and rfc1042 header (llc/snap) with an | ||
62 | * EthernetII header, keep the src/dst and snap_type | ||
63 | * (ethertype). | ||
64 | * The firmware only passes up SNAP frames converting | ||
65 | * all RX Data from 802.11 to 802.2/LLC/SNAP frames. | ||
66 | * To create the Ethernet II, just move the src, dst address | ||
67 | * right before the snap_type. | ||
68 | */ | ||
69 | eth_hdr = (struct ethhdr *) | ||
70 | ((u8 *) &rx_pkt_hdr->eth803_hdr | ||
71 | + sizeof(rx_pkt_hdr->eth803_hdr) + | ||
72 | sizeof(rx_pkt_hdr->rfc1042_hdr) | ||
73 | - sizeof(rx_pkt_hdr->eth803_hdr.h_dest) | ||
74 | - sizeof(rx_pkt_hdr->eth803_hdr.h_source) | ||
75 | - sizeof(rx_pkt_hdr->rfc1042_hdr.snap_type)); | ||
76 | |||
77 | memcpy(eth_hdr->h_source, rx_pkt_hdr->eth803_hdr.h_source, | ||
78 | sizeof(eth_hdr->h_source)); | ||
79 | memcpy(eth_hdr->h_dest, rx_pkt_hdr->eth803_hdr.h_dest, | ||
80 | sizeof(eth_hdr->h_dest)); | ||
81 | |||
82 | /* Chop off the rxpd + the excess memory from the 802.2/llc/snap | ||
83 | header that was removed. */ | ||
84 | hdr_chop = (u8 *) eth_hdr - (u8 *) local_rx_pd; | ||
85 | } else { | ||
86 | /* Chop off the rxpd */ | ||
87 | hdr_chop = (u8 *) &rx_pkt_hdr->eth803_hdr - | ||
88 | (u8 *) local_rx_pd; | ||
89 | } | ||
90 | |||
91 | /* Chop off the leading header bytes so the it points to the start of | ||
92 | either the reconstructed EthII frame or the 802.2/llc/snap frame */ | ||
93 | skb_pull(skb, hdr_chop); | ||
94 | |||
95 | priv->rxpd_rate = local_rx_pd->rx_rate; | ||
96 | |||
97 | priv->rxpd_htinfo = local_rx_pd->ht_info; | ||
98 | |||
99 | ret = mwifiex_recv_packet(adapter, skb); | ||
100 | if (ret == -1) | ||
101 | dev_err(adapter->dev, "recv packet failed\n"); | ||
102 | |||
103 | return ret; | ||
104 | } | ||
105 | |||
106 | /* | ||
107 | * This function processes the received buffer. | ||
108 | * | ||
109 | * The function looks into the RxPD and performs sanity tests on the | ||
110 | * received buffer to ensure its a valid packet, before processing it | ||
111 | * further. If the packet is determined to be aggregated, it is | ||
112 | * de-aggregated accordingly. Non-unicast packets are sent directly to | ||
113 | * the kernel/upper layers. Unicast packets are handed over to the | ||
114 | * Rx reordering routine if 11n is enabled. | ||
115 | * | ||
116 | * The completion callback is called after processing in complete. | ||
117 | */ | ||
118 | int mwifiex_process_sta_rx_packet(struct mwifiex_adapter *adapter, | ||
119 | struct sk_buff *skb) | ||
120 | { | ||
121 | int ret = 0; | ||
122 | struct rxpd *local_rx_pd; | ||
123 | struct mwifiex_rxinfo *rx_info = MWIFIEX_SKB_RXCB(skb); | ||
124 | struct rx_packet_hdr *rx_pkt_hdr; | ||
125 | u8 ta[ETH_ALEN]; | ||
126 | u16 rx_pkt_type = 0; | ||
127 | struct mwifiex_private *priv = adapter->priv[rx_info->bss_index]; | ||
128 | |||
129 | local_rx_pd = (struct rxpd *) (skb->data); | ||
130 | rx_pkt_type = local_rx_pd->rx_pkt_type; | ||
131 | |||
132 | rx_pkt_hdr = (struct rx_packet_hdr *) ((u8 *) local_rx_pd + | ||
133 | local_rx_pd->rx_pkt_offset); | ||
134 | |||
135 | if ((local_rx_pd->rx_pkt_offset + local_rx_pd->rx_pkt_length) > | ||
136 | (u16) skb->len) { | ||
137 | dev_err(adapter->dev, "wrong rx packet: len=%d," | ||
138 | " rx_pkt_offset=%d, rx_pkt_length=%d\n", skb->len, | ||
139 | local_rx_pd->rx_pkt_offset, local_rx_pd->rx_pkt_length); | ||
140 | priv->stats.rx_dropped++; | ||
141 | dev_kfree_skb_any(skb); | ||
142 | return ret; | ||
143 | } | ||
144 | if (local_rx_pd->rx_pkt_type == PKT_TYPE_AMSDU) { | ||
145 | mwifiex_11n_deaggregate_pkt(priv, skb); | ||
146 | return ret; | ||
147 | } | ||
148 | /* | ||
149 | * If the packet is not an unicast packet then send the packet | ||
150 | * directly to os. Don't pass thru rx reordering | ||
151 | */ | ||
152 | if (!IS_11N_ENABLED(priv) || | ||
153 | memcmp(priv->curr_addr, rx_pkt_hdr->eth803_hdr.h_dest, ETH_ALEN)) { | ||
154 | mwifiex_process_rx_packet(adapter, skb); | ||
155 | return ret; | ||
156 | } | ||
157 | |||
158 | if (mwifiex_queuing_ra_based(priv)) { | ||
159 | memcpy(ta, rx_pkt_hdr->eth803_hdr.h_source, ETH_ALEN); | ||
160 | } else { | ||
161 | if (rx_pkt_type != PKT_TYPE_BAR) | ||
162 | priv->rx_seq[local_rx_pd->priority] = | ||
163 | local_rx_pd->seq_num; | ||
164 | memcpy(ta, priv->curr_bss_params.bss_descriptor.mac_address, | ||
165 | ETH_ALEN); | ||
166 | } | ||
167 | |||
168 | /* Reorder and send to OS */ | ||
169 | ret = mwifiex_11n_rx_reorder_pkt(priv, local_rx_pd->seq_num, | ||
170 | local_rx_pd->priority, ta, | ||
171 | (u8) local_rx_pd->rx_pkt_type, | ||
172 | (void *) skb); | ||
173 | |||
174 | if (ret || (rx_pkt_type == PKT_TYPE_BAR)) { | ||
175 | if (priv && (ret == -1)) | ||
176 | priv->stats.rx_dropped++; | ||
177 | |||
178 | dev_kfree_skb_any(skb); | ||
179 | } | ||
180 | |||
181 | return ret; | ||
182 | } | ||
diff --git a/drivers/net/wireless/mwifiex/sta_tx.c b/drivers/net/wireless/mwifiex/sta_tx.c new file mode 100644 index 000000000000..e8db6bd021c6 --- /dev/null +++ b/drivers/net/wireless/mwifiex/sta_tx.c | |||
@@ -0,0 +1,202 @@ | |||
1 | /* | ||
2 | * Marvell Wireless LAN device driver: station TX data handling | ||
3 | * | ||
4 | * Copyright (C) 2011, Marvell International Ltd. | ||
5 | * | ||
6 | * This software file (the "File") is distributed by Marvell International | ||
7 | * Ltd. under the terms of the GNU General Public License Version 2, June 1991 | ||
8 | * (the "License"). You may use, redistribute and/or modify this File in | ||
9 | * accordance with the terms and conditions of the License, a copy of which | ||
10 | * is available by writing to the Free Software Foundation, Inc., | ||
11 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the | ||
12 | * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. | ||
13 | * | ||
14 | * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE | ||
15 | * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE | ||
16 | * ARE EXPRESSLY DISCLAIMED. The License provides additional details about | ||
17 | * this warranty disclaimer. | ||
18 | */ | ||
19 | |||
20 | #include "decl.h" | ||
21 | #include "ioctl.h" | ||
22 | #include "util.h" | ||
23 | #include "fw.h" | ||
24 | #include "main.h" | ||
25 | #include "wmm.h" | ||
26 | |||
27 | /* | ||
28 | * This function fills the TxPD for tx packets. | ||
29 | * | ||
30 | * The Tx buffer received by this function should already have the | ||
31 | * header space allocated for TxPD. | ||
32 | * | ||
33 | * This function inserts the TxPD in between interface header and actual | ||
34 | * data and adjusts the buffer pointers accordingly. | ||
35 | * | ||
36 | * The following TxPD fields are set by this function, as required - | ||
37 | * - BSS number | ||
38 | * - Tx packet length and offset | ||
39 | * - Priority | ||
40 | * - Packet delay | ||
41 | * - Priority specific Tx control | ||
42 | * - Flags | ||
43 | */ | ||
44 | void *mwifiex_process_sta_txpd(struct mwifiex_private *priv, | ||
45 | struct sk_buff *skb) | ||
46 | { | ||
47 | struct mwifiex_adapter *adapter = priv->adapter; | ||
48 | struct txpd *local_tx_pd; | ||
49 | struct mwifiex_txinfo *tx_info = MWIFIEX_SKB_TXCB(skb); | ||
50 | |||
51 | if (!skb->len) { | ||
52 | dev_err(adapter->dev, "Tx: bad packet length: %d\n", | ||
53 | skb->len); | ||
54 | tx_info->status_code = MWIFIEX_ERROR_PKT_SIZE_INVALID; | ||
55 | return skb->data; | ||
56 | } | ||
57 | |||
58 | BUG_ON(skb_headroom(skb) < (sizeof(*local_tx_pd) + INTF_HEADER_LEN)); | ||
59 | skb_push(skb, sizeof(*local_tx_pd)); | ||
60 | |||
61 | local_tx_pd = (struct txpd *) skb->data; | ||
62 | memset(local_tx_pd, 0, sizeof(struct txpd)); | ||
63 | local_tx_pd->bss_num = priv->bss_num; | ||
64 | local_tx_pd->bss_type = priv->bss_type; | ||
65 | local_tx_pd->tx_pkt_length = cpu_to_le16((u16) (skb->len - | ||
66 | sizeof(struct txpd))); | ||
67 | |||
68 | local_tx_pd->priority = (u8) skb->priority; | ||
69 | local_tx_pd->pkt_delay_2ms = | ||
70 | mwifiex_wmm_compute_drv_pkt_delay(priv, skb); | ||
71 | |||
72 | if (local_tx_pd->priority < | ||
73 | ARRAY_SIZE(priv->wmm.user_pri_pkt_tx_ctrl)) | ||
74 | /* | ||
75 | * Set the priority specific tx_control field, setting of 0 will | ||
76 | * cause the default value to be used later in this function | ||
77 | */ | ||
78 | local_tx_pd->tx_control = | ||
79 | cpu_to_le32(priv->wmm.user_pri_pkt_tx_ctrl[local_tx_pd-> | ||
80 | priority]); | ||
81 | |||
82 | if (adapter->pps_uapsd_mode) { | ||
83 | if (mwifiex_check_last_packet_indication(priv)) { | ||
84 | adapter->tx_lock_flag = true; | ||
85 | local_tx_pd->flags = | ||
86 | MWIFIEX_TxPD_POWER_MGMT_LAST_PACKET; | ||
87 | } | ||
88 | } | ||
89 | |||
90 | /* Offset of actual data */ | ||
91 | local_tx_pd->tx_pkt_offset = cpu_to_le16(sizeof(struct txpd)); | ||
92 | |||
93 | /* make space for INTF_HEADER_LEN */ | ||
94 | skb_push(skb, INTF_HEADER_LEN); | ||
95 | |||
96 | if (!local_tx_pd->tx_control) | ||
97 | /* TxCtrl set by user or default */ | ||
98 | local_tx_pd->tx_control = cpu_to_le32(priv->pkt_tx_ctrl); | ||
99 | |||
100 | return skb->data; | ||
101 | } | ||
102 | |||
103 | /* | ||
104 | * This function tells firmware to send a NULL data packet. | ||
105 | * | ||
106 | * The function creates a NULL data packet with TxPD and sends to the | ||
107 | * firmware for transmission, with highest priority setting. | ||
108 | */ | ||
109 | int mwifiex_send_null_packet(struct mwifiex_private *priv, u8 flags) | ||
110 | { | ||
111 | struct mwifiex_adapter *adapter = priv->adapter; | ||
112 | struct txpd *local_tx_pd; | ||
113 | /* sizeof(struct txpd) + Interface specific header */ | ||
114 | #define NULL_PACKET_HDR 64 | ||
115 | u32 data_len = NULL_PACKET_HDR; | ||
116 | struct sk_buff *skb = NULL; | ||
117 | int ret = 0; | ||
118 | struct mwifiex_txinfo *tx_info = NULL; | ||
119 | |||
120 | if (adapter->surprise_removed) | ||
121 | return -1; | ||
122 | |||
123 | if (!priv->media_connected) | ||
124 | return -1; | ||
125 | |||
126 | if (adapter->data_sent) | ||
127 | return -1; | ||
128 | |||
129 | skb = dev_alloc_skb(data_len); | ||
130 | if (!skb) | ||
131 | return -1; | ||
132 | |||
133 | tx_info = MWIFIEX_SKB_TXCB(skb); | ||
134 | tx_info->bss_index = priv->bss_index; | ||
135 | skb_reserve(skb, sizeof(struct txpd) + INTF_HEADER_LEN); | ||
136 | skb_push(skb, sizeof(struct txpd)); | ||
137 | |||
138 | local_tx_pd = (struct txpd *) skb->data; | ||
139 | local_tx_pd->tx_control = cpu_to_le32(priv->pkt_tx_ctrl); | ||
140 | local_tx_pd->flags = flags; | ||
141 | local_tx_pd->priority = WMM_HIGHEST_PRIORITY; | ||
142 | local_tx_pd->tx_pkt_offset = cpu_to_le16(sizeof(struct txpd)); | ||
143 | local_tx_pd->bss_num = priv->bss_num; | ||
144 | local_tx_pd->bss_type = priv->bss_type; | ||
145 | |||
146 | skb_push(skb, INTF_HEADER_LEN); | ||
147 | |||
148 | ret = adapter->if_ops.host_to_card(adapter, MWIFIEX_TYPE_DATA, | ||
149 | skb->data, skb->len, NULL); | ||
150 | switch (ret) { | ||
151 | case -EBUSY: | ||
152 | adapter->data_sent = true; | ||
153 | /* Fall through FAILURE handling */ | ||
154 | case -1: | ||
155 | dev_kfree_skb_any(skb); | ||
156 | dev_err(adapter->dev, "%s: host_to_card failed: ret=%d\n", | ||
157 | __func__, ret); | ||
158 | adapter->dbg.num_tx_host_to_card_failure++; | ||
159 | break; | ||
160 | case 0: | ||
161 | dev_kfree_skb_any(skb); | ||
162 | dev_dbg(adapter->dev, "data: %s: host_to_card succeeded\n", | ||
163 | __func__); | ||
164 | adapter->tx_lock_flag = true; | ||
165 | break; | ||
166 | case -EINPROGRESS: | ||
167 | break; | ||
168 | default: | ||
169 | break; | ||
170 | } | ||
171 | |||
172 | return ret; | ||
173 | } | ||
174 | |||
175 | /* | ||
176 | * This function checks if we need to send last packet indication. | ||
177 | */ | ||
178 | u8 | ||
179 | mwifiex_check_last_packet_indication(struct mwifiex_private *priv) | ||
180 | { | ||
181 | struct mwifiex_adapter *adapter = priv->adapter; | ||
182 | u8 ret = false; | ||
183 | u8 prop_ps = true; | ||
184 | |||
185 | if (!adapter->sleep_period.period) | ||
186 | return ret; | ||
187 | if (mwifiex_wmm_lists_empty(adapter)) { | ||
188 | if ((priv->curr_bss_params.wmm_uapsd_enabled && | ||
189 | priv->wmm_qosinfo) || prop_ps) | ||
190 | ret = true; | ||
191 | } | ||
192 | |||
193 | if (ret && !adapter->cmd_sent && !adapter->curr_cmd | ||
194 | && !is_command_pending(adapter)) { | ||
195 | adapter->delay_null_pkt = false; | ||
196 | ret = true; | ||
197 | } else { | ||
198 | ret = false; | ||
199 | adapter->delay_null_pkt = true; | ||
200 | } | ||
201 | return ret; | ||
202 | } | ||
diff --git a/drivers/net/wireless/mwifiex/txrx.c b/drivers/net/wireless/mwifiex/txrx.c new file mode 100644 index 000000000000..f06923cb1c4b --- /dev/null +++ b/drivers/net/wireless/mwifiex/txrx.c | |||
@@ -0,0 +1,202 @@ | |||
1 | /* | ||
2 | * Marvell Wireless LAN device driver: generic TX/RX data handling | ||
3 | * | ||
4 | * Copyright (C) 2011, Marvell International Ltd. | ||
5 | * | ||
6 | * This software file (the "File") is distributed by Marvell International | ||
7 | * Ltd. under the terms of the GNU General Public License Version 2, June 1991 | ||
8 | * (the "License"). You may use, redistribute and/or modify this File in | ||
9 | * accordance with the terms and conditions of the License, a copy of which | ||
10 | * is available by writing to the Free Software Foundation, Inc., | ||
11 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the | ||
12 | * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. | ||
13 | * | ||
14 | * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE | ||
15 | * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE | ||
16 | * ARE EXPRESSLY DISCLAIMED. The License provides additional details about | ||
17 | * this warranty disclaimer. | ||
18 | */ | ||
19 | |||
20 | #include "decl.h" | ||
21 | #include "ioctl.h" | ||
22 | #include "util.h" | ||
23 | #include "fw.h" | ||
24 | #include "main.h" | ||
25 | #include "wmm.h" | ||
26 | |||
27 | /* | ||
28 | * This function processes the received buffer. | ||
29 | * | ||
30 | * Main responsibility of this function is to parse the RxPD to | ||
31 | * identify the correct interface this packet is headed for and | ||
32 | * forwarding it to the associated handling function, where the | ||
33 | * packet will be further processed and sent to kernel/upper layer | ||
34 | * if required. | ||
35 | */ | ||
36 | int mwifiex_handle_rx_packet(struct mwifiex_adapter *adapter, | ||
37 | struct sk_buff *skb) | ||
38 | { | ||
39 | int ret = 0; | ||
40 | struct mwifiex_private *priv = | ||
41 | mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY); | ||
42 | struct rxpd *local_rx_pd; | ||
43 | struct mwifiex_rxinfo *rx_info = MWIFIEX_SKB_RXCB(skb); | ||
44 | |||
45 | local_rx_pd = (struct rxpd *) (skb->data); | ||
46 | /* Get the BSS number from rxpd, get corresponding priv */ | ||
47 | priv = mwifiex_get_priv_by_id(adapter, local_rx_pd->bss_num & | ||
48 | BSS_NUM_MASK, local_rx_pd->bss_type); | ||
49 | if (!priv) | ||
50 | priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY); | ||
51 | |||
52 | rx_info->bss_index = priv->bss_index; | ||
53 | ret = mwifiex_process_sta_rx_packet(adapter, skb); | ||
54 | |||
55 | return ret; | ||
56 | } | ||
57 | EXPORT_SYMBOL_GPL(mwifiex_handle_rx_packet); | ||
58 | |||
59 | /* | ||
60 | * This function sends a packet to device. | ||
61 | * | ||
62 | * It processes the packet to add the TxPD, checks condition and | ||
63 | * sends the processed packet to firmware for transmission. | ||
64 | * | ||
65 | * On successful completion, the function calls the completion callback | ||
66 | * and logs the time. | ||
67 | */ | ||
68 | int mwifiex_process_tx(struct mwifiex_private *priv, struct sk_buff *skb, | ||
69 | struct mwifiex_tx_param *tx_param) | ||
70 | { | ||
71 | int ret = -1; | ||
72 | struct mwifiex_adapter *adapter = priv->adapter; | ||
73 | u8 *head_ptr = NULL; | ||
74 | struct txpd *local_tx_pd = NULL; | ||
75 | |||
76 | head_ptr = (u8 *) mwifiex_process_sta_txpd(priv, skb); | ||
77 | if (head_ptr) { | ||
78 | if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA) | ||
79 | local_tx_pd = | ||
80 | (struct txpd *) (head_ptr + INTF_HEADER_LEN); | ||
81 | |||
82 | ret = adapter->if_ops.host_to_card(adapter, MWIFIEX_TYPE_DATA, | ||
83 | skb->data, skb->len, tx_param); | ||
84 | } | ||
85 | |||
86 | switch (ret) { | ||
87 | case -EBUSY: | ||
88 | if ((GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA) && | ||
89 | (adapter->pps_uapsd_mode) && | ||
90 | (adapter->tx_lock_flag)) { | ||
91 | priv->adapter->tx_lock_flag = false; | ||
92 | local_tx_pd->flags = 0; | ||
93 | } | ||
94 | dev_dbg(adapter->dev, "data: -EBUSY is returned\n"); | ||
95 | break; | ||
96 | case -1: | ||
97 | adapter->data_sent = false; | ||
98 | dev_err(adapter->dev, "mwifiex_write_data_async failed: 0x%X\n", | ||
99 | ret); | ||
100 | adapter->dbg.num_tx_host_to_card_failure++; | ||
101 | mwifiex_write_data_complete(adapter, skb, ret); | ||
102 | break; | ||
103 | case -EINPROGRESS: | ||
104 | adapter->data_sent = false; | ||
105 | break; | ||
106 | case 0: | ||
107 | mwifiex_write_data_complete(adapter, skb, ret); | ||
108 | break; | ||
109 | default: | ||
110 | break; | ||
111 | } | ||
112 | |||
113 | return ret; | ||
114 | } | ||
115 | |||
116 | /* | ||
117 | * Packet send completion callback handler. | ||
118 | * | ||
119 | * It either frees the buffer directly or forwards it to another | ||
120 | * completion callback which checks conditions, updates statistics, | ||
121 | * wakes up stalled traffic queue if required, and then frees the buffer. | ||
122 | */ | ||
123 | int mwifiex_write_data_complete(struct mwifiex_adapter *adapter, | ||
124 | struct sk_buff *skb, int status) | ||
125 | { | ||
126 | struct mwifiex_private *priv = NULL, *tpriv = NULL; | ||
127 | struct mwifiex_txinfo *tx_info = NULL; | ||
128 | int i; | ||
129 | |||
130 | if (!skb) | ||
131 | return 0; | ||
132 | |||
133 | tx_info = MWIFIEX_SKB_TXCB(skb); | ||
134 | priv = mwifiex_bss_index_to_priv(adapter, tx_info->bss_index); | ||
135 | if (!priv) | ||
136 | goto done; | ||
137 | |||
138 | priv->netdev->trans_start = jiffies; | ||
139 | if (!status) { | ||
140 | priv->stats.tx_packets++; | ||
141 | priv->stats.tx_bytes += skb->len; | ||
142 | } else { | ||
143 | priv->stats.tx_errors++; | ||
144 | } | ||
145 | atomic_dec(&adapter->tx_pending); | ||
146 | |||
147 | for (i = 0; i < adapter->priv_num; i++) { | ||
148 | |||
149 | tpriv = adapter->priv[i]; | ||
150 | |||
151 | if ((GET_BSS_ROLE(tpriv) == MWIFIEX_BSS_ROLE_STA) | ||
152 | && (tpriv->media_connected)) { | ||
153 | if (netif_queue_stopped(tpriv->netdev)) | ||
154 | netif_wake_queue(tpriv->netdev); | ||
155 | } | ||
156 | } | ||
157 | done: | ||
158 | dev_kfree_skb_any(skb); | ||
159 | |||
160 | return 0; | ||
161 | } | ||
162 | |||
163 | /* | ||
164 | * Packet receive completion callback handler. | ||
165 | * | ||
166 | * This function calls another completion callback handler which | ||
167 | * updates the statistics, and optionally updates the parent buffer | ||
168 | * use count before freeing the received packet. | ||
169 | */ | ||
170 | int mwifiex_recv_packet_complete(struct mwifiex_adapter *adapter, | ||
171 | struct sk_buff *skb, int status) | ||
172 | { | ||
173 | struct mwifiex_rxinfo *rx_info = MWIFIEX_SKB_RXCB(skb); | ||
174 | struct mwifiex_rxinfo *rx_info_parent = NULL; | ||
175 | struct mwifiex_private *priv; | ||
176 | struct sk_buff *skb_parent = NULL; | ||
177 | unsigned long flags; | ||
178 | |||
179 | priv = adapter->priv[rx_info->bss_index]; | ||
180 | |||
181 | if (priv && (status == -1)) | ||
182 | priv->stats.rx_dropped++; | ||
183 | |||
184 | if (rx_info->parent) { | ||
185 | skb_parent = rx_info->parent; | ||
186 | rx_info_parent = MWIFIEX_SKB_RXCB(skb_parent); | ||
187 | |||
188 | spin_lock_irqsave(&priv->rx_pkt_lock, flags); | ||
189 | --rx_info_parent->use_count; | ||
190 | |||
191 | if (!rx_info_parent->use_count) { | ||
192 | spin_unlock_irqrestore(&priv->rx_pkt_lock, flags); | ||
193 | dev_kfree_skb_any(skb_parent); | ||
194 | } else { | ||
195 | spin_unlock_irqrestore(&priv->rx_pkt_lock, flags); | ||
196 | } | ||
197 | } else { | ||
198 | dev_kfree_skb_any(skb); | ||
199 | } | ||
200 | |||
201 | return 0; | ||
202 | } | ||
diff --git a/drivers/net/wireless/mwifiex/util.c b/drivers/net/wireless/mwifiex/util.c new file mode 100644 index 000000000000..205022aa52f5 --- /dev/null +++ b/drivers/net/wireless/mwifiex/util.c | |||
@@ -0,0 +1,252 @@ | |||
1 | /* | ||
2 | * Marvell Wireless LAN device driver: utility functions | ||
3 | * | ||
4 | * Copyright (C) 2011, Marvell International Ltd. | ||
5 | * | ||
6 | * This software file (the "File") is distributed by Marvell International | ||
7 | * Ltd. under the terms of the GNU General Public License Version 2, June 1991 | ||
8 | * (the "License"). You may use, redistribute and/or modify this File in | ||
9 | * accordance with the terms and conditions of the License, a copy of which | ||
10 | * is available by writing to the Free Software Foundation, Inc., | ||
11 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the | ||
12 | * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. | ||
13 | * | ||
14 | * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE | ||
15 | * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE | ||
16 | * ARE EXPRESSLY DISCLAIMED. The License provides additional details about | ||
17 | * this warranty disclaimer. | ||
18 | */ | ||
19 | |||
20 | #include "decl.h" | ||
21 | #include "ioctl.h" | ||
22 | #include "util.h" | ||
23 | #include "fw.h" | ||
24 | #include "main.h" | ||
25 | #include "wmm.h" | ||
26 | #include "11n.h" | ||
27 | |||
28 | /* | ||
29 | * Firmware initialization complete callback handler. | ||
30 | * | ||
31 | * This function wakes up the function waiting on the init | ||
32 | * wait queue for the firmware initialization to complete. | ||
33 | */ | ||
34 | int mwifiex_init_fw_complete(struct mwifiex_adapter *adapter) | ||
35 | { | ||
36 | |||
37 | adapter->init_wait_q_woken = true; | ||
38 | wake_up_interruptible(&adapter->init_wait_q); | ||
39 | return 0; | ||
40 | } | ||
41 | |||
42 | /* | ||
43 | * Firmware shutdown complete callback handler. | ||
44 | * | ||
45 | * This function sets the hardware status to not ready and wakes up | ||
46 | * the function waiting on the init wait queue for the firmware | ||
47 | * shutdown to complete. | ||
48 | */ | ||
49 | int mwifiex_shutdown_fw_complete(struct mwifiex_adapter *adapter) | ||
50 | { | ||
51 | adapter->hw_status = MWIFIEX_HW_STATUS_NOT_READY; | ||
52 | adapter->init_wait_q_woken = true; | ||
53 | wake_up_interruptible(&adapter->init_wait_q); | ||
54 | return 0; | ||
55 | } | ||
56 | |||
57 | /* | ||
58 | * IOCTL request handler to send function init/shutdown command | ||
59 | * to firmware. | ||
60 | * | ||
61 | * This function prepares the correct firmware command and | ||
62 | * issues it. | ||
63 | */ | ||
64 | int mwifiex_misc_ioctl_init_shutdown(struct mwifiex_adapter *adapter, | ||
65 | struct mwifiex_wait_queue *wait, | ||
66 | u32 func_init_shutdown) | ||
67 | { | ||
68 | struct mwifiex_private *priv = adapter->priv[wait->bss_index]; | ||
69 | int ret; | ||
70 | u16 cmd; | ||
71 | |||
72 | if (func_init_shutdown == MWIFIEX_FUNC_INIT) { | ||
73 | cmd = HostCmd_CMD_FUNC_INIT; | ||
74 | } else if (func_init_shutdown == MWIFIEX_FUNC_SHUTDOWN) { | ||
75 | cmd = HostCmd_CMD_FUNC_SHUTDOWN; | ||
76 | } else { | ||
77 | dev_err(adapter->dev, "unsupported parameter\n"); | ||
78 | return -1; | ||
79 | } | ||
80 | |||
81 | /* Send command to firmware */ | ||
82 | ret = mwifiex_prepare_cmd(priv, cmd, HostCmd_ACT_GEN_SET, | ||
83 | 0, wait, NULL); | ||
84 | |||
85 | if (!ret) | ||
86 | ret = -EINPROGRESS; | ||
87 | |||
88 | return ret; | ||
89 | } | ||
90 | |||
91 | /* | ||
92 | * IOCTL request handler to set/get debug information. | ||
93 | * | ||
94 | * This function collates/sets the information from/to different driver | ||
95 | * structures. | ||
96 | */ | ||
97 | int mwifiex_get_debug_info(struct mwifiex_private *priv, | ||
98 | struct mwifiex_debug_info *info) | ||
99 | { | ||
100 | struct mwifiex_adapter *adapter = priv->adapter; | ||
101 | |||
102 | if (info) { | ||
103 | memcpy(info->packets_out, | ||
104 | priv->wmm.packets_out, | ||
105 | sizeof(priv->wmm.packets_out)); | ||
106 | info->max_tx_buf_size = (u32) adapter->max_tx_buf_size; | ||
107 | info->tx_buf_size = (u32) adapter->tx_buf_size; | ||
108 | info->rx_tbl_num = mwifiex_get_rx_reorder_tbl( | ||
109 | priv, info->rx_tbl); | ||
110 | info->tx_tbl_num = mwifiex_get_tx_ba_stream_tbl( | ||
111 | priv, info->tx_tbl); | ||
112 | info->ps_mode = adapter->ps_mode; | ||
113 | info->ps_state = adapter->ps_state; | ||
114 | info->is_deep_sleep = adapter->is_deep_sleep; | ||
115 | info->pm_wakeup_card_req = adapter->pm_wakeup_card_req; | ||
116 | info->pm_wakeup_fw_try = adapter->pm_wakeup_fw_try; | ||
117 | info->is_hs_configured = adapter->is_hs_configured; | ||
118 | info->hs_activated = adapter->hs_activated; | ||
119 | info->num_cmd_host_to_card_failure | ||
120 | = adapter->dbg.num_cmd_host_to_card_failure; | ||
121 | info->num_cmd_sleep_cfm_host_to_card_failure | ||
122 | = adapter->dbg.num_cmd_sleep_cfm_host_to_card_failure; | ||
123 | info->num_tx_host_to_card_failure | ||
124 | = adapter->dbg.num_tx_host_to_card_failure; | ||
125 | info->num_event_deauth = adapter->dbg.num_event_deauth; | ||
126 | info->num_event_disassoc = adapter->dbg.num_event_disassoc; | ||
127 | info->num_event_link_lost = adapter->dbg.num_event_link_lost; | ||
128 | info->num_cmd_deauth = adapter->dbg.num_cmd_deauth; | ||
129 | info->num_cmd_assoc_success = | ||
130 | adapter->dbg.num_cmd_assoc_success; | ||
131 | info->num_cmd_assoc_failure = | ||
132 | adapter->dbg.num_cmd_assoc_failure; | ||
133 | info->num_tx_timeout = adapter->dbg.num_tx_timeout; | ||
134 | info->num_cmd_timeout = adapter->dbg.num_cmd_timeout; | ||
135 | info->timeout_cmd_id = adapter->dbg.timeout_cmd_id; | ||
136 | info->timeout_cmd_act = adapter->dbg.timeout_cmd_act; | ||
137 | memcpy(info->last_cmd_id, adapter->dbg.last_cmd_id, | ||
138 | sizeof(adapter->dbg.last_cmd_id)); | ||
139 | memcpy(info->last_cmd_act, adapter->dbg.last_cmd_act, | ||
140 | sizeof(adapter->dbg.last_cmd_act)); | ||
141 | info->last_cmd_index = adapter->dbg.last_cmd_index; | ||
142 | memcpy(info->last_cmd_resp_id, adapter->dbg.last_cmd_resp_id, | ||
143 | sizeof(adapter->dbg.last_cmd_resp_id)); | ||
144 | info->last_cmd_resp_index = adapter->dbg.last_cmd_resp_index; | ||
145 | memcpy(info->last_event, adapter->dbg.last_event, | ||
146 | sizeof(adapter->dbg.last_event)); | ||
147 | info->last_event_index = adapter->dbg.last_event_index; | ||
148 | info->data_sent = adapter->data_sent; | ||
149 | info->cmd_sent = adapter->cmd_sent; | ||
150 | info->cmd_resp_received = adapter->cmd_resp_received; | ||
151 | } | ||
152 | |||
153 | return 0; | ||
154 | } | ||
155 | |||
156 | /* | ||
157 | * This function processes the received packet before sending it to the | ||
158 | * kernel. | ||
159 | * | ||
160 | * It extracts the SKB from the received buffer and sends it to kernel. | ||
161 | * In case the received buffer does not contain the data in SKB format, | ||
162 | * the function creates a blank SKB, fills it with the data from the | ||
163 | * received buffer and then sends this new SKB to the kernel. | ||
164 | */ | ||
165 | int mwifiex_recv_packet(struct mwifiex_adapter *adapter, struct sk_buff *skb) | ||
166 | { | ||
167 | struct mwifiex_rxinfo *rx_info = NULL; | ||
168 | struct mwifiex_private *priv = NULL; | ||
169 | |||
170 | if (!skb) | ||
171 | return -1; | ||
172 | |||
173 | rx_info = MWIFIEX_SKB_RXCB(skb); | ||
174 | priv = mwifiex_bss_index_to_priv(adapter, rx_info->bss_index); | ||
175 | if (!priv) | ||
176 | return -1; | ||
177 | |||
178 | skb->dev = priv->netdev; | ||
179 | skb->protocol = eth_type_trans(skb, priv->netdev); | ||
180 | skb->ip_summed = CHECKSUM_NONE; | ||
181 | priv->stats.rx_bytes += skb->len; | ||
182 | priv->stats.rx_packets++; | ||
183 | if (in_interrupt()) | ||
184 | netif_rx(skb); | ||
185 | else | ||
186 | netif_rx_ni(skb); | ||
187 | |||
188 | return 0; | ||
189 | } | ||
190 | |||
191 | /* | ||
192 | * Receive packet completion callback handler. | ||
193 | * | ||
194 | * This function updates the statistics and frees the buffer SKB. | ||
195 | */ | ||
196 | int mwifiex_recv_complete(struct mwifiex_adapter *adapter, | ||
197 | struct sk_buff *skb, int status) | ||
198 | { | ||
199 | struct mwifiex_private *priv = NULL; | ||
200 | struct mwifiex_rxinfo *rx_info = NULL; | ||
201 | |||
202 | if (!skb) | ||
203 | return 0; | ||
204 | |||
205 | rx_info = MWIFIEX_SKB_RXCB(skb); | ||
206 | priv = mwifiex_bss_index_to_priv(adapter, rx_info->bss_index); | ||
207 | |||
208 | if (priv && (status == -1)) | ||
209 | priv->stats.rx_dropped++; | ||
210 | |||
211 | dev_kfree_skb_any(skb); | ||
212 | |||
213 | return 0; | ||
214 | } | ||
215 | |||
216 | /* | ||
217 | * IOCTL completion callback handler. | ||
218 | * | ||
219 | * This function is called when a pending IOCTL is completed. | ||
220 | * | ||
221 | * If work queue support is enabled, the function wakes up the | ||
222 | * corresponding waiting function. Otherwise, it processes the | ||
223 | * IOCTL response and frees the response buffer. | ||
224 | */ | ||
225 | int mwifiex_ioctl_complete(struct mwifiex_adapter *adapter, | ||
226 | struct mwifiex_wait_queue *wait_queue, | ||
227 | int status) | ||
228 | { | ||
229 | enum mwifiex_error_code status_code = | ||
230 | (enum mwifiex_error_code) wait_queue->status; | ||
231 | |||
232 | atomic_dec(&adapter->ioctl_pending); | ||
233 | |||
234 | dev_dbg(adapter->dev, "cmd: IOCTL completed: status=%d," | ||
235 | " status_code=%#x\n", status, status_code); | ||
236 | |||
237 | if (wait_queue->enabled) { | ||
238 | *wait_queue->condition = true; | ||
239 | wait_queue->status = status; | ||
240 | if (status && (status_code == MWIFIEX_ERROR_CMD_TIMEOUT)) | ||
241 | dev_err(adapter->dev, "cmd timeout\n"); | ||
242 | else | ||
243 | wake_up_interruptible(wait_queue->wait); | ||
244 | } else { | ||
245 | if (status) | ||
246 | dev_err(adapter->dev, "cmd failed: status_code=%#x\n", | ||
247 | status_code); | ||
248 | kfree(wait_queue); | ||
249 | } | ||
250 | |||
251 | return 0; | ||
252 | } | ||
diff --git a/drivers/net/wireless/mwifiex/util.h b/drivers/net/wireless/mwifiex/util.h new file mode 100644 index 000000000000..9506afc6c0e4 --- /dev/null +++ b/drivers/net/wireless/mwifiex/util.h | |||
@@ -0,0 +1,32 @@ | |||
1 | /* | ||
2 | * Marvell Wireless LAN device driver: utility functions | ||
3 | * | ||
4 | * Copyright (C) 2011, Marvell International Ltd. | ||
5 | * | ||
6 | * This software file (the "File") is distributed by Marvell International | ||
7 | * Ltd. under the terms of the GNU General Public License Version 2, June 1991 | ||
8 | * (the "License"). You may use, redistribute and/or modify this File in | ||
9 | * accordance with the terms and conditions of the License, a copy of which | ||
10 | * is available by writing to the Free Software Foundation, Inc., | ||
11 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the | ||
12 | * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. | ||
13 | * | ||
14 | * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE | ||
15 | * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE | ||
16 | * ARE EXPRESSLY DISCLAIMED. The License provides additional details about | ||
17 | * this warranty disclaimer. | ||
18 | */ | ||
19 | |||
20 | #ifndef _MWIFIEX_UTIL_H_ | ||
21 | #define _MWIFIEX_UTIL_H_ | ||
22 | |||
23 | static inline struct mwifiex_rxinfo *MWIFIEX_SKB_RXCB(struct sk_buff *skb) | ||
24 | { | ||
25 | return (struct mwifiex_rxinfo *)skb->cb; | ||
26 | } | ||
27 | |||
28 | static inline struct mwifiex_txinfo *MWIFIEX_SKB_TXCB(struct sk_buff *skb) | ||
29 | { | ||
30 | return (struct mwifiex_txinfo *)skb->cb; | ||
31 | } | ||
32 | #endif /* !_MWIFIEX_UTIL_H_ */ | ||
diff --git a/drivers/net/wireless/mwifiex/wmm.c b/drivers/net/wireless/mwifiex/wmm.c new file mode 100644 index 000000000000..1cfbc6bed692 --- /dev/null +++ b/drivers/net/wireless/mwifiex/wmm.c | |||
@@ -0,0 +1,1237 @@ | |||
1 | /* | ||
2 | * Marvell Wireless LAN device driver: WMM | ||
3 | * | ||
4 | * Copyright (C) 2011, Marvell International Ltd. | ||
5 | * | ||
6 | * This software file (the "File") is distributed by Marvell International | ||
7 | * Ltd. under the terms of the GNU General Public License Version 2, June 1991 | ||
8 | * (the "License"). You may use, redistribute and/or modify this File in | ||
9 | * accordance with the terms and conditions of the License, a copy of which | ||
10 | * is available by writing to the Free Software Foundation, Inc., | ||
11 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the | ||
12 | * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. | ||
13 | * | ||
14 | * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE | ||
15 | * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE | ||
16 | * ARE EXPRESSLY DISCLAIMED. The License provides additional details about | ||
17 | * this warranty disclaimer. | ||
18 | */ | ||
19 | |||
20 | #include "decl.h" | ||
21 | #include "ioctl.h" | ||
22 | #include "util.h" | ||
23 | #include "fw.h" | ||
24 | #include "main.h" | ||
25 | #include "wmm.h" | ||
26 | #include "11n.h" | ||
27 | |||
28 | |||
29 | /* Maximum value FW can accept for driver delay in packet transmission */ | ||
30 | #define DRV_PKT_DELAY_TO_FW_MAX 512 | ||
31 | |||
32 | |||
33 | #define WMM_QUEUED_PACKET_LOWER_LIMIT 180 | ||
34 | |||
35 | #define WMM_QUEUED_PACKET_UPPER_LIMIT 200 | ||
36 | |||
37 | /* Offset for TOS field in the IP header */ | ||
38 | #define IPTOS_OFFSET 5 | ||
39 | |||
40 | /* WMM information IE */ | ||
41 | static const u8 wmm_info_ie[] = { WLAN_EID_VENDOR_SPECIFIC, 0x07, | ||
42 | 0x00, 0x50, 0xf2, 0x02, | ||
43 | 0x00, 0x01, 0x00 | ||
44 | }; | ||
45 | |||
46 | static const u8 wmm_aci_to_qidx_map[] = { WMM_AC_BE, | ||
47 | WMM_AC_BK, | ||
48 | WMM_AC_VI, | ||
49 | WMM_AC_VO | ||
50 | }; | ||
51 | |||
52 | static u8 tos_to_tid[] = { | ||
53 | /* TID DSCP_P2 DSCP_P1 DSCP_P0 WMM_AC */ | ||
54 | 0x01, /* 0 1 0 AC_BK */ | ||
55 | 0x02, /* 0 0 0 AC_BK */ | ||
56 | 0x00, /* 0 0 1 AC_BE */ | ||
57 | 0x03, /* 0 1 1 AC_BE */ | ||
58 | 0x04, /* 1 0 0 AC_VI */ | ||
59 | 0x05, /* 1 0 1 AC_VI */ | ||
60 | 0x06, /* 1 1 0 AC_VO */ | ||
61 | 0x07 /* 1 1 1 AC_VO */ | ||
62 | }; | ||
63 | |||
64 | /* | ||
65 | * This table inverses the tos_to_tid operation to get a priority | ||
66 | * which is in sequential order, and can be compared. | ||
67 | * Use this to compare the priority of two different TIDs. | ||
68 | */ | ||
69 | static u8 tos_to_tid_inv[] = { | ||
70 | 0x02, /* from tos_to_tid[2] = 0 */ | ||
71 | 0x00, /* from tos_to_tid[0] = 1 */ | ||
72 | 0x01, /* from tos_to_tid[1] = 2 */ | ||
73 | 0x03, | ||
74 | 0x04, | ||
75 | 0x05, | ||
76 | 0x06, | ||
77 | 0x07}; | ||
78 | |||
79 | static u8 ac_to_tid[4][2] = { {1, 2}, {0, 3}, {4, 5}, {6, 7} }; | ||
80 | |||
81 | /* | ||
82 | * This function debug prints the priority parameters for a WMM AC. | ||
83 | */ | ||
84 | static void | ||
85 | mwifiex_wmm_ac_debug_print(const struct ieee_types_wmm_ac_parameters *ac_param) | ||
86 | { | ||
87 | const char *ac_str[] = { "BK", "BE", "VI", "VO" }; | ||
88 | |||
89 | pr_debug("info: WMM AC_%s: ACI=%d, ACM=%d, Aifsn=%d, " | ||
90 | "EcwMin=%d, EcwMax=%d, TxopLimit=%d\n", | ||
91 | ac_str[wmm_aci_to_qidx_map[(ac_param->aci_aifsn_bitmap | ||
92 | & MWIFIEX_ACI) >> 5]], | ||
93 | (ac_param->aci_aifsn_bitmap & MWIFIEX_ACI) >> 5, | ||
94 | (ac_param->aci_aifsn_bitmap & MWIFIEX_ACM) >> 4, | ||
95 | ac_param->aci_aifsn_bitmap & MWIFIEX_AIFSN, | ||
96 | ac_param->ecw_bitmap & MWIFIEX_ECW_MIN, | ||
97 | (ac_param->ecw_bitmap & MWIFIEX_ECW_MAX) >> 4, | ||
98 | le16_to_cpu(ac_param->tx_op_limit)); | ||
99 | } | ||
100 | |||
101 | /* | ||
102 | * This function allocates a route address list. | ||
103 | * | ||
104 | * The function also initializes the list with the provided RA. | ||
105 | */ | ||
106 | static struct mwifiex_ra_list_tbl * | ||
107 | mwifiex_wmm_allocate_ralist_node(struct mwifiex_adapter *adapter, u8 *ra) | ||
108 | { | ||
109 | struct mwifiex_ra_list_tbl *ra_list; | ||
110 | |||
111 | ra_list = kzalloc(sizeof(struct mwifiex_ra_list_tbl), GFP_ATOMIC); | ||
112 | |||
113 | if (!ra_list) { | ||
114 | dev_err(adapter->dev, "%s: failed to alloc ra_list\n", | ||
115 | __func__); | ||
116 | return NULL; | ||
117 | } | ||
118 | INIT_LIST_HEAD(&ra_list->list); | ||
119 | skb_queue_head_init(&ra_list->skb_head); | ||
120 | |||
121 | memcpy(ra_list->ra, ra, ETH_ALEN); | ||
122 | |||
123 | ra_list->total_pkts_size = 0; | ||
124 | |||
125 | dev_dbg(adapter->dev, "info: allocated ra_list %p\n", ra_list); | ||
126 | |||
127 | return ra_list; | ||
128 | } | ||
129 | |||
130 | /* | ||
131 | * This function allocates and adds a RA list for all TIDs | ||
132 | * with the given RA. | ||
133 | */ | ||
134 | void | ||
135 | mwifiex_ralist_add(struct mwifiex_private *priv, u8 *ra) | ||
136 | { | ||
137 | int i; | ||
138 | struct mwifiex_ra_list_tbl *ra_list; | ||
139 | struct mwifiex_adapter *adapter = priv->adapter; | ||
140 | |||
141 | for (i = 0; i < MAX_NUM_TID; ++i) { | ||
142 | ra_list = mwifiex_wmm_allocate_ralist_node(adapter, ra); | ||
143 | dev_dbg(adapter->dev, "info: created ra_list %p\n", ra_list); | ||
144 | |||
145 | if (!ra_list) | ||
146 | break; | ||
147 | |||
148 | if (!mwifiex_queuing_ra_based(priv)) | ||
149 | ra_list->is_11n_enabled = IS_11N_ENABLED(priv); | ||
150 | else | ||
151 | ra_list->is_11n_enabled = false; | ||
152 | |||
153 | dev_dbg(adapter->dev, "data: ralist %p: is_11n_enabled=%d\n", | ||
154 | ra_list, ra_list->is_11n_enabled); | ||
155 | |||
156 | list_add_tail(&ra_list->list, | ||
157 | &priv->wmm.tid_tbl_ptr[i].ra_list); | ||
158 | |||
159 | if (!priv->wmm.tid_tbl_ptr[i].ra_list_curr) | ||
160 | priv->wmm.tid_tbl_ptr[i].ra_list_curr = ra_list; | ||
161 | } | ||
162 | } | ||
163 | |||
164 | /* | ||
165 | * This function sets the WMM queue priorities to their default values. | ||
166 | */ | ||
167 | static void mwifiex_wmm_default_queue_priorities(struct mwifiex_private *priv) | ||
168 | { | ||
169 | /* Default queue priorities: VO->VI->BE->BK */ | ||
170 | priv->wmm.queue_priority[0] = WMM_AC_VO; | ||
171 | priv->wmm.queue_priority[1] = WMM_AC_VI; | ||
172 | priv->wmm.queue_priority[2] = WMM_AC_BE; | ||
173 | priv->wmm.queue_priority[3] = WMM_AC_BK; | ||
174 | } | ||
175 | |||
176 | /* | ||
177 | * This function map ACs to TIDs. | ||
178 | */ | ||
179 | static void | ||
180 | mwifiex_wmm_queue_priorities_tid(struct mwifiex_private *priv, | ||
181 | u8 queue_priority[]) | ||
182 | { | ||
183 | int i; | ||
184 | |||
185 | for (i = 0; i < 4; ++i) { | ||
186 | tos_to_tid[7 - (i * 2)] = ac_to_tid[queue_priority[i]][1]; | ||
187 | tos_to_tid[6 - (i * 2)] = ac_to_tid[queue_priority[i]][0]; | ||
188 | } | ||
189 | } | ||
190 | |||
191 | /* | ||
192 | * This function initializes WMM priority queues. | ||
193 | */ | ||
194 | void | ||
195 | mwifiex_wmm_setup_queue_priorities(struct mwifiex_private *priv, | ||
196 | struct ieee_types_wmm_parameter *wmm_ie) | ||
197 | { | ||
198 | u16 cw_min, avg_back_off, tmp[4]; | ||
199 | u32 i, j, num_ac; | ||
200 | u8 ac_idx; | ||
201 | |||
202 | if (!wmm_ie || !priv->wmm_enabled) { | ||
203 | /* WMM is not enabled, just set the defaults and return */ | ||
204 | mwifiex_wmm_default_queue_priorities(priv); | ||
205 | return; | ||
206 | } | ||
207 | |||
208 | dev_dbg(priv->adapter->dev, "info: WMM Parameter IE: version=%d, " | ||
209 | "qos_info Parameter Set Count=%d, Reserved=%#x\n", | ||
210 | wmm_ie->vend_hdr.version, wmm_ie->qos_info_bitmap & | ||
211 | IEEE80211_WMM_IE_AP_QOSINFO_PARAM_SET_CNT_MASK, | ||
212 | wmm_ie->reserved); | ||
213 | |||
214 | for (num_ac = 0; num_ac < ARRAY_SIZE(wmm_ie->ac_params); num_ac++) { | ||
215 | cw_min = (1 << (wmm_ie->ac_params[num_ac].ecw_bitmap & | ||
216 | MWIFIEX_ECW_MIN)) - 1; | ||
217 | avg_back_off = (cw_min >> 1) + | ||
218 | (wmm_ie->ac_params[num_ac].aci_aifsn_bitmap & | ||
219 | MWIFIEX_AIFSN); | ||
220 | |||
221 | ac_idx = wmm_aci_to_qidx_map[(wmm_ie->ac_params[num_ac]. | ||
222 | aci_aifsn_bitmap & | ||
223 | MWIFIEX_ACI) >> 5]; | ||
224 | priv->wmm.queue_priority[ac_idx] = ac_idx; | ||
225 | tmp[ac_idx] = avg_back_off; | ||
226 | |||
227 | dev_dbg(priv->adapter->dev, "info: WMM: CWmax=%d CWmin=%d Avg Back-off=%d\n", | ||
228 | (1 << ((wmm_ie->ac_params[num_ac].ecw_bitmap & | ||
229 | MWIFIEX_ECW_MAX) >> 4)) - 1, | ||
230 | cw_min, avg_back_off); | ||
231 | mwifiex_wmm_ac_debug_print(&wmm_ie->ac_params[num_ac]); | ||
232 | } | ||
233 | |||
234 | /* Bubble sort */ | ||
235 | for (i = 0; i < num_ac; i++) { | ||
236 | for (j = 1; j < num_ac - i; j++) { | ||
237 | if (tmp[j - 1] > tmp[j]) { | ||
238 | swap(tmp[j - 1], tmp[j]); | ||
239 | swap(priv->wmm.queue_priority[j - 1], | ||
240 | priv->wmm.queue_priority[j]); | ||
241 | } else if (tmp[j - 1] == tmp[j]) { | ||
242 | if (priv->wmm.queue_priority[j - 1] | ||
243 | < priv->wmm.queue_priority[j]) | ||
244 | swap(priv->wmm.queue_priority[j - 1], | ||
245 | priv->wmm.queue_priority[j]); | ||
246 | } | ||
247 | } | ||
248 | } | ||
249 | |||
250 | mwifiex_wmm_queue_priorities_tid(priv, priv->wmm.queue_priority); | ||
251 | } | ||
252 | |||
253 | /* | ||
254 | * This function evaluates whether or not an AC is to be downgraded. | ||
255 | * | ||
256 | * In case the AC is not enabled, the highest AC is returned that is | ||
257 | * enabled and does not require admission control. | ||
258 | */ | ||
259 | static enum mwifiex_wmm_ac_e | ||
260 | mwifiex_wmm_eval_downgrade_ac(struct mwifiex_private *priv, | ||
261 | enum mwifiex_wmm_ac_e eval_ac) | ||
262 | { | ||
263 | int down_ac; | ||
264 | enum mwifiex_wmm_ac_e ret_ac; | ||
265 | struct mwifiex_wmm_ac_status *ac_status; | ||
266 | |||
267 | ac_status = &priv->wmm.ac_status[eval_ac]; | ||
268 | |||
269 | if (!ac_status->disabled) | ||
270 | /* Okay to use this AC, its enabled */ | ||
271 | return eval_ac; | ||
272 | |||
273 | /* Setup a default return value of the lowest priority */ | ||
274 | ret_ac = WMM_AC_BK; | ||
275 | |||
276 | /* | ||
277 | * Find the highest AC that is enabled and does not require | ||
278 | * admission control. The spec disallows downgrading to an AC, | ||
279 | * which is enabled due to a completed admission control. | ||
280 | * Unadmitted traffic is not to be sent on an AC with admitted | ||
281 | * traffic. | ||
282 | */ | ||
283 | for (down_ac = WMM_AC_BK; down_ac < eval_ac; down_ac++) { | ||
284 | ac_status = &priv->wmm.ac_status[down_ac]; | ||
285 | |||
286 | if (!ac_status->disabled && !ac_status->flow_required) | ||
287 | /* AC is enabled and does not require admission | ||
288 | control */ | ||
289 | ret_ac = (enum mwifiex_wmm_ac_e) down_ac; | ||
290 | } | ||
291 | |||
292 | return ret_ac; | ||
293 | } | ||
294 | |||
295 | /* | ||
296 | * This function downgrades WMM priority queue. | ||
297 | */ | ||
298 | void | ||
299 | mwifiex_wmm_setup_ac_downgrade(struct mwifiex_private *priv) | ||
300 | { | ||
301 | int ac_val; | ||
302 | |||
303 | dev_dbg(priv->adapter->dev, "info: WMM: AC Priorities:" | ||
304 | "BK(0), BE(1), VI(2), VO(3)\n"); | ||
305 | |||
306 | if (!priv->wmm_enabled) { | ||
307 | /* WMM is not enabled, default priorities */ | ||
308 | for (ac_val = WMM_AC_BK; ac_val <= WMM_AC_VO; ac_val++) | ||
309 | priv->wmm.ac_down_graded_vals[ac_val] = | ||
310 | (enum mwifiex_wmm_ac_e) ac_val; | ||
311 | } else { | ||
312 | for (ac_val = WMM_AC_BK; ac_val <= WMM_AC_VO; ac_val++) { | ||
313 | priv->wmm.ac_down_graded_vals[ac_val] | ||
314 | = mwifiex_wmm_eval_downgrade_ac(priv, | ||
315 | (enum mwifiex_wmm_ac_e) ac_val); | ||
316 | dev_dbg(priv->adapter->dev, "info: WMM: AC PRIO %d maps to %d\n", | ||
317 | ac_val, priv->wmm.ac_down_graded_vals[ac_val]); | ||
318 | } | ||
319 | } | ||
320 | } | ||
321 | |||
322 | /* | ||
323 | * This function converts the IP TOS field to an WMM AC | ||
324 | * Queue assignment. | ||
325 | */ | ||
326 | static enum mwifiex_wmm_ac_e | ||
327 | mwifiex_wmm_convert_tos_to_ac(struct mwifiex_adapter *adapter, u32 tos) | ||
328 | { | ||
329 | /* Map of TOS UP values to WMM AC */ | ||
330 | const enum mwifiex_wmm_ac_e tos_to_ac[] = { WMM_AC_BE, | ||
331 | WMM_AC_BK, | ||
332 | WMM_AC_BK, | ||
333 | WMM_AC_BE, | ||
334 | WMM_AC_VI, | ||
335 | WMM_AC_VI, | ||
336 | WMM_AC_VO, | ||
337 | WMM_AC_VO | ||
338 | }; | ||
339 | |||
340 | if (tos >= ARRAY_SIZE(tos_to_ac)) | ||
341 | return WMM_AC_BE; | ||
342 | |||
343 | return tos_to_ac[tos]; | ||
344 | } | ||
345 | |||
346 | /* | ||
347 | * This function evaluates a given TID and downgrades it to a lower | ||
348 | * TID if the WMM Parameter IE received from the AP indicates that the | ||
349 | * AP is disabled (due to call admission control (ACM bit). Mapping | ||
350 | * of TID to AC is taken care of internally. | ||
351 | */ | ||
352 | static u8 | ||
353 | mwifiex_wmm_downgrade_tid(struct mwifiex_private *priv, u32 tid) | ||
354 | { | ||
355 | enum mwifiex_wmm_ac_e ac, ac_down; | ||
356 | u8 new_tid; | ||
357 | |||
358 | ac = mwifiex_wmm_convert_tos_to_ac(priv->adapter, tid); | ||
359 | ac_down = priv->wmm.ac_down_graded_vals[ac]; | ||
360 | |||
361 | /* Send the index to tid array, picking from the array will be | ||
362 | * taken care by dequeuing function | ||
363 | */ | ||
364 | new_tid = ac_to_tid[ac_down][tid % 2]; | ||
365 | |||
366 | return new_tid; | ||
367 | } | ||
368 | |||
369 | /* | ||
370 | * This function initializes the WMM state information and the | ||
371 | * WMM data path queues. | ||
372 | */ | ||
373 | void | ||
374 | mwifiex_wmm_init(struct mwifiex_adapter *adapter) | ||
375 | { | ||
376 | int i, j; | ||
377 | struct mwifiex_private *priv; | ||
378 | |||
379 | for (j = 0; j < adapter->priv_num; ++j) { | ||
380 | priv = adapter->priv[j]; | ||
381 | if (!priv) | ||
382 | continue; | ||
383 | |||
384 | for (i = 0; i < MAX_NUM_TID; ++i) { | ||
385 | priv->aggr_prio_tbl[i].amsdu = tos_to_tid_inv[i]; | ||
386 | priv->aggr_prio_tbl[i].ampdu_ap = tos_to_tid_inv[i]; | ||
387 | priv->aggr_prio_tbl[i].ampdu_user = tos_to_tid_inv[i]; | ||
388 | priv->wmm.tid_tbl_ptr[i].ra_list_curr = NULL; | ||
389 | } | ||
390 | |||
391 | priv->aggr_prio_tbl[6].amsdu | ||
392 | = priv->aggr_prio_tbl[6].ampdu_ap | ||
393 | = priv->aggr_prio_tbl[6].ampdu_user | ||
394 | = BA_STREAM_NOT_ALLOWED; | ||
395 | |||
396 | priv->aggr_prio_tbl[7].amsdu = priv->aggr_prio_tbl[7].ampdu_ap | ||
397 | = priv->aggr_prio_tbl[7].ampdu_user | ||
398 | = BA_STREAM_NOT_ALLOWED; | ||
399 | |||
400 | priv->add_ba_param.timeout = MWIFIEX_DEFAULT_BLOCK_ACK_TIMEOUT; | ||
401 | priv->add_ba_param.tx_win_size = MWIFIEX_AMPDU_DEF_TXWINSIZE; | ||
402 | priv->add_ba_param.rx_win_size = MWIFIEX_AMPDU_DEF_RXWINSIZE; | ||
403 | } | ||
404 | } | ||
405 | |||
406 | /* | ||
407 | * This function checks if WMM Tx queue is empty. | ||
408 | */ | ||
409 | int | ||
410 | mwifiex_wmm_lists_empty(struct mwifiex_adapter *adapter) | ||
411 | { | ||
412 | int i, j; | ||
413 | struct mwifiex_private *priv; | ||
414 | |||
415 | for (j = 0; j < adapter->priv_num; ++j) { | ||
416 | priv = adapter->priv[j]; | ||
417 | if (priv) { | ||
418 | for (i = 0; i < MAX_NUM_TID; i++) | ||
419 | if (!mwifiex_wmm_is_ra_list_empty(adapter, | ||
420 | &priv->wmm.tid_tbl_ptr[i].ra_list)) | ||
421 | return false; | ||
422 | } | ||
423 | } | ||
424 | |||
425 | return true; | ||
426 | } | ||
427 | |||
428 | /* | ||
429 | * This function deletes all packets in an RA list node. | ||
430 | * | ||
431 | * The packet sent completion callback handler are called with | ||
432 | * status failure, after they are dequeued to ensure proper | ||
433 | * cleanup. The RA list node itself is freed at the end. | ||
434 | */ | ||
435 | static void | ||
436 | mwifiex_wmm_del_pkts_in_ralist_node(struct mwifiex_private *priv, | ||
437 | struct mwifiex_ra_list_tbl *ra_list) | ||
438 | { | ||
439 | struct mwifiex_adapter *adapter = priv->adapter; | ||
440 | struct sk_buff *skb, *tmp; | ||
441 | |||
442 | skb_queue_walk_safe(&ra_list->skb_head, skb, tmp) | ||
443 | mwifiex_write_data_complete(adapter, skb, -1); | ||
444 | } | ||
445 | |||
446 | /* | ||
447 | * This function deletes all packets in an RA list. | ||
448 | * | ||
449 | * Each nodes in the RA list are freed individually first, and then | ||
450 | * the RA list itself is freed. | ||
451 | */ | ||
452 | static void | ||
453 | mwifiex_wmm_del_pkts_in_ralist(struct mwifiex_private *priv, | ||
454 | struct list_head *ra_list_head) | ||
455 | { | ||
456 | struct mwifiex_ra_list_tbl *ra_list; | ||
457 | |||
458 | list_for_each_entry(ra_list, ra_list_head, list) | ||
459 | mwifiex_wmm_del_pkts_in_ralist_node(priv, ra_list); | ||
460 | } | ||
461 | |||
462 | /* | ||
463 | * This function deletes all packets in all RA lists. | ||
464 | */ | ||
465 | static void mwifiex_wmm_cleanup_queues(struct mwifiex_private *priv) | ||
466 | { | ||
467 | int i; | ||
468 | |||
469 | for (i = 0; i < MAX_NUM_TID; i++) | ||
470 | mwifiex_wmm_del_pkts_in_ralist(priv, &priv->wmm.tid_tbl_ptr[i]. | ||
471 | ra_list); | ||
472 | } | ||
473 | |||
474 | /* | ||
475 | * This function deletes all route addresses from all RA lists. | ||
476 | */ | ||
477 | static void mwifiex_wmm_delete_all_ralist(struct mwifiex_private *priv) | ||
478 | { | ||
479 | struct mwifiex_ra_list_tbl *ra_list, *tmp_node; | ||
480 | int i; | ||
481 | |||
482 | for (i = 0; i < MAX_NUM_TID; ++i) { | ||
483 | dev_dbg(priv->adapter->dev, | ||
484 | "info: ra_list: freeing buf for tid %d\n", i); | ||
485 | list_for_each_entry_safe(ra_list, tmp_node, | ||
486 | &priv->wmm.tid_tbl_ptr[i].ra_list, list) { | ||
487 | list_del(&ra_list->list); | ||
488 | kfree(ra_list); | ||
489 | } | ||
490 | |||
491 | INIT_LIST_HEAD(&priv->wmm.tid_tbl_ptr[i].ra_list); | ||
492 | |||
493 | priv->wmm.tid_tbl_ptr[i].ra_list_curr = NULL; | ||
494 | } | ||
495 | } | ||
496 | |||
497 | /* | ||
498 | * This function cleans up the Tx and Rx queues. | ||
499 | * | ||
500 | * Cleanup includes - | ||
501 | * - All packets in RA lists | ||
502 | * - All entries in Rx reorder table | ||
503 | * - All entries in Tx BA stream table | ||
504 | * - MPA buffer (if required) | ||
505 | * - All RA lists | ||
506 | */ | ||
507 | void | ||
508 | mwifiex_clean_txrx(struct mwifiex_private *priv) | ||
509 | { | ||
510 | unsigned long flags; | ||
511 | |||
512 | mwifiex_11n_cleanup_reorder_tbl(priv); | ||
513 | spin_lock_irqsave(&priv->wmm.ra_list_spinlock, flags); | ||
514 | |||
515 | mwifiex_wmm_cleanup_queues(priv); | ||
516 | mwifiex_11n_delete_all_tx_ba_stream_tbl(priv); | ||
517 | |||
518 | if (priv->adapter->if_ops.cleanup_mpa_buf) | ||
519 | priv->adapter->if_ops.cleanup_mpa_buf(priv->adapter); | ||
520 | |||
521 | mwifiex_wmm_delete_all_ralist(priv); | ||
522 | memcpy(tos_to_tid, ac_to_tid, sizeof(tos_to_tid)); | ||
523 | |||
524 | spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, flags); | ||
525 | } | ||
526 | |||
527 | /* | ||
528 | * This function retrieves a particular RA list node, matching with the | ||
529 | * given TID and RA address. | ||
530 | */ | ||
531 | static struct mwifiex_ra_list_tbl * | ||
532 | mwifiex_wmm_get_ralist_node(struct mwifiex_private *priv, u8 tid, | ||
533 | u8 *ra_addr) | ||
534 | { | ||
535 | struct mwifiex_ra_list_tbl *ra_list; | ||
536 | |||
537 | list_for_each_entry(ra_list, &priv->wmm.tid_tbl_ptr[tid].ra_list, | ||
538 | list) { | ||
539 | if (!memcmp(ra_list->ra, ra_addr, ETH_ALEN)) | ||
540 | return ra_list; | ||
541 | } | ||
542 | |||
543 | return NULL; | ||
544 | } | ||
545 | |||
546 | /* | ||
547 | * This function retrieves an RA list node for a given TID and | ||
548 | * RA address pair. | ||
549 | * | ||
550 | * If no such node is found, a new node is added first and then | ||
551 | * retrieved. | ||
552 | */ | ||
553 | static struct mwifiex_ra_list_tbl * | ||
554 | mwifiex_wmm_get_queue_raptr(struct mwifiex_private *priv, u8 tid, u8 *ra_addr) | ||
555 | { | ||
556 | struct mwifiex_ra_list_tbl *ra_list; | ||
557 | |||
558 | ra_list = mwifiex_wmm_get_ralist_node(priv, tid, ra_addr); | ||
559 | if (ra_list) | ||
560 | return ra_list; | ||
561 | mwifiex_ralist_add(priv, ra_addr); | ||
562 | |||
563 | return mwifiex_wmm_get_ralist_node(priv, tid, ra_addr); | ||
564 | } | ||
565 | |||
566 | /* | ||
567 | * This function checks if a particular RA list node exists in a given TID | ||
568 | * table index. | ||
569 | */ | ||
570 | int | ||
571 | mwifiex_is_ralist_valid(struct mwifiex_private *priv, | ||
572 | struct mwifiex_ra_list_tbl *ra_list, int ptr_index) | ||
573 | { | ||
574 | struct mwifiex_ra_list_tbl *rlist; | ||
575 | |||
576 | list_for_each_entry(rlist, &priv->wmm.tid_tbl_ptr[ptr_index].ra_list, | ||
577 | list) { | ||
578 | if (rlist == ra_list) | ||
579 | return true; | ||
580 | } | ||
581 | |||
582 | return false; | ||
583 | } | ||
584 | |||
585 | /* | ||
586 | * This function adds a packet to WMM queue. | ||
587 | * | ||
588 | * In disconnected state the packet is immediately dropped and the | ||
589 | * packet send completion callback is called with status failure. | ||
590 | * | ||
591 | * Otherwise, the correct RA list node is located and the packet | ||
592 | * is queued at the list tail. | ||
593 | */ | ||
594 | void | ||
595 | mwifiex_wmm_add_buf_txqueue(struct mwifiex_adapter *adapter, | ||
596 | struct sk_buff *skb) | ||
597 | { | ||
598 | struct mwifiex_txinfo *tx_info = MWIFIEX_SKB_TXCB(skb); | ||
599 | struct mwifiex_private *priv = adapter->priv[tx_info->bss_index]; | ||
600 | u32 tid; | ||
601 | struct mwifiex_ra_list_tbl *ra_list; | ||
602 | u8 ra[ETH_ALEN], tid_down; | ||
603 | unsigned long flags; | ||
604 | |||
605 | if (!priv->media_connected) { | ||
606 | dev_dbg(adapter->dev, "data: drop packet in disconnect\n"); | ||
607 | mwifiex_write_data_complete(adapter, skb, -1); | ||
608 | return; | ||
609 | } | ||
610 | |||
611 | tid = skb->priority; | ||
612 | |||
613 | spin_lock_irqsave(&priv->wmm.ra_list_spinlock, flags); | ||
614 | |||
615 | tid_down = mwifiex_wmm_downgrade_tid(priv, tid); | ||
616 | |||
617 | /* In case of infra as we have already created the list during | ||
618 | association we just don't have to call get_queue_raptr, we will | ||
619 | have only 1 raptr for a tid in case of infra */ | ||
620 | if (!mwifiex_queuing_ra_based(priv)) { | ||
621 | if (!list_empty(&priv->wmm.tid_tbl_ptr[tid_down].ra_list)) | ||
622 | ra_list = list_first_entry( | ||
623 | &priv->wmm.tid_tbl_ptr[tid_down].ra_list, | ||
624 | struct mwifiex_ra_list_tbl, list); | ||
625 | else | ||
626 | ra_list = NULL; | ||
627 | } else { | ||
628 | memcpy(ra, skb->data, ETH_ALEN); | ||
629 | ra_list = mwifiex_wmm_get_queue_raptr(priv, tid_down, ra); | ||
630 | } | ||
631 | |||
632 | if (!ra_list) { | ||
633 | spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, flags); | ||
634 | mwifiex_write_data_complete(adapter, skb, -1); | ||
635 | return; | ||
636 | } | ||
637 | |||
638 | skb_queue_tail(&ra_list->skb_head, skb); | ||
639 | |||
640 | ra_list->total_pkts_size += skb->len; | ||
641 | |||
642 | spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, flags); | ||
643 | } | ||
644 | |||
645 | /* | ||
646 | * This function processes the get WMM status command response from firmware. | ||
647 | * | ||
648 | * The response may contain multiple TLVs - | ||
649 | * - AC Queue status TLVs | ||
650 | * - Current WMM Parameter IE TLV | ||
651 | * - Admission Control action frame TLVs | ||
652 | * | ||
653 | * This function parses the TLVs and then calls further specific functions | ||
654 | * to process any changes in the queue prioritize or state. | ||
655 | */ | ||
656 | int mwifiex_ret_wmm_get_status(struct mwifiex_private *priv, | ||
657 | const struct host_cmd_ds_command *resp) | ||
658 | { | ||
659 | u8 *curr = (u8 *) &resp->params.get_wmm_status; | ||
660 | uint16_t resp_len = le16_to_cpu(resp->size), tlv_len; | ||
661 | int valid = true; | ||
662 | |||
663 | struct mwifiex_ie_types_data *tlv_hdr; | ||
664 | struct mwifiex_ie_types_wmm_queue_status *tlv_wmm_qstatus; | ||
665 | struct ieee_types_wmm_parameter *wmm_param_ie = NULL; | ||
666 | struct mwifiex_wmm_ac_status *ac_status; | ||
667 | |||
668 | dev_dbg(priv->adapter->dev, "info: WMM: WMM_GET_STATUS cmdresp received: %d\n", | ||
669 | resp_len); | ||
670 | |||
671 | while ((resp_len >= sizeof(tlv_hdr->header)) && valid) { | ||
672 | tlv_hdr = (struct mwifiex_ie_types_data *) curr; | ||
673 | tlv_len = le16_to_cpu(tlv_hdr->header.len); | ||
674 | |||
675 | switch (le16_to_cpu(tlv_hdr->header.type)) { | ||
676 | case TLV_TYPE_WMMQSTATUS: | ||
677 | tlv_wmm_qstatus = | ||
678 | (struct mwifiex_ie_types_wmm_queue_status *) | ||
679 | tlv_hdr; | ||
680 | dev_dbg(priv->adapter->dev, | ||
681 | "info: CMD_RESP: WMM_GET_STATUS:" | ||
682 | " QSTATUS TLV: %d, %d, %d\n", | ||
683 | tlv_wmm_qstatus->queue_index, | ||
684 | tlv_wmm_qstatus->flow_required, | ||
685 | tlv_wmm_qstatus->disabled); | ||
686 | |||
687 | ac_status = &priv->wmm.ac_status[tlv_wmm_qstatus-> | ||
688 | queue_index]; | ||
689 | ac_status->disabled = tlv_wmm_qstatus->disabled; | ||
690 | ac_status->flow_required = | ||
691 | tlv_wmm_qstatus->flow_required; | ||
692 | ac_status->flow_created = tlv_wmm_qstatus->flow_created; | ||
693 | break; | ||
694 | |||
695 | case WLAN_EID_VENDOR_SPECIFIC: | ||
696 | /* | ||
697 | * Point the regular IEEE IE 2 bytes into the Marvell IE | ||
698 | * and setup the IEEE IE type and length byte fields | ||
699 | */ | ||
700 | |||
701 | wmm_param_ie = | ||
702 | (struct ieee_types_wmm_parameter *) (curr + | ||
703 | 2); | ||
704 | wmm_param_ie->vend_hdr.len = (u8) tlv_len; | ||
705 | wmm_param_ie->vend_hdr.element_id = | ||
706 | WLAN_EID_VENDOR_SPECIFIC; | ||
707 | |||
708 | dev_dbg(priv->adapter->dev, | ||
709 | "info: CMD_RESP: WMM_GET_STATUS:" | ||
710 | " WMM Parameter Set Count: %d\n", | ||
711 | wmm_param_ie->qos_info_bitmap & | ||
712 | IEEE80211_WMM_IE_AP_QOSINFO_PARAM_SET_CNT_MASK); | ||
713 | |||
714 | memcpy((u8 *) &priv->curr_bss_params.bss_descriptor. | ||
715 | wmm_ie, wmm_param_ie, | ||
716 | wmm_param_ie->vend_hdr.len + 2); | ||
717 | |||
718 | break; | ||
719 | |||
720 | default: | ||
721 | valid = false; | ||
722 | break; | ||
723 | } | ||
724 | |||
725 | curr += (tlv_len + sizeof(tlv_hdr->header)); | ||
726 | resp_len -= (tlv_len + sizeof(tlv_hdr->header)); | ||
727 | } | ||
728 | |||
729 | mwifiex_wmm_setup_queue_priorities(priv, wmm_param_ie); | ||
730 | mwifiex_wmm_setup_ac_downgrade(priv); | ||
731 | |||
732 | return 0; | ||
733 | } | ||
734 | |||
735 | /* | ||
736 | * Callback handler from the command module to allow insertion of a WMM TLV. | ||
737 | * | ||
738 | * If the BSS we are associating to supports WMM, this function adds the | ||
739 | * required WMM Information IE to the association request command buffer in | ||
740 | * the form of a Marvell extended IEEE IE. | ||
741 | */ | ||
742 | u32 | ||
743 | mwifiex_wmm_process_association_req(struct mwifiex_private *priv, | ||
744 | u8 **assoc_buf, | ||
745 | struct ieee_types_wmm_parameter *wmm_ie, | ||
746 | struct ieee80211_ht_cap *ht_cap) | ||
747 | { | ||
748 | struct mwifiex_ie_types_wmm_param_set *wmm_tlv; | ||
749 | u32 ret_len = 0; | ||
750 | |||
751 | /* Null checks */ | ||
752 | if (!assoc_buf) | ||
753 | return 0; | ||
754 | if (!(*assoc_buf)) | ||
755 | return 0; | ||
756 | |||
757 | if (!wmm_ie) | ||
758 | return 0; | ||
759 | |||
760 | dev_dbg(priv->adapter->dev, "info: WMM: process assoc req:" | ||
761 | "bss->wmmIe=0x%x\n", | ||
762 | wmm_ie->vend_hdr.element_id); | ||
763 | |||
764 | if ((priv->wmm_required | ||
765 | || (ht_cap && (priv->adapter->config_bands & BAND_GN | ||
766 | || priv->adapter->config_bands & BAND_AN)) | ||
767 | ) | ||
768 | && wmm_ie->vend_hdr.element_id == WLAN_EID_VENDOR_SPECIFIC) { | ||
769 | wmm_tlv = (struct mwifiex_ie_types_wmm_param_set *) *assoc_buf; | ||
770 | wmm_tlv->header.type = cpu_to_le16((u16) wmm_info_ie[0]); | ||
771 | wmm_tlv->header.len = cpu_to_le16((u16) wmm_info_ie[1]); | ||
772 | memcpy(wmm_tlv->wmm_ie, &wmm_info_ie[2], | ||
773 | le16_to_cpu(wmm_tlv->header.len)); | ||
774 | if (wmm_ie->qos_info_bitmap & IEEE80211_WMM_IE_AP_QOSINFO_UAPSD) | ||
775 | memcpy((u8 *) (wmm_tlv->wmm_ie | ||
776 | + le16_to_cpu(wmm_tlv->header.len) | ||
777 | - sizeof(priv->wmm_qosinfo)), | ||
778 | &priv->wmm_qosinfo, | ||
779 | sizeof(priv->wmm_qosinfo)); | ||
780 | |||
781 | ret_len = sizeof(wmm_tlv->header) | ||
782 | + le16_to_cpu(wmm_tlv->header.len); | ||
783 | |||
784 | *assoc_buf += ret_len; | ||
785 | } | ||
786 | |||
787 | return ret_len; | ||
788 | } | ||
789 | |||
790 | /* | ||
791 | * This function computes the time delay in the driver queues for a | ||
792 | * given packet. | ||
793 | * | ||
794 | * When the packet is received at the OS/Driver interface, the current | ||
795 | * time is set in the packet structure. The difference between the present | ||
796 | * time and that received time is computed in this function and limited | ||
797 | * based on pre-compiled limits in the driver. | ||
798 | */ | ||
799 | u8 | ||
800 | mwifiex_wmm_compute_drv_pkt_delay(struct mwifiex_private *priv, | ||
801 | const struct sk_buff *skb) | ||
802 | { | ||
803 | u8 ret_val = 0; | ||
804 | struct timeval out_tstamp, in_tstamp; | ||
805 | u32 queue_delay; | ||
806 | |||
807 | do_gettimeofday(&out_tstamp); | ||
808 | in_tstamp = ktime_to_timeval(skb->tstamp); | ||
809 | |||
810 | queue_delay = (out_tstamp.tv_sec - in_tstamp.tv_sec) * 1000; | ||
811 | queue_delay += (out_tstamp.tv_usec - in_tstamp.tv_usec) / 1000; | ||
812 | |||
813 | /* | ||
814 | * Queue delay is passed as a uint8 in units of 2ms (ms shifted | ||
815 | * by 1). Min value (other than 0) is therefore 2ms, max is 510ms. | ||
816 | * | ||
817 | * Pass max value if queue_delay is beyond the uint8 range | ||
818 | */ | ||
819 | ret_val = (u8) (min(queue_delay, priv->wmm.drv_pkt_delay_max) >> 1); | ||
820 | |||
821 | dev_dbg(priv->adapter->dev, "data: WMM: Pkt Delay: %d ms," | ||
822 | " %d ms sent to FW\n", queue_delay, ret_val); | ||
823 | |||
824 | return ret_val; | ||
825 | } | ||
826 | |||
827 | /* | ||
828 | * This function retrieves the highest priority RA list table pointer. | ||
829 | */ | ||
830 | static struct mwifiex_ra_list_tbl * | ||
831 | mwifiex_wmm_get_highest_priolist_ptr(struct mwifiex_adapter *adapter, | ||
832 | struct mwifiex_private **priv, int *tid) | ||
833 | { | ||
834 | struct mwifiex_private *priv_tmp; | ||
835 | struct mwifiex_ra_list_tbl *ptr, *head; | ||
836 | struct mwifiex_bss_prio_node *bssprio_node, *bssprio_head; | ||
837 | struct mwifiex_tid_tbl *tid_ptr; | ||
838 | int is_list_empty; | ||
839 | unsigned long flags; | ||
840 | int i, j; | ||
841 | |||
842 | for (j = adapter->priv_num - 1; j >= 0; --j) { | ||
843 | spin_lock_irqsave(&adapter->bss_prio_tbl[j].bss_prio_lock, | ||
844 | flags); | ||
845 | is_list_empty = list_empty(&adapter->bss_prio_tbl[j] | ||
846 | .bss_prio_head); | ||
847 | spin_unlock_irqrestore(&adapter->bss_prio_tbl[j].bss_prio_lock, | ||
848 | flags); | ||
849 | if (is_list_empty) | ||
850 | continue; | ||
851 | |||
852 | if (adapter->bss_prio_tbl[j].bss_prio_cur == | ||
853 | (struct mwifiex_bss_prio_node *) | ||
854 | &adapter->bss_prio_tbl[j].bss_prio_head) { | ||
855 | bssprio_node = | ||
856 | list_first_entry(&adapter->bss_prio_tbl[j] | ||
857 | .bss_prio_head, | ||
858 | struct mwifiex_bss_prio_node, | ||
859 | list); | ||
860 | bssprio_head = bssprio_node; | ||
861 | } else { | ||
862 | bssprio_node = adapter->bss_prio_tbl[j].bss_prio_cur; | ||
863 | bssprio_head = bssprio_node; | ||
864 | } | ||
865 | |||
866 | do { | ||
867 | priv_tmp = bssprio_node->priv; | ||
868 | |||
869 | for (i = HIGH_PRIO_TID; i >= LOW_PRIO_TID; --i) { | ||
870 | |||
871 | tid_ptr = &(priv_tmp)->wmm. | ||
872 | tid_tbl_ptr[tos_to_tid[i]]; | ||
873 | |||
874 | spin_lock_irqsave(&tid_ptr->tid_tbl_lock, | ||
875 | flags); | ||
876 | is_list_empty = | ||
877 | list_empty(&adapter->bss_prio_tbl[j] | ||
878 | .bss_prio_head); | ||
879 | spin_unlock_irqrestore(&tid_ptr->tid_tbl_lock, | ||
880 | flags); | ||
881 | if (is_list_empty) | ||
882 | continue; | ||
883 | |||
884 | /* | ||
885 | * Always choose the next ra we transmitted | ||
886 | * last time, this way we pick the ra's in | ||
887 | * round robin fashion. | ||
888 | */ | ||
889 | ptr = list_first_entry( | ||
890 | &tid_ptr->ra_list_curr->list, | ||
891 | struct mwifiex_ra_list_tbl, | ||
892 | list); | ||
893 | |||
894 | head = ptr; | ||
895 | if (ptr == (struct mwifiex_ra_list_tbl *) | ||
896 | &tid_ptr->ra_list) { | ||
897 | /* Get next ra */ | ||
898 | ptr = list_first_entry(&ptr->list, | ||
899 | struct mwifiex_ra_list_tbl, list); | ||
900 | head = ptr; | ||
901 | } | ||
902 | |||
903 | do { | ||
904 | is_list_empty = | ||
905 | skb_queue_empty(&ptr->skb_head); | ||
906 | if (!is_list_empty) { | ||
907 | *priv = priv_tmp; | ||
908 | *tid = tos_to_tid[i]; | ||
909 | return ptr; | ||
910 | } | ||
911 | /* Get next ra */ | ||
912 | ptr = list_first_entry(&ptr->list, | ||
913 | struct mwifiex_ra_list_tbl, | ||
914 | list); | ||
915 | if (ptr == | ||
916 | (struct mwifiex_ra_list_tbl *) | ||
917 | &tid_ptr->ra_list) | ||
918 | ptr = list_first_entry( | ||
919 | &ptr->list, | ||
920 | struct mwifiex_ra_list_tbl, | ||
921 | list); | ||
922 | } while (ptr != head); | ||
923 | } | ||
924 | |||
925 | /* Get next bss priority node */ | ||
926 | bssprio_node = list_first_entry(&bssprio_node->list, | ||
927 | struct mwifiex_bss_prio_node, | ||
928 | list); | ||
929 | |||
930 | if (bssprio_node == | ||
931 | (struct mwifiex_bss_prio_node *) | ||
932 | &adapter->bss_prio_tbl[j].bss_prio_head) | ||
933 | /* Get next bss priority node */ | ||
934 | bssprio_node = list_first_entry( | ||
935 | &bssprio_node->list, | ||
936 | struct mwifiex_bss_prio_node, | ||
937 | list); | ||
938 | } while (bssprio_node != bssprio_head); | ||
939 | } | ||
940 | return NULL; | ||
941 | } | ||
942 | |||
943 | /* | ||
944 | * This function gets the number of packets in the Tx queue of a | ||
945 | * particular RA list. | ||
946 | */ | ||
947 | static int | ||
948 | mwifiex_num_pkts_in_txq(struct mwifiex_private *priv, | ||
949 | struct mwifiex_ra_list_tbl *ptr, int max_buf_size) | ||
950 | { | ||
951 | int count = 0, total_size = 0; | ||
952 | struct sk_buff *skb, *tmp; | ||
953 | |||
954 | skb_queue_walk_safe(&ptr->skb_head, skb, tmp) { | ||
955 | total_size += skb->len; | ||
956 | if (total_size < max_buf_size) | ||
957 | ++count; | ||
958 | else | ||
959 | break; | ||
960 | } | ||
961 | |||
962 | return count; | ||
963 | } | ||
964 | |||
965 | /* | ||
966 | * This function sends a single packet to firmware for transmission. | ||
967 | */ | ||
968 | static void | ||
969 | mwifiex_send_single_packet(struct mwifiex_private *priv, | ||
970 | struct mwifiex_ra_list_tbl *ptr, int ptr_index, | ||
971 | unsigned long ra_list_flags) | ||
972 | __releases(&priv->wmm.ra_list_spinlock) | ||
973 | { | ||
974 | struct sk_buff *skb, *skb_next; | ||
975 | struct mwifiex_tx_param tx_param; | ||
976 | struct mwifiex_adapter *adapter = priv->adapter; | ||
977 | int status = 0; | ||
978 | struct mwifiex_txinfo *tx_info; | ||
979 | |||
980 | if (skb_queue_empty(&ptr->skb_head)) { | ||
981 | spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, | ||
982 | ra_list_flags); | ||
983 | dev_dbg(adapter->dev, "data: nothing to send\n"); | ||
984 | return; | ||
985 | } | ||
986 | |||
987 | skb = skb_dequeue(&ptr->skb_head); | ||
988 | |||
989 | tx_info = MWIFIEX_SKB_TXCB(skb); | ||
990 | dev_dbg(adapter->dev, "data: dequeuing the packet %p %p\n", ptr, skb); | ||
991 | |||
992 | ptr->total_pkts_size -= skb->len; | ||
993 | |||
994 | if (!skb_queue_empty(&ptr->skb_head)) | ||
995 | skb_next = skb_peek(&ptr->skb_head); | ||
996 | else | ||
997 | skb_next = NULL; | ||
998 | |||
999 | spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, ra_list_flags); | ||
1000 | |||
1001 | tx_param.next_pkt_len = ((skb_next) ? skb_next->len + | ||
1002 | sizeof(struct txpd) : 0); | ||
1003 | |||
1004 | status = mwifiex_process_tx(priv, skb, &tx_param); | ||
1005 | |||
1006 | if (status == -EBUSY) { | ||
1007 | /* Queue the packet back at the head */ | ||
1008 | spin_lock_irqsave(&priv->wmm.ra_list_spinlock, ra_list_flags); | ||
1009 | |||
1010 | if (!mwifiex_is_ralist_valid(priv, ptr, ptr_index)) { | ||
1011 | spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, | ||
1012 | ra_list_flags); | ||
1013 | mwifiex_write_data_complete(adapter, skb, -1); | ||
1014 | return; | ||
1015 | } | ||
1016 | |||
1017 | skb_queue_tail(&ptr->skb_head, skb); | ||
1018 | |||
1019 | ptr->total_pkts_size += skb->len; | ||
1020 | tx_info->flags |= MWIFIEX_BUF_FLAG_REQUEUED_PKT; | ||
1021 | spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, | ||
1022 | ra_list_flags); | ||
1023 | } else { | ||
1024 | spin_lock_irqsave(&priv->wmm.ra_list_spinlock, ra_list_flags); | ||
1025 | if (mwifiex_is_ralist_valid(priv, ptr, ptr_index)) { | ||
1026 | priv->wmm.packets_out[ptr_index]++; | ||
1027 | priv->wmm.tid_tbl_ptr[ptr_index].ra_list_curr = ptr; | ||
1028 | } | ||
1029 | adapter->bss_prio_tbl[priv->bss_priority].bss_prio_cur = | ||
1030 | list_first_entry( | ||
1031 | &adapter->bss_prio_tbl[priv->bss_priority] | ||
1032 | .bss_prio_cur->list, | ||
1033 | struct mwifiex_bss_prio_node, | ||
1034 | list); | ||
1035 | spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, | ||
1036 | ra_list_flags); | ||
1037 | } | ||
1038 | } | ||
1039 | |||
1040 | /* | ||
1041 | * This function checks if the first packet in the given RA list | ||
1042 | * is already processed or not. | ||
1043 | */ | ||
1044 | static int | ||
1045 | mwifiex_is_ptr_processed(struct mwifiex_private *priv, | ||
1046 | struct mwifiex_ra_list_tbl *ptr) | ||
1047 | { | ||
1048 | struct sk_buff *skb; | ||
1049 | struct mwifiex_txinfo *tx_info; | ||
1050 | |||
1051 | if (skb_queue_empty(&ptr->skb_head)) | ||
1052 | return false; | ||
1053 | |||
1054 | skb = skb_peek(&ptr->skb_head); | ||
1055 | |||
1056 | tx_info = MWIFIEX_SKB_TXCB(skb); | ||
1057 | if (tx_info->flags & MWIFIEX_BUF_FLAG_REQUEUED_PKT) | ||
1058 | return true; | ||
1059 | |||
1060 | return false; | ||
1061 | } | ||
1062 | |||
1063 | /* | ||
1064 | * This function sends a single processed packet to firmware for | ||
1065 | * transmission. | ||
1066 | */ | ||
1067 | static void | ||
1068 | mwifiex_send_processed_packet(struct mwifiex_private *priv, | ||
1069 | struct mwifiex_ra_list_tbl *ptr, int ptr_index, | ||
1070 | unsigned long ra_list_flags) | ||
1071 | __releases(&priv->wmm.ra_list_spinlock) | ||
1072 | { | ||
1073 | struct mwifiex_tx_param tx_param; | ||
1074 | struct mwifiex_adapter *adapter = priv->adapter; | ||
1075 | int ret = -1; | ||
1076 | struct sk_buff *skb, *skb_next; | ||
1077 | struct mwifiex_txinfo *tx_info; | ||
1078 | |||
1079 | if (skb_queue_empty(&ptr->skb_head)) { | ||
1080 | spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, | ||
1081 | ra_list_flags); | ||
1082 | return; | ||
1083 | } | ||
1084 | |||
1085 | skb = skb_dequeue(&ptr->skb_head); | ||
1086 | |||
1087 | if (!skb_queue_empty(&ptr->skb_head)) | ||
1088 | skb_next = skb_peek(&ptr->skb_head); | ||
1089 | else | ||
1090 | skb_next = NULL; | ||
1091 | |||
1092 | tx_info = MWIFIEX_SKB_TXCB(skb); | ||
1093 | |||
1094 | spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, ra_list_flags); | ||
1095 | tx_param.next_pkt_len = | ||
1096 | ((skb_next) ? skb_next->len + | ||
1097 | sizeof(struct txpd) : 0); | ||
1098 | ret = adapter->if_ops.host_to_card(adapter, MWIFIEX_TYPE_DATA, | ||
1099 | skb->data, skb->len, &tx_param); | ||
1100 | switch (ret) { | ||
1101 | case -EBUSY: | ||
1102 | dev_dbg(adapter->dev, "data: -EBUSY is returned\n"); | ||
1103 | spin_lock_irqsave(&priv->wmm.ra_list_spinlock, ra_list_flags); | ||
1104 | |||
1105 | if (!mwifiex_is_ralist_valid(priv, ptr, ptr_index)) { | ||
1106 | spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, | ||
1107 | ra_list_flags); | ||
1108 | mwifiex_write_data_complete(adapter, skb, -1); | ||
1109 | return; | ||
1110 | } | ||
1111 | |||
1112 | skb_queue_tail(&ptr->skb_head, skb); | ||
1113 | |||
1114 | tx_info->flags |= MWIFIEX_BUF_FLAG_REQUEUED_PKT; | ||
1115 | spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, | ||
1116 | ra_list_flags); | ||
1117 | break; | ||
1118 | case -1: | ||
1119 | adapter->data_sent = false; | ||
1120 | dev_err(adapter->dev, "host_to_card failed: %#x\n", ret); | ||
1121 | adapter->dbg.num_tx_host_to_card_failure++; | ||
1122 | mwifiex_write_data_complete(adapter, skb, ret); | ||
1123 | break; | ||
1124 | case -EINPROGRESS: | ||
1125 | adapter->data_sent = false; | ||
1126 | default: | ||
1127 | break; | ||
1128 | } | ||
1129 | if (ret != -EBUSY) { | ||
1130 | spin_lock_irqsave(&priv->wmm.ra_list_spinlock, ra_list_flags); | ||
1131 | if (mwifiex_is_ralist_valid(priv, ptr, ptr_index)) { | ||
1132 | priv->wmm.packets_out[ptr_index]++; | ||
1133 | priv->wmm.tid_tbl_ptr[ptr_index].ra_list_curr = ptr; | ||
1134 | } | ||
1135 | adapter->bss_prio_tbl[priv->bss_priority].bss_prio_cur = | ||
1136 | list_first_entry( | ||
1137 | &adapter->bss_prio_tbl[priv->bss_priority] | ||
1138 | .bss_prio_cur->list, | ||
1139 | struct mwifiex_bss_prio_node, | ||
1140 | list); | ||
1141 | spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, | ||
1142 | ra_list_flags); | ||
1143 | } | ||
1144 | } | ||
1145 | |||
1146 | /* | ||
1147 | * This function dequeues a packet from the highest priority list | ||
1148 | * and transmits it. | ||
1149 | */ | ||
1150 | static int | ||
1151 | mwifiex_dequeue_tx_packet(struct mwifiex_adapter *adapter) | ||
1152 | { | ||
1153 | struct mwifiex_ra_list_tbl *ptr; | ||
1154 | struct mwifiex_private *priv = NULL; | ||
1155 | int ptr_index = 0; | ||
1156 | u8 ra[ETH_ALEN]; | ||
1157 | int tid_del = 0, tid = 0; | ||
1158 | unsigned long flags; | ||
1159 | |||
1160 | ptr = mwifiex_wmm_get_highest_priolist_ptr(adapter, &priv, &ptr_index); | ||
1161 | if (!ptr) | ||
1162 | return -1; | ||
1163 | |||
1164 | tid = mwifiex_get_tid(priv->adapter, ptr); | ||
1165 | |||
1166 | dev_dbg(adapter->dev, "data: tid=%d\n", tid); | ||
1167 | |||
1168 | spin_lock_irqsave(&priv->wmm.ra_list_spinlock, flags); | ||
1169 | if (!mwifiex_is_ralist_valid(priv, ptr, ptr_index)) { | ||
1170 | spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, flags); | ||
1171 | return -1; | ||
1172 | } | ||
1173 | |||
1174 | if (mwifiex_is_ptr_processed(priv, ptr)) { | ||
1175 | mwifiex_send_processed_packet(priv, ptr, ptr_index, flags); | ||
1176 | /* ra_list_spinlock has been freed in | ||
1177 | mwifiex_send_processed_packet() */ | ||
1178 | return 0; | ||
1179 | } | ||
1180 | |||
1181 | if (!ptr->is_11n_enabled || mwifiex_is_ba_stream_setup(priv, ptr, tid) | ||
1182 | || ((priv->sec_info.wpa_enabled | ||
1183 | || priv->sec_info.wpa2_enabled) && !priv->wpa_is_gtk_set) | ||
1184 | ) { | ||
1185 | mwifiex_send_single_packet(priv, ptr, ptr_index, flags); | ||
1186 | /* ra_list_spinlock has been freed in | ||
1187 | mwifiex_send_single_packet() */ | ||
1188 | } else { | ||
1189 | if (mwifiex_is_ampdu_allowed(priv, ptr, tid)) { | ||
1190 | if (mwifiex_is_ba_stream_avail(priv)) { | ||
1191 | mwifiex_11n_create_tx_ba_stream_tbl(priv, | ||
1192 | ptr->ra, tid, | ||
1193 | BA_STREAM_SETUP_INPROGRESS); | ||
1194 | mwifiex_send_addba(priv, tid, ptr->ra); | ||
1195 | } else if (mwifiex_find_stream_to_delete | ||
1196 | (priv, ptr, tid, &tid_del, ra)) { | ||
1197 | mwifiex_11n_create_tx_ba_stream_tbl(priv, | ||
1198 | ptr->ra, tid, | ||
1199 | BA_STREAM_SETUP_INPROGRESS); | ||
1200 | mwifiex_send_delba(priv, tid_del, ra, 1); | ||
1201 | } | ||
1202 | } | ||
1203 | /* Minimum number of AMSDU */ | ||
1204 | #define MIN_NUM_AMSDU 2 | ||
1205 | if (mwifiex_is_amsdu_allowed(priv, ptr, tid) && | ||
1206 | (mwifiex_num_pkts_in_txq(priv, ptr, adapter->tx_buf_size) >= | ||
1207 | MIN_NUM_AMSDU)) | ||
1208 | mwifiex_11n_aggregate_pkt(priv, ptr, INTF_HEADER_LEN, | ||
1209 | ptr_index, flags); | ||
1210 | /* ra_list_spinlock has been freed in | ||
1211 | mwifiex_11n_aggregate_pkt() */ | ||
1212 | else | ||
1213 | mwifiex_send_single_packet(priv, ptr, ptr_index, flags); | ||
1214 | /* ra_list_spinlock has been freed in | ||
1215 | mwifiex_send_single_packet() */ | ||
1216 | } | ||
1217 | return 0; | ||
1218 | } | ||
1219 | |||
1220 | /* | ||
1221 | * This function transmits the highest priority packet awaiting in the | ||
1222 | * WMM Queues. | ||
1223 | */ | ||
1224 | void | ||
1225 | mwifiex_wmm_process_tx(struct mwifiex_adapter *adapter) | ||
1226 | { | ||
1227 | do { | ||
1228 | /* Check if busy */ | ||
1229 | if (adapter->data_sent || adapter->tx_lock_flag) | ||
1230 | break; | ||
1231 | |||
1232 | if (mwifiex_dequeue_tx_packet(adapter)) | ||
1233 | break; | ||
1234 | } while (true); | ||
1235 | |||
1236 | return; | ||
1237 | } | ||
diff --git a/drivers/net/wireless/mwifiex/wmm.h b/drivers/net/wireless/mwifiex/wmm.h new file mode 100644 index 000000000000..241f1b0b77f9 --- /dev/null +++ b/drivers/net/wireless/mwifiex/wmm.h | |||
@@ -0,0 +1,112 @@ | |||
1 | /* | ||
2 | * Marvell Wireless LAN device driver: WMM | ||
3 | * | ||
4 | * Copyright (C) 2011, Marvell International Ltd. | ||
5 | * | ||
6 | * This software file (the "File") is distributed by Marvell International | ||
7 | * Ltd. under the terms of the GNU General Public License Version 2, June 1991 | ||
8 | * (the "License"). You may use, redistribute and/or modify this File in | ||
9 | * accordance with the terms and conditions of the License, a copy of which | ||
10 | * is available by writing to the Free Software Foundation, Inc., | ||
11 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the | ||
12 | * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. | ||
13 | * | ||
14 | * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE | ||
15 | * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE | ||
16 | * ARE EXPRESSLY DISCLAIMED. The License provides additional details about | ||
17 | * this warranty disclaimer. | ||
18 | */ | ||
19 | |||
20 | #ifndef _MWIFIEX_WMM_H_ | ||
21 | #define _MWIFIEX_WMM_H_ | ||
22 | |||
23 | enum ieee_types_wmm_aciaifsn_bitmasks { | ||
24 | MWIFIEX_AIFSN = (BIT(0) | BIT(1) | BIT(2) | BIT(3)), | ||
25 | MWIFIEX_ACM = BIT(4), | ||
26 | MWIFIEX_ACI = (BIT(5) | BIT(6)), | ||
27 | }; | ||
28 | |||
29 | enum ieee_types_wmm_ecw_bitmasks { | ||
30 | MWIFIEX_ECW_MIN = (BIT(0) | BIT(1) | BIT(2) | BIT(3)), | ||
31 | MWIFIEX_ECW_MAX = (BIT(4) | BIT(5) | BIT(6) | BIT(7)), | ||
32 | }; | ||
33 | |||
34 | /* | ||
35 | * This function retrieves the TID of the given RA list. | ||
36 | */ | ||
37 | static inline int | ||
38 | mwifiex_get_tid(struct mwifiex_adapter *adapter, | ||
39 | struct mwifiex_ra_list_tbl *ptr) | ||
40 | { | ||
41 | struct sk_buff *skb; | ||
42 | |||
43 | if (skb_queue_empty(&ptr->skb_head)) | ||
44 | return 0; | ||
45 | |||
46 | skb = skb_peek(&ptr->skb_head); | ||
47 | |||
48 | return skb->priority; | ||
49 | } | ||
50 | |||
51 | /* | ||
52 | * This function gets the length of a list. | ||
53 | */ | ||
54 | static inline int | ||
55 | mwifiex_wmm_list_len(struct mwifiex_adapter *adapter, struct list_head *head) | ||
56 | { | ||
57 | struct list_head *pos; | ||
58 | int count = 0; | ||
59 | |||
60 | list_for_each(pos, head) | ||
61 | ++count; | ||
62 | |||
63 | return count; | ||
64 | } | ||
65 | |||
66 | /* | ||
67 | * This function checks if a RA list is empty or not. | ||
68 | */ | ||
69 | static inline u8 | ||
70 | mwifiex_wmm_is_ra_list_empty(struct mwifiex_adapter *adapter, | ||
71 | struct list_head *ra_list_hhead) | ||
72 | { | ||
73 | struct mwifiex_ra_list_tbl *ra_list; | ||
74 | int is_list_empty; | ||
75 | |||
76 | list_for_each_entry(ra_list, ra_list_hhead, list) { | ||
77 | is_list_empty = skb_queue_empty(&ra_list->skb_head); | ||
78 | if (!is_list_empty) | ||
79 | return false; | ||
80 | } | ||
81 | |||
82 | return true; | ||
83 | } | ||
84 | |||
85 | void mwifiex_wmm_add_buf_txqueue(struct mwifiex_adapter *adapter, | ||
86 | struct sk_buff *skb); | ||
87 | void mwifiex_ralist_add(struct mwifiex_private *priv, u8 *ra); | ||
88 | |||
89 | int mwifiex_wmm_lists_empty(struct mwifiex_adapter *adapter); | ||
90 | void mwifiex_wmm_process_tx(struct mwifiex_adapter *adapter); | ||
91 | int mwifiex_is_ralist_valid(struct mwifiex_private *priv, | ||
92 | struct mwifiex_ra_list_tbl *ra_list, int tid); | ||
93 | |||
94 | u8 mwifiex_wmm_compute_drv_pkt_delay(struct mwifiex_private *priv, | ||
95 | const struct sk_buff *skb); | ||
96 | void mwifiex_wmm_init(struct mwifiex_adapter *adapter); | ||
97 | |||
98 | extern u32 mwifiex_wmm_process_association_req(struct mwifiex_private *priv, | ||
99 | u8 **assoc_buf, | ||
100 | struct ieee_types_wmm_parameter | ||
101 | *wmmie, | ||
102 | struct ieee80211_ht_cap | ||
103 | *htcap); | ||
104 | |||
105 | void mwifiex_wmm_setup_queue_priorities(struct mwifiex_private *priv, | ||
106 | struct ieee_types_wmm_parameter | ||
107 | *wmm_ie); | ||
108 | void mwifiex_wmm_setup_ac_downgrade(struct mwifiex_private *priv); | ||
109 | extern int mwifiex_ret_wmm_get_status(struct mwifiex_private *priv, | ||
110 | const struct host_cmd_ds_command *resp); | ||
111 | |||
112 | #endif /* !_MWIFIEX_WMM_H_ */ | ||