diff options
Diffstat (limited to 'drivers/net/wireless/mwifiex')
35 files changed, 23441 insertions, 0 deletions
diff --git a/drivers/net/wireless/mwifiex/11n.c b/drivers/net/wireless/mwifiex/11n.c new file mode 100644 index 000000000000..916183d39009 --- /dev/null +++ b/drivers/net/wireless/mwifiex/11n.c | |||
@@ -0,0 +1,744 @@ | |||
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 | * HT capability information field, AMPDU Parameters field, supported MCS set | ||
33 | * fields are retrieved from cfg80211 stack | ||
34 | * | ||
35 | * RD responder bit to set to clear in the extended capability header. | ||
36 | */ | ||
37 | void | ||
38 | mwifiex_fill_cap_info(struct mwifiex_private *priv, u8 radio_type, | ||
39 | struct mwifiex_ie_types_htcap *ht_cap) | ||
40 | { | ||
41 | uint16_t ht_ext_cap = le16_to_cpu(ht_cap->ht_cap.extended_ht_cap_info); | ||
42 | struct ieee80211_supported_band *sband = | ||
43 | priv->wdev->wiphy->bands[radio_type]; | ||
44 | |||
45 | ht_cap->ht_cap.ampdu_params_info = | ||
46 | (sband->ht_cap.ampdu_factor & | ||
47 | IEEE80211_HT_AMPDU_PARM_FACTOR)| | ||
48 | ((sband->ht_cap.ampdu_density << | ||
49 | IEEE80211_HT_AMPDU_PARM_DENSITY_SHIFT) & | ||
50 | IEEE80211_HT_AMPDU_PARM_DENSITY); | ||
51 | |||
52 | memcpy((u8 *) &ht_cap->ht_cap.mcs, &sband->ht_cap.mcs, | ||
53 | sizeof(sband->ht_cap.mcs)); | ||
54 | |||
55 | if (priv->bss_mode == NL80211_IFTYPE_STATION || | ||
56 | (sband->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40)) | ||
57 | /* Set MCS32 for infra mode or ad-hoc mode with 40MHz support */ | ||
58 | SETHT_MCS32(ht_cap->ht_cap.mcs.rx_mask); | ||
59 | |||
60 | /* Clear RD responder bit */ | ||
61 | ht_ext_cap &= ~IEEE80211_HT_EXT_CAP_RD_RESPONDER; | ||
62 | |||
63 | ht_cap->ht_cap.cap_info = cpu_to_le16(sband->ht_cap.cap); | ||
64 | ht_cap->ht_cap.extended_ht_cap_info = cpu_to_le16(ht_ext_cap); | ||
65 | } | ||
66 | |||
67 | /* | ||
68 | * This function returns the pointer to an entry in BA Stream | ||
69 | * table which matches the requested BA status. | ||
70 | */ | ||
71 | static struct mwifiex_tx_ba_stream_tbl * | ||
72 | mwifiex_11n_get_tx_ba_stream_status(struct mwifiex_private *priv, | ||
73 | enum mwifiex_ba_status ba_status) | ||
74 | { | ||
75 | struct mwifiex_tx_ba_stream_tbl *tx_ba_tsr_tbl; | ||
76 | unsigned long flags; | ||
77 | |||
78 | spin_lock_irqsave(&priv->tx_ba_stream_tbl_lock, flags); | ||
79 | list_for_each_entry(tx_ba_tsr_tbl, &priv->tx_ba_stream_tbl_ptr, list) { | ||
80 | if (tx_ba_tsr_tbl->ba_status == ba_status) { | ||
81 | spin_unlock_irqrestore(&priv->tx_ba_stream_tbl_lock, | ||
82 | flags); | ||
83 | return tx_ba_tsr_tbl; | ||
84 | } | ||
85 | } | ||
86 | spin_unlock_irqrestore(&priv->tx_ba_stream_tbl_lock, flags); | ||
87 | return NULL; | ||
88 | } | ||
89 | |||
90 | /* | ||
91 | * This function handles the command response of delete a block | ||
92 | * ack request. | ||
93 | * | ||
94 | * The function checks the response success status and takes action | ||
95 | * accordingly (send an add BA request in case of success, or recreate | ||
96 | * the deleted stream in case of failure, if the add BA was also | ||
97 | * initiated by us). | ||
98 | */ | ||
99 | int mwifiex_ret_11n_delba(struct mwifiex_private *priv, | ||
100 | struct host_cmd_ds_command *resp) | ||
101 | { | ||
102 | int tid; | ||
103 | struct mwifiex_tx_ba_stream_tbl *tx_ba_tbl; | ||
104 | struct host_cmd_ds_11n_delba *del_ba = | ||
105 | (struct host_cmd_ds_11n_delba *) &resp->params.del_ba; | ||
106 | uint16_t del_ba_param_set = le16_to_cpu(del_ba->del_ba_param_set); | ||
107 | |||
108 | tid = del_ba_param_set >> DELBA_TID_POS; | ||
109 | if (del_ba->del_result == BA_RESULT_SUCCESS) { | ||
110 | mwifiex_11n_delete_ba_stream_tbl(priv, tid, | ||
111 | del_ba->peer_mac_addr, TYPE_DELBA_SENT, | ||
112 | INITIATOR_BIT(del_ba_param_set)); | ||
113 | |||
114 | tx_ba_tbl = mwifiex_11n_get_tx_ba_stream_status(priv, | ||
115 | BA_STREAM_SETUP_INPROGRESS); | ||
116 | if (tx_ba_tbl) | ||
117 | mwifiex_send_addba(priv, tx_ba_tbl->tid, | ||
118 | tx_ba_tbl->ra); | ||
119 | } else { /* | ||
120 | * In case of failure, recreate the deleted stream in case | ||
121 | * we initiated the ADDBA | ||
122 | */ | ||
123 | if (INITIATOR_BIT(del_ba_param_set)) { | ||
124 | mwifiex_11n_create_tx_ba_stream_tbl(priv, | ||
125 | del_ba->peer_mac_addr, tid, | ||
126 | BA_STREAM_SETUP_INPROGRESS); | ||
127 | |||
128 | tx_ba_tbl = mwifiex_11n_get_tx_ba_stream_status(priv, | ||
129 | BA_STREAM_SETUP_INPROGRESS); | ||
130 | if (tx_ba_tbl) | ||
131 | mwifiex_11n_delete_ba_stream_tbl(priv, | ||
132 | tx_ba_tbl->tid, tx_ba_tbl->ra, | ||
133 | TYPE_DELBA_SENT, true); | ||
134 | } | ||
135 | } | ||
136 | |||
137 | return 0; | ||
138 | } | ||
139 | |||
140 | /* | ||
141 | * This function handles the command response of add a block | ||
142 | * ack request. | ||
143 | * | ||
144 | * Handling includes changing the header fields to CPU formats, checking | ||
145 | * the response success status and taking actions accordingly (delete the | ||
146 | * BA stream table in case of failure). | ||
147 | */ | ||
148 | int mwifiex_ret_11n_addba_req(struct mwifiex_private *priv, | ||
149 | struct host_cmd_ds_command *resp) | ||
150 | { | ||
151 | int tid; | ||
152 | struct host_cmd_ds_11n_addba_rsp *add_ba_rsp = | ||
153 | (struct host_cmd_ds_11n_addba_rsp *) &resp->params.add_ba_rsp; | ||
154 | struct mwifiex_tx_ba_stream_tbl *tx_ba_tbl; | ||
155 | |||
156 | add_ba_rsp->ssn = cpu_to_le16((le16_to_cpu(add_ba_rsp->ssn)) | ||
157 | & SSN_MASK); | ||
158 | |||
159 | tid = (le16_to_cpu(add_ba_rsp->block_ack_param_set) | ||
160 | & IEEE80211_ADDBA_PARAM_TID_MASK) | ||
161 | >> BLOCKACKPARAM_TID_POS; | ||
162 | if (le16_to_cpu(add_ba_rsp->status_code) == BA_RESULT_SUCCESS) { | ||
163 | tx_ba_tbl = mwifiex_11n_get_tx_ba_stream_tbl(priv, tid, | ||
164 | add_ba_rsp->peer_mac_addr); | ||
165 | if (tx_ba_tbl) { | ||
166 | dev_dbg(priv->adapter->dev, "info: BA stream complete\n"); | ||
167 | tx_ba_tbl->ba_status = BA_STREAM_SETUP_COMPLETE; | ||
168 | } else { | ||
169 | dev_err(priv->adapter->dev, "BA stream not created\n"); | ||
170 | } | ||
171 | } else { | ||
172 | mwifiex_11n_delete_ba_stream_tbl(priv, tid, | ||
173 | add_ba_rsp->peer_mac_addr, | ||
174 | TYPE_DELBA_SENT, true); | ||
175 | if (add_ba_rsp->add_rsp_result != BA_RESULT_TIMEOUT) | ||
176 | priv->aggr_prio_tbl[tid].ampdu_ap = | ||
177 | BA_STREAM_NOT_ALLOWED; | ||
178 | } | ||
179 | |||
180 | return 0; | ||
181 | } | ||
182 | |||
183 | /* | ||
184 | * This function handles the command response of 11n configuration request. | ||
185 | * | ||
186 | * Handling includes changing the header fields into CPU format. | ||
187 | */ | ||
188 | int mwifiex_ret_11n_cfg(struct host_cmd_ds_command *resp, void *data_buf) | ||
189 | { | ||
190 | struct mwifiex_ds_11n_tx_cfg *tx_cfg; | ||
191 | struct host_cmd_ds_11n_cfg *htcfg = &resp->params.htcfg; | ||
192 | |||
193 | if (data_buf) { | ||
194 | tx_cfg = (struct mwifiex_ds_11n_tx_cfg *) data_buf; | ||
195 | tx_cfg->tx_htcap = le16_to_cpu(htcfg->ht_tx_cap); | ||
196 | tx_cfg->tx_htinfo = le16_to_cpu(htcfg->ht_tx_info); | ||
197 | } | ||
198 | return 0; | ||
199 | } | ||
200 | |||
201 | /* | ||
202 | * This function prepares command of reconfigure Tx buffer. | ||
203 | * | ||
204 | * Preparation includes - | ||
205 | * - Setting command ID, action and proper size | ||
206 | * - Setting Tx buffer size (for SET only) | ||
207 | * - Ensuring correct endian-ness | ||
208 | */ | ||
209 | int mwifiex_cmd_recfg_tx_buf(struct mwifiex_private *priv, | ||
210 | struct host_cmd_ds_command *cmd, int cmd_action, | ||
211 | void *data_buf) | ||
212 | { | ||
213 | struct host_cmd_ds_txbuf_cfg *tx_buf = &cmd->params.tx_buf; | ||
214 | u16 action = (u16) cmd_action; | ||
215 | u16 buf_size = *((u16 *) data_buf); | ||
216 | |||
217 | cmd->command = cpu_to_le16(HostCmd_CMD_RECONFIGURE_TX_BUFF); | ||
218 | cmd->size = | ||
219 | cpu_to_le16(sizeof(struct host_cmd_ds_txbuf_cfg) + S_DS_GEN); | ||
220 | tx_buf->action = cpu_to_le16(action); | ||
221 | switch (action) { | ||
222 | case HostCmd_ACT_GEN_SET: | ||
223 | dev_dbg(priv->adapter->dev, "cmd: set tx_buf=%d\n", buf_size); | ||
224 | tx_buf->buff_size = cpu_to_le16(buf_size); | ||
225 | break; | ||
226 | case HostCmd_ACT_GEN_GET: | ||
227 | default: | ||
228 | tx_buf->buff_size = 0; | ||
229 | break; | ||
230 | } | ||
231 | return 0; | ||
232 | } | ||
233 | |||
234 | /* | ||
235 | * This function prepares command of AMSDU aggregation control. | ||
236 | * | ||
237 | * Preparation includes - | ||
238 | * - Setting command ID, action and proper size | ||
239 | * - Setting AMSDU control parameters (for SET only) | ||
240 | * - Ensuring correct endian-ness | ||
241 | */ | ||
242 | int mwifiex_cmd_amsdu_aggr_ctrl(struct host_cmd_ds_command *cmd, | ||
243 | int cmd_action, void *data_buf) | ||
244 | { | ||
245 | struct host_cmd_ds_amsdu_aggr_ctrl *amsdu_ctrl = | ||
246 | &cmd->params.amsdu_aggr_ctrl; | ||
247 | u16 action = (u16) cmd_action; | ||
248 | struct mwifiex_ds_11n_amsdu_aggr_ctrl *aa_ctrl = | ||
249 | (struct mwifiex_ds_11n_amsdu_aggr_ctrl *) data_buf; | ||
250 | |||
251 | cmd->command = cpu_to_le16(HostCmd_CMD_AMSDU_AGGR_CTRL); | ||
252 | cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_amsdu_aggr_ctrl) | ||
253 | + S_DS_GEN); | ||
254 | amsdu_ctrl->action = cpu_to_le16(action); | ||
255 | switch (action) { | ||
256 | case HostCmd_ACT_GEN_SET: | ||
257 | amsdu_ctrl->enable = cpu_to_le16(aa_ctrl->enable); | ||
258 | amsdu_ctrl->curr_buf_size = 0; | ||
259 | break; | ||
260 | case HostCmd_ACT_GEN_GET: | ||
261 | default: | ||
262 | amsdu_ctrl->curr_buf_size = 0; | ||
263 | break; | ||
264 | } | ||
265 | return 0; | ||
266 | } | ||
267 | |||
268 | /* | ||
269 | * This function handles the command response of AMSDU aggregation | ||
270 | * control request. | ||
271 | * | ||
272 | * Handling includes changing the header fields into CPU format. | ||
273 | */ | ||
274 | int mwifiex_ret_amsdu_aggr_ctrl(struct host_cmd_ds_command *resp, | ||
275 | void *data_buf) | ||
276 | { | ||
277 | struct mwifiex_ds_11n_amsdu_aggr_ctrl *amsdu_aggr_ctrl; | ||
278 | struct host_cmd_ds_amsdu_aggr_ctrl *amsdu_ctrl = | ||
279 | &resp->params.amsdu_aggr_ctrl; | ||
280 | |||
281 | if (data_buf) { | ||
282 | amsdu_aggr_ctrl = | ||
283 | (struct mwifiex_ds_11n_amsdu_aggr_ctrl *) data_buf; | ||
284 | amsdu_aggr_ctrl->enable = le16_to_cpu(amsdu_ctrl->enable); | ||
285 | amsdu_aggr_ctrl->curr_buf_size = | ||
286 | le16_to_cpu(amsdu_ctrl->curr_buf_size); | ||
287 | } | ||
288 | return 0; | ||
289 | } | ||
290 | |||
291 | /* | ||
292 | * This function prepares 11n configuration command. | ||
293 | * | ||
294 | * Preparation includes - | ||
295 | * - Setting command ID, action and proper size | ||
296 | * - Setting HT Tx capability and HT Tx information fields | ||
297 | * - Ensuring correct endian-ness | ||
298 | */ | ||
299 | int mwifiex_cmd_11n_cfg(struct host_cmd_ds_command *cmd, | ||
300 | u16 cmd_action, void *data_buf) | ||
301 | { | ||
302 | struct host_cmd_ds_11n_cfg *htcfg = &cmd->params.htcfg; | ||
303 | struct mwifiex_ds_11n_tx_cfg *txcfg = | ||
304 | (struct mwifiex_ds_11n_tx_cfg *) data_buf; | ||
305 | |||
306 | cmd->command = cpu_to_le16(HostCmd_CMD_11N_CFG); | ||
307 | cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_11n_cfg) + S_DS_GEN); | ||
308 | htcfg->action = cpu_to_le16(cmd_action); | ||
309 | htcfg->ht_tx_cap = cpu_to_le16(txcfg->tx_htcap); | ||
310 | htcfg->ht_tx_info = cpu_to_le16(txcfg->tx_htinfo); | ||
311 | return 0; | ||
312 | } | ||
313 | |||
314 | /* | ||
315 | * This function appends an 11n TLV to a buffer. | ||
316 | * | ||
317 | * Buffer allocation is responsibility of the calling | ||
318 | * function. No size validation is made here. | ||
319 | * | ||
320 | * The function fills up the following sections, if applicable - | ||
321 | * - HT capability IE | ||
322 | * - HT information IE (with channel list) | ||
323 | * - 20/40 BSS Coexistence IE | ||
324 | * - HT Extended Capabilities IE | ||
325 | */ | ||
326 | int | ||
327 | mwifiex_cmd_append_11n_tlv(struct mwifiex_private *priv, | ||
328 | struct mwifiex_bssdescriptor *bss_desc, | ||
329 | u8 **buffer) | ||
330 | { | ||
331 | struct mwifiex_ie_types_htcap *ht_cap; | ||
332 | struct mwifiex_ie_types_htinfo *ht_info; | ||
333 | struct mwifiex_ie_types_chan_list_param_set *chan_list; | ||
334 | struct mwifiex_ie_types_2040bssco *bss_co_2040; | ||
335 | struct mwifiex_ie_types_extcap *ext_cap; | ||
336 | int ret_len = 0; | ||
337 | struct ieee80211_supported_band *sband; | ||
338 | u8 radio_type; | ||
339 | |||
340 | if (!buffer || !*buffer) | ||
341 | return ret_len; | ||
342 | |||
343 | radio_type = mwifiex_band_to_radio_type((u8) bss_desc->bss_band); | ||
344 | sband = priv->wdev->wiphy->bands[radio_type]; | ||
345 | |||
346 | if (bss_desc->bcn_ht_cap) { | ||
347 | ht_cap = (struct mwifiex_ie_types_htcap *) *buffer; | ||
348 | memset(ht_cap, 0, sizeof(struct mwifiex_ie_types_htcap)); | ||
349 | ht_cap->header.type = cpu_to_le16(WLAN_EID_HT_CAPABILITY); | ||
350 | ht_cap->header.len = | ||
351 | cpu_to_le16(sizeof(struct ieee80211_ht_cap)); | ||
352 | memcpy((u8 *) ht_cap + sizeof(struct mwifiex_ie_types_header), | ||
353 | (u8 *) bss_desc->bcn_ht_cap + | ||
354 | sizeof(struct ieee_types_header), | ||
355 | le16_to_cpu(ht_cap->header.len)); | ||
356 | |||
357 | mwifiex_fill_cap_info(priv, radio_type, ht_cap); | ||
358 | |||
359 | *buffer += sizeof(struct mwifiex_ie_types_htcap); | ||
360 | ret_len += sizeof(struct mwifiex_ie_types_htcap); | ||
361 | } | ||
362 | |||
363 | if (bss_desc->bcn_ht_info) { | ||
364 | if (priv->bss_mode == NL80211_IFTYPE_ADHOC) { | ||
365 | ht_info = (struct mwifiex_ie_types_htinfo *) *buffer; | ||
366 | memset(ht_info, 0, | ||
367 | sizeof(struct mwifiex_ie_types_htinfo)); | ||
368 | ht_info->header.type = | ||
369 | cpu_to_le16(WLAN_EID_HT_INFORMATION); | ||
370 | ht_info->header.len = | ||
371 | cpu_to_le16(sizeof(struct ieee80211_ht_info)); | ||
372 | |||
373 | memcpy((u8 *) ht_info + | ||
374 | sizeof(struct mwifiex_ie_types_header), | ||
375 | (u8 *) bss_desc->bcn_ht_info + | ||
376 | sizeof(struct ieee_types_header), | ||
377 | le16_to_cpu(ht_info->header.len)); | ||
378 | |||
379 | if (!(sband->ht_cap.cap & | ||
380 | IEEE80211_HT_CAP_SUP_WIDTH_20_40)) | ||
381 | ht_info->ht_info.ht_param &= | ||
382 | ~(IEEE80211_HT_PARAM_CHAN_WIDTH_ANY | | ||
383 | IEEE80211_HT_PARAM_CHA_SEC_OFFSET); | ||
384 | |||
385 | *buffer += sizeof(struct mwifiex_ie_types_htinfo); | ||
386 | ret_len += sizeof(struct mwifiex_ie_types_htinfo); | ||
387 | } | ||
388 | |||
389 | chan_list = | ||
390 | (struct mwifiex_ie_types_chan_list_param_set *) *buffer; | ||
391 | memset(chan_list, 0, | ||
392 | sizeof(struct mwifiex_ie_types_chan_list_param_set)); | ||
393 | chan_list->header.type = cpu_to_le16(TLV_TYPE_CHANLIST); | ||
394 | chan_list->header.len = cpu_to_le16( | ||
395 | sizeof(struct mwifiex_ie_types_chan_list_param_set) - | ||
396 | sizeof(struct mwifiex_ie_types_header)); | ||
397 | chan_list->chan_scan_param[0].chan_number = | ||
398 | bss_desc->bcn_ht_info->control_chan; | ||
399 | chan_list->chan_scan_param[0].radio_type = | ||
400 | mwifiex_band_to_radio_type((u8) bss_desc->bss_band); | ||
401 | |||
402 | if ((sband->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) | ||
403 | && (bss_desc->bcn_ht_info->ht_param & | ||
404 | IEEE80211_HT_PARAM_CHAN_WIDTH_ANY)) | ||
405 | SET_SECONDARYCHAN(chan_list->chan_scan_param[0]. | ||
406 | radio_type, | ||
407 | (bss_desc->bcn_ht_info->ht_param & | ||
408 | IEEE80211_HT_PARAM_CHA_SEC_OFFSET)); | ||
409 | |||
410 | *buffer += sizeof(struct mwifiex_ie_types_chan_list_param_set); | ||
411 | ret_len += sizeof(struct mwifiex_ie_types_chan_list_param_set); | ||
412 | } | ||
413 | |||
414 | if (bss_desc->bcn_bss_co_2040) { | ||
415 | bss_co_2040 = (struct mwifiex_ie_types_2040bssco *) *buffer; | ||
416 | memset(bss_co_2040, 0, | ||
417 | sizeof(struct mwifiex_ie_types_2040bssco)); | ||
418 | bss_co_2040->header.type = cpu_to_le16(WLAN_EID_BSS_COEX_2040); | ||
419 | bss_co_2040->header.len = | ||
420 | cpu_to_le16(sizeof(bss_co_2040->bss_co_2040)); | ||
421 | |||
422 | memcpy((u8 *) bss_co_2040 + | ||
423 | sizeof(struct mwifiex_ie_types_header), | ||
424 | (u8 *) bss_desc->bcn_bss_co_2040 + | ||
425 | sizeof(struct ieee_types_header), | ||
426 | le16_to_cpu(bss_co_2040->header.len)); | ||
427 | |||
428 | *buffer += sizeof(struct mwifiex_ie_types_2040bssco); | ||
429 | ret_len += sizeof(struct mwifiex_ie_types_2040bssco); | ||
430 | } | ||
431 | |||
432 | if (bss_desc->bcn_ext_cap) { | ||
433 | ext_cap = (struct mwifiex_ie_types_extcap *) *buffer; | ||
434 | memset(ext_cap, 0, sizeof(struct mwifiex_ie_types_extcap)); | ||
435 | ext_cap->header.type = cpu_to_le16(WLAN_EID_EXT_CAPABILITY); | ||
436 | ext_cap->header.len = cpu_to_le16(sizeof(ext_cap->ext_cap)); | ||
437 | |||
438 | memcpy((u8 *) ext_cap + | ||
439 | sizeof(struct mwifiex_ie_types_header), | ||
440 | (u8 *) bss_desc->bcn_ext_cap + | ||
441 | sizeof(struct ieee_types_header), | ||
442 | le16_to_cpu(ext_cap->header.len)); | ||
443 | |||
444 | *buffer += sizeof(struct mwifiex_ie_types_extcap); | ||
445 | ret_len += sizeof(struct mwifiex_ie_types_extcap); | ||
446 | } | ||
447 | |||
448 | return ret_len; | ||
449 | } | ||
450 | |||
451 | /* | ||
452 | * This function reconfigures the Tx buffer size in firmware. | ||
453 | * | ||
454 | * This function prepares a firmware command and issues it, if | ||
455 | * the current Tx buffer size is different from the one requested. | ||
456 | * Maximum configurable Tx buffer size is limited by the HT capability | ||
457 | * field value. | ||
458 | */ | ||
459 | void | ||
460 | mwifiex_cfg_tx_buf(struct mwifiex_private *priv, | ||
461 | struct mwifiex_bssdescriptor *bss_desc) | ||
462 | { | ||
463 | u16 max_amsdu = MWIFIEX_TX_DATA_BUF_SIZE_2K; | ||
464 | u16 tx_buf, curr_tx_buf_size = 0; | ||
465 | |||
466 | if (bss_desc->bcn_ht_cap) { | ||
467 | if (le16_to_cpu(bss_desc->bcn_ht_cap->cap_info) & | ||
468 | IEEE80211_HT_CAP_MAX_AMSDU) | ||
469 | max_amsdu = MWIFIEX_TX_DATA_BUF_SIZE_8K; | ||
470 | else | ||
471 | max_amsdu = MWIFIEX_TX_DATA_BUF_SIZE_4K; | ||
472 | } | ||
473 | |||
474 | tx_buf = min(priv->adapter->max_tx_buf_size, max_amsdu); | ||
475 | |||
476 | dev_dbg(priv->adapter->dev, "info: max_amsdu=%d, max_tx_buf=%d\n", | ||
477 | max_amsdu, priv->adapter->max_tx_buf_size); | ||
478 | |||
479 | if (priv->adapter->curr_tx_buf_size <= MWIFIEX_TX_DATA_BUF_SIZE_2K) | ||
480 | curr_tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_2K; | ||
481 | else if (priv->adapter->curr_tx_buf_size <= MWIFIEX_TX_DATA_BUF_SIZE_4K) | ||
482 | curr_tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_4K; | ||
483 | else if (priv->adapter->curr_tx_buf_size <= MWIFIEX_TX_DATA_BUF_SIZE_8K) | ||
484 | curr_tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_8K; | ||
485 | if (curr_tx_buf_size != tx_buf) | ||
486 | mwifiex_send_cmd_async(priv, HostCmd_CMD_RECONFIGURE_TX_BUFF, | ||
487 | HostCmd_ACT_GEN_SET, 0, &tx_buf); | ||
488 | } | ||
489 | |||
490 | /* | ||
491 | * This function checks if the given pointer is valid entry of | ||
492 | * Tx BA Stream table. | ||
493 | */ | ||
494 | static int mwifiex_is_tx_ba_stream_ptr_valid(struct mwifiex_private *priv, | ||
495 | struct mwifiex_tx_ba_stream_tbl *tx_tbl_ptr) | ||
496 | { | ||
497 | struct mwifiex_tx_ba_stream_tbl *tx_ba_tsr_tbl; | ||
498 | |||
499 | list_for_each_entry(tx_ba_tsr_tbl, &priv->tx_ba_stream_tbl_ptr, list) { | ||
500 | if (tx_ba_tsr_tbl == tx_tbl_ptr) | ||
501 | return true; | ||
502 | } | ||
503 | |||
504 | return false; | ||
505 | } | ||
506 | |||
507 | /* | ||
508 | * This function deletes the given entry in Tx BA Stream table. | ||
509 | * | ||
510 | * The function also performs a validity check on the supplied | ||
511 | * pointer before trying to delete. | ||
512 | */ | ||
513 | void mwifiex_11n_delete_tx_ba_stream_tbl_entry(struct mwifiex_private *priv, | ||
514 | struct mwifiex_tx_ba_stream_tbl *tx_ba_tsr_tbl) | ||
515 | { | ||
516 | if (!tx_ba_tsr_tbl && | ||
517 | mwifiex_is_tx_ba_stream_ptr_valid(priv, tx_ba_tsr_tbl)) | ||
518 | return; | ||
519 | |||
520 | dev_dbg(priv->adapter->dev, "info: tx_ba_tsr_tbl %p\n", tx_ba_tsr_tbl); | ||
521 | |||
522 | list_del(&tx_ba_tsr_tbl->list); | ||
523 | |||
524 | kfree(tx_ba_tsr_tbl); | ||
525 | } | ||
526 | |||
527 | /* | ||
528 | * This function deletes all the entries in Tx BA Stream table. | ||
529 | */ | ||
530 | void mwifiex_11n_delete_all_tx_ba_stream_tbl(struct mwifiex_private *priv) | ||
531 | { | ||
532 | int i; | ||
533 | struct mwifiex_tx_ba_stream_tbl *del_tbl_ptr, *tmp_node; | ||
534 | unsigned long flags; | ||
535 | |||
536 | spin_lock_irqsave(&priv->tx_ba_stream_tbl_lock, flags); | ||
537 | list_for_each_entry_safe(del_tbl_ptr, tmp_node, | ||
538 | &priv->tx_ba_stream_tbl_ptr, list) | ||
539 | mwifiex_11n_delete_tx_ba_stream_tbl_entry(priv, del_tbl_ptr); | ||
540 | spin_unlock_irqrestore(&priv->tx_ba_stream_tbl_lock, flags); | ||
541 | |||
542 | INIT_LIST_HEAD(&priv->tx_ba_stream_tbl_ptr); | ||
543 | |||
544 | for (i = 0; i < MAX_NUM_TID; ++i) | ||
545 | priv->aggr_prio_tbl[i].ampdu_ap = | ||
546 | priv->aggr_prio_tbl[i].ampdu_user; | ||
547 | } | ||
548 | |||
549 | /* | ||
550 | * This function returns the pointer to an entry in BA Stream | ||
551 | * table which matches the given RA/TID pair. | ||
552 | */ | ||
553 | struct mwifiex_tx_ba_stream_tbl * | ||
554 | mwifiex_11n_get_tx_ba_stream_tbl(struct mwifiex_private *priv, | ||
555 | int tid, u8 *ra) | ||
556 | { | ||
557 | struct mwifiex_tx_ba_stream_tbl *tx_ba_tsr_tbl; | ||
558 | unsigned long flags; | ||
559 | |||
560 | spin_lock_irqsave(&priv->tx_ba_stream_tbl_lock, flags); | ||
561 | list_for_each_entry(tx_ba_tsr_tbl, &priv->tx_ba_stream_tbl_ptr, list) { | ||
562 | if ((!memcmp(tx_ba_tsr_tbl->ra, ra, ETH_ALEN)) | ||
563 | && (tx_ba_tsr_tbl->tid == tid)) { | ||
564 | spin_unlock_irqrestore(&priv->tx_ba_stream_tbl_lock, | ||
565 | flags); | ||
566 | return tx_ba_tsr_tbl; | ||
567 | } | ||
568 | } | ||
569 | spin_unlock_irqrestore(&priv->tx_ba_stream_tbl_lock, flags); | ||
570 | return NULL; | ||
571 | } | ||
572 | |||
573 | /* | ||
574 | * This function creates an entry in Tx BA stream table for the | ||
575 | * given RA/TID pair. | ||
576 | */ | ||
577 | void mwifiex_11n_create_tx_ba_stream_tbl(struct mwifiex_private *priv, | ||
578 | u8 *ra, int tid, | ||
579 | enum mwifiex_ba_status ba_status) | ||
580 | { | ||
581 | struct mwifiex_tx_ba_stream_tbl *new_node; | ||
582 | unsigned long flags; | ||
583 | |||
584 | if (!mwifiex_11n_get_tx_ba_stream_tbl(priv, tid, ra)) { | ||
585 | new_node = kzalloc(sizeof(struct mwifiex_tx_ba_stream_tbl), | ||
586 | GFP_ATOMIC); | ||
587 | if (!new_node) { | ||
588 | dev_err(priv->adapter->dev, | ||
589 | "%s: failed to alloc new_node\n", __func__); | ||
590 | return; | ||
591 | } | ||
592 | |||
593 | INIT_LIST_HEAD(&new_node->list); | ||
594 | |||
595 | new_node->tid = tid; | ||
596 | new_node->ba_status = ba_status; | ||
597 | memcpy(new_node->ra, ra, ETH_ALEN); | ||
598 | |||
599 | spin_lock_irqsave(&priv->tx_ba_stream_tbl_lock, flags); | ||
600 | list_add_tail(&new_node->list, &priv->tx_ba_stream_tbl_ptr); | ||
601 | spin_unlock_irqrestore(&priv->tx_ba_stream_tbl_lock, flags); | ||
602 | } | ||
603 | } | ||
604 | |||
605 | /* | ||
606 | * This function sends an add BA request to the given TID/RA pair. | ||
607 | */ | ||
608 | int mwifiex_send_addba(struct mwifiex_private *priv, int tid, u8 *peer_mac) | ||
609 | { | ||
610 | struct host_cmd_ds_11n_addba_req add_ba_req; | ||
611 | static u8 dialog_tok; | ||
612 | int ret; | ||
613 | |||
614 | dev_dbg(priv->adapter->dev, "cmd: %s: tid %d\n", __func__, tid); | ||
615 | |||
616 | add_ba_req.block_ack_param_set = cpu_to_le16( | ||
617 | (u16) ((tid << BLOCKACKPARAM_TID_POS) | | ||
618 | (priv->add_ba_param. | ||
619 | tx_win_size << BLOCKACKPARAM_WINSIZE_POS) | | ||
620 | IMMEDIATE_BLOCK_ACK)); | ||
621 | add_ba_req.block_ack_tmo = cpu_to_le16((u16)priv->add_ba_param.timeout); | ||
622 | |||
623 | ++dialog_tok; | ||
624 | |||
625 | if (dialog_tok == 0) | ||
626 | dialog_tok = 1; | ||
627 | |||
628 | add_ba_req.dialog_token = dialog_tok; | ||
629 | memcpy(&add_ba_req.peer_mac_addr, peer_mac, ETH_ALEN); | ||
630 | |||
631 | /* We don't wait for the response of this command */ | ||
632 | ret = mwifiex_send_cmd_async(priv, HostCmd_CMD_11N_ADDBA_REQ, | ||
633 | 0, 0, &add_ba_req); | ||
634 | |||
635 | return ret; | ||
636 | } | ||
637 | |||
638 | /* | ||
639 | * This function sends a delete BA request to the given TID/RA pair. | ||
640 | */ | ||
641 | int mwifiex_send_delba(struct mwifiex_private *priv, int tid, u8 *peer_mac, | ||
642 | int initiator) | ||
643 | { | ||
644 | struct host_cmd_ds_11n_delba delba; | ||
645 | int ret; | ||
646 | uint16_t del_ba_param_set; | ||
647 | |||
648 | memset(&delba, 0, sizeof(delba)); | ||
649 | delba.del_ba_param_set = cpu_to_le16(tid << DELBA_TID_POS); | ||
650 | |||
651 | del_ba_param_set = le16_to_cpu(delba.del_ba_param_set); | ||
652 | if (initiator) | ||
653 | del_ba_param_set |= IEEE80211_DELBA_PARAM_INITIATOR_MASK; | ||
654 | else | ||
655 | del_ba_param_set &= ~IEEE80211_DELBA_PARAM_INITIATOR_MASK; | ||
656 | |||
657 | memcpy(&delba.peer_mac_addr, peer_mac, ETH_ALEN); | ||
658 | |||
659 | /* We don't wait for the response of this command */ | ||
660 | ret = mwifiex_send_cmd_async(priv, HostCmd_CMD_11N_DELBA, | ||
661 | HostCmd_ACT_GEN_SET, 0, &delba); | ||
662 | |||
663 | return ret; | ||
664 | } | ||
665 | |||
666 | /* | ||
667 | * This function handles the command response of a delete BA request. | ||
668 | */ | ||
669 | void mwifiex_11n_delete_ba_stream(struct mwifiex_private *priv, u8 *del_ba) | ||
670 | { | ||
671 | struct host_cmd_ds_11n_delba *cmd_del_ba = | ||
672 | (struct host_cmd_ds_11n_delba *) del_ba; | ||
673 | uint16_t del_ba_param_set = le16_to_cpu(cmd_del_ba->del_ba_param_set); | ||
674 | int tid; | ||
675 | |||
676 | tid = del_ba_param_set >> DELBA_TID_POS; | ||
677 | |||
678 | mwifiex_11n_delete_ba_stream_tbl(priv, tid, cmd_del_ba->peer_mac_addr, | ||
679 | TYPE_DELBA_RECEIVE, | ||
680 | INITIATOR_BIT(del_ba_param_set)); | ||
681 | } | ||
682 | |||
683 | /* | ||
684 | * This function retrieves the Rx reordering table. | ||
685 | */ | ||
686 | int mwifiex_get_rx_reorder_tbl(struct mwifiex_private *priv, | ||
687 | struct mwifiex_ds_rx_reorder_tbl *buf) | ||
688 | { | ||
689 | int i; | ||
690 | struct mwifiex_ds_rx_reorder_tbl *rx_reo_tbl = buf; | ||
691 | struct mwifiex_rx_reorder_tbl *rx_reorder_tbl_ptr; | ||
692 | int count = 0; | ||
693 | unsigned long flags; | ||
694 | |||
695 | spin_lock_irqsave(&priv->rx_reorder_tbl_lock, flags); | ||
696 | list_for_each_entry(rx_reorder_tbl_ptr, &priv->rx_reorder_tbl_ptr, | ||
697 | list) { | ||
698 | rx_reo_tbl->tid = (u16) rx_reorder_tbl_ptr->tid; | ||
699 | memcpy(rx_reo_tbl->ta, rx_reorder_tbl_ptr->ta, ETH_ALEN); | ||
700 | rx_reo_tbl->start_win = rx_reorder_tbl_ptr->start_win; | ||
701 | rx_reo_tbl->win_size = rx_reorder_tbl_ptr->win_size; | ||
702 | for (i = 0; i < rx_reorder_tbl_ptr->win_size; ++i) { | ||
703 | if (rx_reorder_tbl_ptr->rx_reorder_ptr[i]) | ||
704 | rx_reo_tbl->buffer[i] = true; | ||
705 | else | ||
706 | rx_reo_tbl->buffer[i] = false; | ||
707 | } | ||
708 | rx_reo_tbl++; | ||
709 | count++; | ||
710 | |||
711 | if (count >= MWIFIEX_MAX_RX_BASTREAM_SUPPORTED) | ||
712 | break; | ||
713 | } | ||
714 | spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, flags); | ||
715 | |||
716 | return count; | ||
717 | } | ||
718 | |||
719 | /* | ||
720 | * This function retrieves the Tx BA stream table. | ||
721 | */ | ||
722 | int mwifiex_get_tx_ba_stream_tbl(struct mwifiex_private *priv, | ||
723 | struct mwifiex_ds_tx_ba_stream_tbl *buf) | ||
724 | { | ||
725 | struct mwifiex_tx_ba_stream_tbl *tx_ba_tsr_tbl; | ||
726 | struct mwifiex_ds_tx_ba_stream_tbl *rx_reo_tbl = buf; | ||
727 | int count = 0; | ||
728 | unsigned long flags; | ||
729 | |||
730 | spin_lock_irqsave(&priv->tx_ba_stream_tbl_lock, flags); | ||
731 | list_for_each_entry(tx_ba_tsr_tbl, &priv->tx_ba_stream_tbl_ptr, list) { | ||
732 | rx_reo_tbl->tid = (u16) tx_ba_tsr_tbl->tid; | ||
733 | dev_dbg(priv->adapter->dev, "data: %s tid=%d\n", | ||
734 | __func__, rx_reo_tbl->tid); | ||
735 | memcpy(rx_reo_tbl->ra, tx_ba_tsr_tbl->ra, ETH_ALEN); | ||
736 | rx_reo_tbl++; | ||
737 | count++; | ||
738 | if (count >= MWIFIEX_MAX_TX_BASTREAM_SUPPORTED) | ||
739 | break; | ||
740 | } | ||
741 | spin_unlock_irqrestore(&priv->tx_ba_stream_tbl_lock, flags); | ||
742 | |||
743 | return count; | ||
744 | } | ||
diff --git a/drivers/net/wireless/mwifiex/11n.h b/drivers/net/wireless/mwifiex/11n.h new file mode 100644 index 000000000000..a4390a1a2a9f --- /dev/null +++ b/drivers/net/wireless/mwifiex/11n.h | |||
@@ -0,0 +1,161 @@ | |||
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 | int mwifiex_ret_11n_delba(struct mwifiex_private *priv, | ||
28 | struct host_cmd_ds_command *resp); | ||
29 | int mwifiex_ret_11n_addba_req(struct mwifiex_private *priv, | ||
30 | struct host_cmd_ds_command *resp); | ||
31 | int mwifiex_ret_11n_cfg(struct host_cmd_ds_command *resp, | ||
32 | void *data_buf); | ||
33 | int mwifiex_cmd_11n_cfg(struct host_cmd_ds_command *cmd, | ||
34 | u16 cmd_action, void *data_buf); | ||
35 | |||
36 | int mwifiex_cmd_append_11n_tlv(struct mwifiex_private *priv, | ||
37 | struct mwifiex_bssdescriptor *bss_desc, | ||
38 | u8 **buffer); | ||
39 | void mwifiex_cfg_tx_buf(struct mwifiex_private *priv, | ||
40 | struct mwifiex_bssdescriptor *bss_desc); | ||
41 | void mwifiex_fill_cap_info(struct mwifiex_private *, u8 radio_type, | ||
42 | struct mwifiex_ie_types_htcap *); | ||
43 | int mwifiex_set_get_11n_htcap_cfg(struct mwifiex_private *priv, | ||
44 | u16 action, int *htcap_cfg); | ||
45 | void mwifiex_11n_delete_tx_ba_stream_tbl_entry(struct mwifiex_private *priv, | ||
46 | struct mwifiex_tx_ba_stream_tbl | ||
47 | *tx_tbl); | ||
48 | void mwifiex_11n_delete_all_tx_ba_stream_tbl(struct mwifiex_private *priv); | ||
49 | struct mwifiex_tx_ba_stream_tbl *mwifiex_11n_get_tx_ba_stream_tbl(struct | ||
50 | mwifiex_private | ||
51 | *priv, int tid, | ||
52 | u8 *ra); | ||
53 | void mwifiex_11n_create_tx_ba_stream_tbl(struct mwifiex_private *priv, u8 *ra, | ||
54 | int tid, | ||
55 | enum mwifiex_ba_status ba_status); | ||
56 | int mwifiex_send_addba(struct mwifiex_private *priv, int tid, u8 *peer_mac); | ||
57 | int mwifiex_send_delba(struct mwifiex_private *priv, int tid, u8 *peer_mac, | ||
58 | int initiator); | ||
59 | void mwifiex_11n_delete_ba_stream(struct mwifiex_private *priv, u8 *del_ba); | ||
60 | int mwifiex_get_rx_reorder_tbl(struct mwifiex_private *priv, | ||
61 | struct mwifiex_ds_rx_reorder_tbl *buf); | ||
62 | int mwifiex_get_tx_ba_stream_tbl(struct mwifiex_private *priv, | ||
63 | struct mwifiex_ds_tx_ba_stream_tbl *buf); | ||
64 | int mwifiex_ret_amsdu_aggr_ctrl(struct host_cmd_ds_command *resp, | ||
65 | void *data_buf); | ||
66 | int mwifiex_cmd_recfg_tx_buf(struct mwifiex_private *priv, | ||
67 | struct host_cmd_ds_command *cmd, | ||
68 | int cmd_action, void *data_buf); | ||
69 | int mwifiex_cmd_amsdu_aggr_ctrl(struct host_cmd_ds_command *cmd, | ||
70 | int cmd_action, void *data_buf); | ||
71 | |||
72 | /* | ||
73 | * This function checks whether AMPDU is allowed or not for a particular TID. | ||
74 | */ | ||
75 | static inline u8 | ||
76 | mwifiex_is_ampdu_allowed(struct mwifiex_private *priv, int tid) | ||
77 | { | ||
78 | return ((priv->aggr_prio_tbl[tid].ampdu_ap != BA_STREAM_NOT_ALLOWED) | ||
79 | ? true : false); | ||
80 | } | ||
81 | |||
82 | /* | ||
83 | * This function checks whether AMSDU is allowed or not for a particular TID. | ||
84 | */ | ||
85 | static inline u8 | ||
86 | mwifiex_is_amsdu_allowed(struct mwifiex_private *priv, int tid) | ||
87 | { | ||
88 | return (((priv->aggr_prio_tbl[tid].amsdu != BA_STREAM_NOT_ALLOWED) | ||
89 | && ((priv->is_data_rate_auto) | ||
90 | || !((priv->bitmap_rates[2]) & 0x03))) | ||
91 | ? true : false); | ||
92 | } | ||
93 | |||
94 | /* | ||
95 | * This function checks whether a space is available for new BA stream or not. | ||
96 | */ | ||
97 | static inline u8 mwifiex_space_avail_for_new_ba_stream( | ||
98 | struct mwifiex_adapter *adapter) | ||
99 | { | ||
100 | struct mwifiex_private *priv; | ||
101 | u8 i; | ||
102 | u32 ba_stream_num = 0; | ||
103 | |||
104 | for (i = 0; i < adapter->priv_num; i++) { | ||
105 | priv = adapter->priv[i]; | ||
106 | if (priv) | ||
107 | ba_stream_num += mwifiex_wmm_list_len( | ||
108 | (struct list_head *) | ||
109 | &priv->tx_ba_stream_tbl_ptr); | ||
110 | } | ||
111 | |||
112 | return ((ba_stream_num < | ||
113 | MWIFIEX_MAX_TX_BASTREAM_SUPPORTED) ? true : false); | ||
114 | } | ||
115 | |||
116 | /* | ||
117 | * This function finds the correct Tx BA stream to delete. | ||
118 | * | ||
119 | * Upon successfully locating, both the TID and the RA are returned. | ||
120 | */ | ||
121 | static inline u8 | ||
122 | mwifiex_find_stream_to_delete(struct mwifiex_private *priv, int ptr_tid, | ||
123 | int *ptid, u8 *ra) | ||
124 | { | ||
125 | int tid; | ||
126 | u8 ret = false; | ||
127 | struct mwifiex_tx_ba_stream_tbl *tx_tbl; | ||
128 | unsigned long flags; | ||
129 | |||
130 | tid = priv->aggr_prio_tbl[ptr_tid].ampdu_user; | ||
131 | |||
132 | spin_lock_irqsave(&priv->tx_ba_stream_tbl_lock, flags); | ||
133 | list_for_each_entry(tx_tbl, &priv->tx_ba_stream_tbl_ptr, list) { | ||
134 | if (tid > priv->aggr_prio_tbl[tx_tbl->tid].ampdu_user) { | ||
135 | tid = priv->aggr_prio_tbl[tx_tbl->tid].ampdu_user; | ||
136 | *ptid = tx_tbl->tid; | ||
137 | memcpy(ra, tx_tbl->ra, ETH_ALEN); | ||
138 | ret = true; | ||
139 | } | ||
140 | } | ||
141 | spin_unlock_irqrestore(&priv->tx_ba_stream_tbl_lock, flags); | ||
142 | |||
143 | return ret; | ||
144 | } | ||
145 | |||
146 | /* | ||
147 | * This function checks whether BA stream is set up or not. | ||
148 | */ | ||
149 | static inline int | ||
150 | mwifiex_is_ba_stream_setup(struct mwifiex_private *priv, | ||
151 | struct mwifiex_ra_list_tbl *ptr, int tid) | ||
152 | { | ||
153 | struct mwifiex_tx_ba_stream_tbl *tx_tbl; | ||
154 | |||
155 | tx_tbl = mwifiex_11n_get_tx_ba_stream_tbl(priv, tid, ptr->ra); | ||
156 | if (tx_tbl && IS_BASTREAM_SETUP(tx_tbl)) | ||
157 | return true; | ||
158 | |||
159 | return false; | ||
160 | } | ||
161 | #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..f807447e4d99 --- /dev/null +++ b/drivers/net/wireless/mwifiex/11n_aggr.c | |||
@@ -0,0 +1,302 @@ | |||
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 sk_buff *skb_aggr, | ||
48 | struct sk_buff *skb_src, int *pad) | ||
49 | |||
50 | { | ||
51 | int dt_offset; | ||
52 | struct rfc_1042_hdr snap = { | ||
53 | 0xaa, /* LLC DSAP */ | ||
54 | 0xaa, /* LLC SSAP */ | ||
55 | 0x03, /* LLC CTRL */ | ||
56 | {0x00, 0x00, 0x00}, /* SNAP OUI */ | ||
57 | 0x0000 /* SNAP type */ | ||
58 | /* | ||
59 | * This field will be overwritten | ||
60 | * later with ethertype | ||
61 | */ | ||
62 | }; | ||
63 | struct tx_packet_hdr *tx_header; | ||
64 | |||
65 | skb_put(skb_aggr, sizeof(*tx_header)); | ||
66 | |||
67 | tx_header = (struct tx_packet_hdr *) skb_aggr->data; | ||
68 | |||
69 | /* Copy DA and SA */ | ||
70 | dt_offset = 2 * ETH_ALEN; | ||
71 | memcpy(&tx_header->eth803_hdr, skb_src->data, dt_offset); | ||
72 | |||
73 | /* Copy SNAP header */ | ||
74 | snap.snap_type = *(u16 *) ((u8 *)skb_src->data + dt_offset); | ||
75 | dt_offset += sizeof(u16); | ||
76 | |||
77 | memcpy(&tx_header->rfc1042_hdr, &snap, sizeof(struct rfc_1042_hdr)); | ||
78 | |||
79 | skb_pull(skb_src, dt_offset); | ||
80 | |||
81 | /* Update Length field */ | ||
82 | tx_header->eth803_hdr.h_proto = htons(skb_src->len + LLC_SNAP_LEN); | ||
83 | |||
84 | /* Add payload */ | ||
85 | skb_put(skb_aggr, skb_src->len); | ||
86 | memcpy(skb_aggr->data + sizeof(*tx_header), skb_src->data, | ||
87 | skb_src->len); | ||
88 | *pad = (((skb_src->len + LLC_SNAP_LEN) & 3)) ? (4 - (((skb_src->len + | ||
89 | LLC_SNAP_LEN)) & 3)) : 0; | ||
90 | skb_put(skb_aggr, *pad); | ||
91 | |||
92 | return skb_aggr->len + *pad; | ||
93 | } | ||
94 | |||
95 | /* | ||
96 | * Adds TxPD to AMSDU header. | ||
97 | * | ||
98 | * Each AMSDU packet will contain one TxPD at the beginning, | ||
99 | * followed by multiple AMSDU subframes. | ||
100 | */ | ||
101 | static void | ||
102 | mwifiex_11n_form_amsdu_txpd(struct mwifiex_private *priv, | ||
103 | struct sk_buff *skb) | ||
104 | { | ||
105 | struct txpd *local_tx_pd; | ||
106 | |||
107 | skb_push(skb, sizeof(*local_tx_pd)); | ||
108 | |||
109 | local_tx_pd = (struct txpd *) skb->data; | ||
110 | memset(local_tx_pd, 0, sizeof(struct txpd)); | ||
111 | |||
112 | /* Original priority has been overwritten */ | ||
113 | local_tx_pd->priority = (u8) skb->priority; | ||
114 | local_tx_pd->pkt_delay_2ms = | ||
115 | mwifiex_wmm_compute_drv_pkt_delay(priv, skb); | ||
116 | local_tx_pd->bss_num = priv->bss_num; | ||
117 | local_tx_pd->bss_type = priv->bss_type; | ||
118 | /* Always zero as the data is followed by struct txpd */ | ||
119 | local_tx_pd->tx_pkt_offset = cpu_to_le16(sizeof(struct txpd)); | ||
120 | local_tx_pd->tx_pkt_type = cpu_to_le16(PKT_TYPE_AMSDU); | ||
121 | local_tx_pd->tx_pkt_length = cpu_to_le16(skb->len - | ||
122 | sizeof(*local_tx_pd)); | ||
123 | |||
124 | if (local_tx_pd->tx_control == 0) | ||
125 | /* TxCtrl set by user or default */ | ||
126 | local_tx_pd->tx_control = cpu_to_le32(priv->pkt_tx_ctrl); | ||
127 | |||
128 | if ((GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA) && | ||
129 | (priv->adapter->pps_uapsd_mode)) { | ||
130 | if (true == mwifiex_check_last_packet_indication(priv)) { | ||
131 | priv->adapter->tx_lock_flag = true; | ||
132 | local_tx_pd->flags = | ||
133 | MWIFIEX_TxPD_POWER_MGMT_LAST_PACKET; | ||
134 | } | ||
135 | } | ||
136 | } | ||
137 | |||
138 | /* | ||
139 | * Create aggregated packet. | ||
140 | * | ||
141 | * This function creates an aggregated MSDU packet, by combining buffers | ||
142 | * from the RA list. Each individual buffer is encapsulated as an AMSDU | ||
143 | * subframe and all such subframes are concatenated together to form the | ||
144 | * AMSDU packet. | ||
145 | * | ||
146 | * A TxPD is also added to the front of the resultant AMSDU packets for | ||
147 | * transmission. The resultant packets format is - | ||
148 | * | ||
149 | * +---- ~ ----+------ ~ ------+------ ~ ------+-..-+------ ~ ------+ | ||
150 | * | TxPD |AMSDU sub-frame|AMSDU sub-frame| .. |AMSDU sub-frame| | ||
151 | * | | 1 | 2 | .. | n | | ||
152 | * +---- ~ ----+------ ~ ------+------ ~ ------+ .. +------ ~ ------+ | ||
153 | */ | ||
154 | int | ||
155 | mwifiex_11n_aggregate_pkt(struct mwifiex_private *priv, | ||
156 | struct mwifiex_ra_list_tbl *pra_list, int headroom, | ||
157 | int ptrindex, unsigned long ra_list_flags) | ||
158 | __releases(&priv->wmm.ra_list_spinlock) | ||
159 | { | ||
160 | struct mwifiex_adapter *adapter = priv->adapter; | ||
161 | struct sk_buff *skb_aggr, *skb_src; | ||
162 | struct mwifiex_txinfo *tx_info_aggr, *tx_info_src; | ||
163 | int pad = 0, ret; | ||
164 | struct mwifiex_tx_param tx_param; | ||
165 | struct txpd *ptx_pd = NULL; | ||
166 | |||
167 | if (skb_queue_empty(&pra_list->skb_head)) { | ||
168 | spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, | ||
169 | ra_list_flags); | ||
170 | return 0; | ||
171 | } | ||
172 | skb_src = skb_peek(&pra_list->skb_head); | ||
173 | tx_info_src = MWIFIEX_SKB_TXCB(skb_src); | ||
174 | skb_aggr = dev_alloc_skb(adapter->tx_buf_size); | ||
175 | if (!skb_aggr) { | ||
176 | dev_err(adapter->dev, "%s: alloc skb_aggr\n", __func__); | ||
177 | spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, | ||
178 | ra_list_flags); | ||
179 | return -1; | ||
180 | } | ||
181 | skb_reserve(skb_aggr, headroom + sizeof(struct txpd)); | ||
182 | tx_info_aggr = MWIFIEX_SKB_TXCB(skb_aggr); | ||
183 | |||
184 | tx_info_aggr->bss_index = tx_info_src->bss_index; | ||
185 | skb_aggr->priority = skb_src->priority; | ||
186 | |||
187 | while (skb_src && ((skb_headroom(skb_aggr) + skb_src->len | ||
188 | + LLC_SNAP_LEN) | ||
189 | <= adapter->tx_buf_size)) { | ||
190 | |||
191 | if (!skb_queue_empty(&pra_list->skb_head)) | ||
192 | skb_src = skb_dequeue(&pra_list->skb_head); | ||
193 | else | ||
194 | skb_src = NULL; | ||
195 | |||
196 | if (skb_src) | ||
197 | pra_list->total_pkts_size -= skb_src->len; | ||
198 | |||
199 | atomic_dec(&priv->wmm.tx_pkts_queued); | ||
200 | |||
201 | spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, | ||
202 | ra_list_flags); | ||
203 | mwifiex_11n_form_amsdu_pkt(skb_aggr, skb_src, &pad); | ||
204 | |||
205 | mwifiex_write_data_complete(adapter, skb_src, 0); | ||
206 | |||
207 | spin_lock_irqsave(&priv->wmm.ra_list_spinlock, ra_list_flags); | ||
208 | |||
209 | if (!mwifiex_is_ralist_valid(priv, pra_list, ptrindex)) { | ||
210 | spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, | ||
211 | ra_list_flags); | ||
212 | return -1; | ||
213 | } | ||
214 | |||
215 | if (!skb_queue_empty(&pra_list->skb_head)) | ||
216 | skb_src = skb_peek(&pra_list->skb_head); | ||
217 | else | ||
218 | skb_src = NULL; | ||
219 | } | ||
220 | |||
221 | spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, ra_list_flags); | ||
222 | |||
223 | /* Last AMSDU packet does not need padding */ | ||
224 | skb_trim(skb_aggr, skb_aggr->len - pad); | ||
225 | |||
226 | /* Form AMSDU */ | ||
227 | mwifiex_11n_form_amsdu_txpd(priv, skb_aggr); | ||
228 | if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA) | ||
229 | ptx_pd = (struct txpd *)skb_aggr->data; | ||
230 | |||
231 | skb_push(skb_aggr, headroom); | ||
232 | |||
233 | tx_param.next_pkt_len = ((pra_list->total_pkts_size) ? | ||
234 | (((pra_list->total_pkts_size) > | ||
235 | adapter->tx_buf_size) ? adapter-> | ||
236 | tx_buf_size : pra_list->total_pkts_size + | ||
237 | LLC_SNAP_LEN + sizeof(struct txpd)) : 0); | ||
238 | ret = adapter->if_ops.host_to_card(adapter, MWIFIEX_TYPE_DATA, | ||
239 | skb_aggr->data, | ||
240 | skb_aggr->len, &tx_param); | ||
241 | switch (ret) { | ||
242 | case -EBUSY: | ||
243 | spin_lock_irqsave(&priv->wmm.ra_list_spinlock, ra_list_flags); | ||
244 | if (!mwifiex_is_ralist_valid(priv, pra_list, ptrindex)) { | ||
245 | spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, | ||
246 | ra_list_flags); | ||
247 | mwifiex_write_data_complete(adapter, skb_aggr, -1); | ||
248 | return -1; | ||
249 | } | ||
250 | if ((GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA) && | ||
251 | (adapter->pps_uapsd_mode) && | ||
252 | (adapter->tx_lock_flag)) { | ||
253 | priv->adapter->tx_lock_flag = false; | ||
254 | if (ptx_pd) | ||
255 | ptx_pd->flags = 0; | ||
256 | } | ||
257 | |||
258 | skb_queue_tail(&pra_list->skb_head, skb_aggr); | ||
259 | |||
260 | pra_list->total_pkts_size += skb_aggr->len; | ||
261 | |||
262 | atomic_inc(&priv->wmm.tx_pkts_queued); | ||
263 | |||
264 | tx_info_aggr->flags |= MWIFIEX_BUF_FLAG_REQUEUED_PKT; | ||
265 | spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, | ||
266 | ra_list_flags); | ||
267 | dev_dbg(adapter->dev, "data: -EBUSY is returned\n"); | ||
268 | break; | ||
269 | case -1: | ||
270 | adapter->data_sent = false; | ||
271 | dev_err(adapter->dev, "%s: host_to_card failed: %#x\n", | ||
272 | __func__, ret); | ||
273 | adapter->dbg.num_tx_host_to_card_failure++; | ||
274 | mwifiex_write_data_complete(adapter, skb_aggr, ret); | ||
275 | return 0; | ||
276 | case -EINPROGRESS: | ||
277 | adapter->data_sent = false; | ||
278 | break; | ||
279 | case 0: | ||
280 | mwifiex_write_data_complete(adapter, skb_aggr, ret); | ||
281 | break; | ||
282 | default: | ||
283 | break; | ||
284 | } | ||
285 | if (ret != -EBUSY) { | ||
286 | spin_lock_irqsave(&priv->wmm.ra_list_spinlock, ra_list_flags); | ||
287 | if (mwifiex_is_ralist_valid(priv, pra_list, ptrindex)) { | ||
288 | priv->wmm.packets_out[ptrindex]++; | ||
289 | priv->wmm.tid_tbl_ptr[ptrindex].ra_list_curr = pra_list; | ||
290 | } | ||
291 | /* Now bss_prio_cur pointer points to next node */ | ||
292 | adapter->bss_prio_tbl[priv->bss_priority].bss_prio_cur = | ||
293 | list_first_entry( | ||
294 | &adapter->bss_prio_tbl[priv->bss_priority] | ||
295 | .bss_prio_cur->list, | ||
296 | struct mwifiex_bss_prio_node, list); | ||
297 | spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, | ||
298 | ra_list_flags); | ||
299 | } | ||
300 | |||
301 | return 0; | ||
302 | } | ||
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..e5dfdc39a921 --- /dev/null +++ b/drivers/net/wireless/mwifiex/11n_rxreorder.c | |||
@@ -0,0 +1,616 @@ | |||
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 dispatches all packets in the Rx reorder table. | ||
31 | * | ||
32 | * There could be holes in the buffer, which are skipped by the function. | ||
33 | * Since the buffer is linear, the function uses rotation to simulate | ||
34 | * circular buffer. | ||
35 | */ | ||
36 | static int | ||
37 | mwifiex_11n_dispatch_pkt_until_start_win(struct mwifiex_private *priv, | ||
38 | struct mwifiex_rx_reorder_tbl | ||
39 | *rx_reor_tbl_ptr, int start_win) | ||
40 | { | ||
41 | int no_pkt_to_send, i; | ||
42 | void *rx_tmp_ptr; | ||
43 | unsigned long flags; | ||
44 | |||
45 | no_pkt_to_send = (start_win > rx_reor_tbl_ptr->start_win) ? | ||
46 | min((start_win - rx_reor_tbl_ptr->start_win), | ||
47 | rx_reor_tbl_ptr->win_size) : rx_reor_tbl_ptr->win_size; | ||
48 | |||
49 | for (i = 0; i < no_pkt_to_send; ++i) { | ||
50 | spin_lock_irqsave(&priv->rx_pkt_lock, flags); | ||
51 | rx_tmp_ptr = NULL; | ||
52 | if (rx_reor_tbl_ptr->rx_reorder_ptr[i]) { | ||
53 | rx_tmp_ptr = rx_reor_tbl_ptr->rx_reorder_ptr[i]; | ||
54 | rx_reor_tbl_ptr->rx_reorder_ptr[i] = NULL; | ||
55 | } | ||
56 | spin_unlock_irqrestore(&priv->rx_pkt_lock, flags); | ||
57 | if (rx_tmp_ptr) | ||
58 | mwifiex_process_rx_packet(priv->adapter, rx_tmp_ptr); | ||
59 | } | ||
60 | |||
61 | spin_lock_irqsave(&priv->rx_pkt_lock, flags); | ||
62 | /* | ||
63 | * We don't have a circular buffer, hence use rotation to simulate | ||
64 | * circular buffer | ||
65 | */ | ||
66 | for (i = 0; i < rx_reor_tbl_ptr->win_size - no_pkt_to_send; ++i) { | ||
67 | rx_reor_tbl_ptr->rx_reorder_ptr[i] = | ||
68 | rx_reor_tbl_ptr->rx_reorder_ptr[no_pkt_to_send + i]; | ||
69 | rx_reor_tbl_ptr->rx_reorder_ptr[no_pkt_to_send + i] = NULL; | ||
70 | } | ||
71 | |||
72 | rx_reor_tbl_ptr->start_win = start_win; | ||
73 | spin_unlock_irqrestore(&priv->rx_pkt_lock, flags); | ||
74 | |||
75 | return 0; | ||
76 | } | ||
77 | |||
78 | /* | ||
79 | * This function dispatches all packets in the Rx reorder table until | ||
80 | * a hole is found. | ||
81 | * | ||
82 | * The start window is adjusted automatically when a hole is located. | ||
83 | * Since the buffer is linear, the function uses rotation to simulate | ||
84 | * circular buffer. | ||
85 | */ | ||
86 | static int | ||
87 | mwifiex_11n_scan_and_dispatch(struct mwifiex_private *priv, | ||
88 | struct mwifiex_rx_reorder_tbl *rx_reor_tbl_ptr) | ||
89 | { | ||
90 | int i, j, xchg; | ||
91 | void *rx_tmp_ptr; | ||
92 | unsigned long flags; | ||
93 | |||
94 | for (i = 0; i < rx_reor_tbl_ptr->win_size; ++i) { | ||
95 | spin_lock_irqsave(&priv->rx_pkt_lock, flags); | ||
96 | if (!rx_reor_tbl_ptr->rx_reorder_ptr[i]) { | ||
97 | spin_unlock_irqrestore(&priv->rx_pkt_lock, flags); | ||
98 | break; | ||
99 | } | ||
100 | rx_tmp_ptr = rx_reor_tbl_ptr->rx_reorder_ptr[i]; | ||
101 | rx_reor_tbl_ptr->rx_reorder_ptr[i] = NULL; | ||
102 | spin_unlock_irqrestore(&priv->rx_pkt_lock, flags); | ||
103 | mwifiex_process_rx_packet(priv->adapter, rx_tmp_ptr); | ||
104 | } | ||
105 | |||
106 | spin_lock_irqsave(&priv->rx_pkt_lock, flags); | ||
107 | /* | ||
108 | * We don't have a circular buffer, hence use rotation to simulate | ||
109 | * circular buffer | ||
110 | */ | ||
111 | if (i > 0) { | ||
112 | xchg = rx_reor_tbl_ptr->win_size - i; | ||
113 | for (j = 0; j < xchg; ++j) { | ||
114 | rx_reor_tbl_ptr->rx_reorder_ptr[j] = | ||
115 | rx_reor_tbl_ptr->rx_reorder_ptr[i + j]; | ||
116 | rx_reor_tbl_ptr->rx_reorder_ptr[i + j] = NULL; | ||
117 | } | ||
118 | } | ||
119 | rx_reor_tbl_ptr->start_win = (rx_reor_tbl_ptr->start_win + i) | ||
120 | &(MAX_TID_VALUE - 1); | ||
121 | spin_unlock_irqrestore(&priv->rx_pkt_lock, flags); | ||
122 | return 0; | ||
123 | } | ||
124 | |||
125 | /* | ||
126 | * This function deletes the Rx reorder table and frees the memory. | ||
127 | * | ||
128 | * The function stops the associated timer and dispatches all the | ||
129 | * pending packets in the Rx reorder table before deletion. | ||
130 | */ | ||
131 | static void | ||
132 | mwifiex_11n_delete_rx_reorder_tbl_entry(struct mwifiex_private *priv, | ||
133 | struct mwifiex_rx_reorder_tbl | ||
134 | *rx_reor_tbl_ptr) | ||
135 | { | ||
136 | unsigned long flags; | ||
137 | |||
138 | if (!rx_reor_tbl_ptr) | ||
139 | return; | ||
140 | |||
141 | mwifiex_11n_dispatch_pkt_until_start_win(priv, rx_reor_tbl_ptr, | ||
142 | (rx_reor_tbl_ptr->start_win + | ||
143 | rx_reor_tbl_ptr->win_size) | ||
144 | &(MAX_TID_VALUE - 1)); | ||
145 | |||
146 | del_timer(&rx_reor_tbl_ptr->timer_context.timer); | ||
147 | |||
148 | spin_lock_irqsave(&priv->rx_reorder_tbl_lock, flags); | ||
149 | list_del(&rx_reor_tbl_ptr->list); | ||
150 | spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, flags); | ||
151 | |||
152 | kfree(rx_reor_tbl_ptr->rx_reorder_ptr); | ||
153 | kfree(rx_reor_tbl_ptr); | ||
154 | } | ||
155 | |||
156 | /* | ||
157 | * This function returns the pointer to an entry in Rx reordering | ||
158 | * table which matches the given TA/TID pair. | ||
159 | */ | ||
160 | static struct mwifiex_rx_reorder_tbl * | ||
161 | mwifiex_11n_get_rx_reorder_tbl(struct mwifiex_private *priv, int tid, u8 *ta) | ||
162 | { | ||
163 | struct mwifiex_rx_reorder_tbl *rx_reor_tbl_ptr; | ||
164 | unsigned long flags; | ||
165 | |||
166 | spin_lock_irqsave(&priv->rx_reorder_tbl_lock, flags); | ||
167 | list_for_each_entry(rx_reor_tbl_ptr, &priv->rx_reorder_tbl_ptr, list) { | ||
168 | if ((!memcmp(rx_reor_tbl_ptr->ta, ta, ETH_ALEN)) | ||
169 | && (rx_reor_tbl_ptr->tid == tid)) { | ||
170 | spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, | ||
171 | flags); | ||
172 | return rx_reor_tbl_ptr; | ||
173 | } | ||
174 | } | ||
175 | spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, flags); | ||
176 | |||
177 | return NULL; | ||
178 | } | ||
179 | |||
180 | /* | ||
181 | * This function finds the last sequence number used in the packets | ||
182 | * buffered in Rx reordering table. | ||
183 | */ | ||
184 | static int | ||
185 | mwifiex_11n_find_last_seq_num(struct mwifiex_rx_reorder_tbl *rx_reorder_tbl_ptr) | ||
186 | { | ||
187 | int i; | ||
188 | |||
189 | for (i = (rx_reorder_tbl_ptr->win_size - 1); i >= 0; --i) | ||
190 | if (rx_reorder_tbl_ptr->rx_reorder_ptr[i]) | ||
191 | return i; | ||
192 | |||
193 | return -1; | ||
194 | } | ||
195 | |||
196 | /* | ||
197 | * This function flushes all the packets in Rx reordering table. | ||
198 | * | ||
199 | * The function checks if any packets are currently buffered in the | ||
200 | * table or not. In case there are packets available, it dispatches | ||
201 | * them and then dumps the Rx reordering table. | ||
202 | */ | ||
203 | static void | ||
204 | mwifiex_flush_data(unsigned long context) | ||
205 | { | ||
206 | struct reorder_tmr_cnxt *reorder_cnxt = | ||
207 | (struct reorder_tmr_cnxt *) context; | ||
208 | int start_win; | ||
209 | |||
210 | start_win = mwifiex_11n_find_last_seq_num(reorder_cnxt->ptr); | ||
211 | if (start_win >= 0) { | ||
212 | dev_dbg(reorder_cnxt->priv->adapter->dev, | ||
213 | "info: flush data %d\n", start_win); | ||
214 | mwifiex_11n_dispatch_pkt_until_start_win(reorder_cnxt->priv, | ||
215 | reorder_cnxt->ptr, | ||
216 | ((reorder_cnxt->ptr->start_win + | ||
217 | start_win + 1) & (MAX_TID_VALUE - 1))); | ||
218 | } | ||
219 | } | ||
220 | |||
221 | /* | ||
222 | * This function creates an entry in Rx reordering table for the | ||
223 | * given TA/TID. | ||
224 | * | ||
225 | * The function also initializes the entry with sequence number, window | ||
226 | * size as well as initializes the timer. | ||
227 | * | ||
228 | * If the received TA/TID pair is already present, all the packets are | ||
229 | * dispatched and the window size is moved until the SSN. | ||
230 | */ | ||
231 | static void | ||
232 | mwifiex_11n_create_rx_reorder_tbl(struct mwifiex_private *priv, u8 *ta, | ||
233 | int tid, int win_size, int seq_num) | ||
234 | { | ||
235 | int i; | ||
236 | struct mwifiex_rx_reorder_tbl *rx_reor_tbl_ptr, *new_node; | ||
237 | u16 last_seq = 0; | ||
238 | unsigned long flags; | ||
239 | |||
240 | /* | ||
241 | * If we get a TID, ta pair which is already present dispatch all the | ||
242 | * the packets and move the window size until the ssn | ||
243 | */ | ||
244 | rx_reor_tbl_ptr = mwifiex_11n_get_rx_reorder_tbl(priv, tid, ta); | ||
245 | if (rx_reor_tbl_ptr) { | ||
246 | mwifiex_11n_dispatch_pkt_until_start_win(priv, rx_reor_tbl_ptr, | ||
247 | seq_num); | ||
248 | return; | ||
249 | } | ||
250 | /* if !rx_reor_tbl_ptr then create one */ | ||
251 | new_node = kzalloc(sizeof(struct mwifiex_rx_reorder_tbl), GFP_KERNEL); | ||
252 | if (!new_node) { | ||
253 | dev_err(priv->adapter->dev, "%s: failed to alloc new_node\n", | ||
254 | __func__); | ||
255 | return; | ||
256 | } | ||
257 | |||
258 | INIT_LIST_HEAD(&new_node->list); | ||
259 | new_node->tid = tid; | ||
260 | memcpy(new_node->ta, ta, ETH_ALEN); | ||
261 | new_node->start_win = seq_num; | ||
262 | if (mwifiex_queuing_ra_based(priv)) | ||
263 | /* TODO for adhoc */ | ||
264 | dev_dbg(priv->adapter->dev, | ||
265 | "info: ADHOC:last_seq=%d start_win=%d\n", | ||
266 | last_seq, new_node->start_win); | ||
267 | else | ||
268 | last_seq = priv->rx_seq[tid]; | ||
269 | |||
270 | if (last_seq >= new_node->start_win) | ||
271 | new_node->start_win = last_seq + 1; | ||
272 | |||
273 | new_node->win_size = win_size; | ||
274 | |||
275 | new_node->rx_reorder_ptr = kzalloc(sizeof(void *) * win_size, | ||
276 | GFP_KERNEL); | ||
277 | if (!new_node->rx_reorder_ptr) { | ||
278 | kfree((u8 *) new_node); | ||
279 | dev_err(priv->adapter->dev, | ||
280 | "%s: failed to alloc reorder_ptr\n", __func__); | ||
281 | return; | ||
282 | } | ||
283 | |||
284 | new_node->timer_context.ptr = new_node; | ||
285 | new_node->timer_context.priv = priv; | ||
286 | |||
287 | init_timer(&new_node->timer_context.timer); | ||
288 | new_node->timer_context.timer.function = mwifiex_flush_data; | ||
289 | new_node->timer_context.timer.data = | ||
290 | (unsigned long) &new_node->timer_context; | ||
291 | |||
292 | for (i = 0; i < win_size; ++i) | ||
293 | new_node->rx_reorder_ptr[i] = NULL; | ||
294 | |||
295 | spin_lock_irqsave(&priv->rx_reorder_tbl_lock, flags); | ||
296 | list_add_tail(&new_node->list, &priv->rx_reorder_tbl_ptr); | ||
297 | spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, flags); | ||
298 | } | ||
299 | |||
300 | /* | ||
301 | * This function prepares command for adding a BA request. | ||
302 | * | ||
303 | * Preparation includes - | ||
304 | * - Setting command ID and proper size | ||
305 | * - Setting add BA request buffer | ||
306 | * - Ensuring correct endian-ness | ||
307 | */ | ||
308 | int mwifiex_cmd_11n_addba_req(struct host_cmd_ds_command *cmd, void *data_buf) | ||
309 | { | ||
310 | struct host_cmd_ds_11n_addba_req *add_ba_req = | ||
311 | (struct host_cmd_ds_11n_addba_req *) | ||
312 | &cmd->params.add_ba_req; | ||
313 | |||
314 | cmd->command = cpu_to_le16(HostCmd_CMD_11N_ADDBA_REQ); | ||
315 | cmd->size = cpu_to_le16(sizeof(*add_ba_req) + S_DS_GEN); | ||
316 | memcpy(add_ba_req, data_buf, sizeof(*add_ba_req)); | ||
317 | |||
318 | return 0; | ||
319 | } | ||
320 | |||
321 | /* | ||
322 | * This function prepares command for adding a BA response. | ||
323 | * | ||
324 | * Preparation includes - | ||
325 | * - Setting command ID and proper size | ||
326 | * - Setting add BA response buffer | ||
327 | * - Ensuring correct endian-ness | ||
328 | */ | ||
329 | int mwifiex_cmd_11n_addba_rsp_gen(struct mwifiex_private *priv, | ||
330 | struct host_cmd_ds_command *cmd, | ||
331 | void *data_buf) | ||
332 | { | ||
333 | struct host_cmd_ds_11n_addba_rsp *add_ba_rsp = | ||
334 | (struct host_cmd_ds_11n_addba_rsp *) | ||
335 | &cmd->params.add_ba_rsp; | ||
336 | struct host_cmd_ds_11n_addba_req *cmd_addba_req = | ||
337 | (struct host_cmd_ds_11n_addba_req *) data_buf; | ||
338 | u8 tid; | ||
339 | int win_size; | ||
340 | uint16_t block_ack_param_set; | ||
341 | |||
342 | cmd->command = cpu_to_le16(HostCmd_CMD_11N_ADDBA_RSP); | ||
343 | cmd->size = cpu_to_le16(sizeof(*add_ba_rsp) + S_DS_GEN); | ||
344 | |||
345 | memcpy(add_ba_rsp->peer_mac_addr, cmd_addba_req->peer_mac_addr, | ||
346 | ETH_ALEN); | ||
347 | add_ba_rsp->dialog_token = cmd_addba_req->dialog_token; | ||
348 | add_ba_rsp->block_ack_tmo = cmd_addba_req->block_ack_tmo; | ||
349 | add_ba_rsp->ssn = cmd_addba_req->ssn; | ||
350 | |||
351 | block_ack_param_set = le16_to_cpu(cmd_addba_req->block_ack_param_set); | ||
352 | tid = (block_ack_param_set & IEEE80211_ADDBA_PARAM_TID_MASK) | ||
353 | >> BLOCKACKPARAM_TID_POS; | ||
354 | add_ba_rsp->status_code = cpu_to_le16(ADDBA_RSP_STATUS_ACCEPT); | ||
355 | block_ack_param_set &= ~IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK; | ||
356 | /* We donot support AMSDU inside AMPDU, hence reset the bit */ | ||
357 | block_ack_param_set &= ~BLOCKACKPARAM_AMSDU_SUPP_MASK; | ||
358 | block_ack_param_set |= (priv->add_ba_param.rx_win_size << | ||
359 | BLOCKACKPARAM_WINSIZE_POS); | ||
360 | add_ba_rsp->block_ack_param_set = cpu_to_le16(block_ack_param_set); | ||
361 | win_size = (le16_to_cpu(add_ba_rsp->block_ack_param_set) | ||
362 | & IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK) | ||
363 | >> BLOCKACKPARAM_WINSIZE_POS; | ||
364 | cmd_addba_req->block_ack_param_set = cpu_to_le16(block_ack_param_set); | ||
365 | |||
366 | mwifiex_11n_create_rx_reorder_tbl(priv, cmd_addba_req->peer_mac_addr, | ||
367 | tid, win_size, le16_to_cpu(cmd_addba_req->ssn)); | ||
368 | return 0; | ||
369 | } | ||
370 | |||
371 | /* | ||
372 | * This function prepares command for deleting a BA request. | ||
373 | * | ||
374 | * Preparation includes - | ||
375 | * - Setting command ID and proper size | ||
376 | * - Setting del BA request buffer | ||
377 | * - Ensuring correct endian-ness | ||
378 | */ | ||
379 | int mwifiex_cmd_11n_delba(struct host_cmd_ds_command *cmd, void *data_buf) | ||
380 | { | ||
381 | struct host_cmd_ds_11n_delba *del_ba = (struct host_cmd_ds_11n_delba *) | ||
382 | &cmd->params.del_ba; | ||
383 | |||
384 | cmd->command = cpu_to_le16(HostCmd_CMD_11N_DELBA); | ||
385 | cmd->size = cpu_to_le16(sizeof(*del_ba) + S_DS_GEN); | ||
386 | memcpy(del_ba, data_buf, sizeof(*del_ba)); | ||
387 | |||
388 | return 0; | ||
389 | } | ||
390 | |||
391 | /* | ||
392 | * This function identifies if Rx reordering is needed for a received packet. | ||
393 | * | ||
394 | * In case reordering is required, the function will do the reordering | ||
395 | * before sending it to kernel. | ||
396 | * | ||
397 | * The Rx reorder table is checked first with the received TID/TA pair. If | ||
398 | * not found, the received packet is dispatched immediately. But if found, | ||
399 | * the packet is reordered and all the packets in the updated Rx reordering | ||
400 | * table is dispatched until a hole is found. | ||
401 | * | ||
402 | * For sequence number less than the starting window, the packet is dropped. | ||
403 | */ | ||
404 | int mwifiex_11n_rx_reorder_pkt(struct mwifiex_private *priv, | ||
405 | u16 seq_num, u16 tid, | ||
406 | u8 *ta, u8 pkt_type, void *payload) | ||
407 | { | ||
408 | struct mwifiex_rx_reorder_tbl *rx_reor_tbl_ptr; | ||
409 | int start_win, end_win, win_size, ret; | ||
410 | u16 pkt_index; | ||
411 | |||
412 | rx_reor_tbl_ptr = | ||
413 | mwifiex_11n_get_rx_reorder_tbl((struct mwifiex_private *) priv, | ||
414 | tid, ta); | ||
415 | if (!rx_reor_tbl_ptr) { | ||
416 | if (pkt_type != PKT_TYPE_BAR) | ||
417 | mwifiex_process_rx_packet(priv->adapter, payload); | ||
418 | return 0; | ||
419 | } | ||
420 | start_win = rx_reor_tbl_ptr->start_win; | ||
421 | win_size = rx_reor_tbl_ptr->win_size; | ||
422 | end_win = ((start_win + win_size) - 1) & (MAX_TID_VALUE - 1); | ||
423 | del_timer(&rx_reor_tbl_ptr->timer_context.timer); | ||
424 | mod_timer(&rx_reor_tbl_ptr->timer_context.timer, jiffies | ||
425 | + (MIN_FLUSH_TIMER_MS * win_size * HZ) / 1000); | ||
426 | |||
427 | /* | ||
428 | * If seq_num is less then starting win then ignore and drop the | ||
429 | * packet | ||
430 | */ | ||
431 | if ((start_win + TWOPOW11) > (MAX_TID_VALUE - 1)) {/* Wrap */ | ||
432 | if (seq_num >= ((start_win + (TWOPOW11)) & (MAX_TID_VALUE - 1)) | ||
433 | && (seq_num < start_win)) | ||
434 | return -1; | ||
435 | } else if ((seq_num < start_win) | ||
436 | || (seq_num > (start_win + (TWOPOW11)))) { | ||
437 | return -1; | ||
438 | } | ||
439 | |||
440 | /* | ||
441 | * If this packet is a BAR we adjust seq_num as | ||
442 | * WinStart = seq_num | ||
443 | */ | ||
444 | if (pkt_type == PKT_TYPE_BAR) | ||
445 | seq_num = ((seq_num + win_size) - 1) & (MAX_TID_VALUE - 1); | ||
446 | |||
447 | if (((end_win < start_win) | ||
448 | && (seq_num < (TWOPOW11 - (MAX_TID_VALUE - start_win))) | ||
449 | && (seq_num > end_win)) || ((end_win > start_win) | ||
450 | && ((seq_num > end_win) || (seq_num < start_win)))) { | ||
451 | end_win = seq_num; | ||
452 | if (((seq_num - win_size) + 1) >= 0) | ||
453 | start_win = (end_win - win_size) + 1; | ||
454 | else | ||
455 | start_win = (MAX_TID_VALUE - (win_size - seq_num)) + 1; | ||
456 | ret = mwifiex_11n_dispatch_pkt_until_start_win(priv, | ||
457 | rx_reor_tbl_ptr, start_win); | ||
458 | |||
459 | if (ret) | ||
460 | return ret; | ||
461 | } | ||
462 | |||
463 | if (pkt_type != PKT_TYPE_BAR) { | ||
464 | if (seq_num >= start_win) | ||
465 | pkt_index = seq_num - start_win; | ||
466 | else | ||
467 | pkt_index = (seq_num+MAX_TID_VALUE) - start_win; | ||
468 | |||
469 | if (rx_reor_tbl_ptr->rx_reorder_ptr[pkt_index]) | ||
470 | return -1; | ||
471 | |||
472 | rx_reor_tbl_ptr->rx_reorder_ptr[pkt_index] = payload; | ||
473 | } | ||
474 | |||
475 | /* | ||
476 | * Dispatch all packets sequentially from start_win until a | ||
477 | * hole is found and adjust the start_win appropriately | ||
478 | */ | ||
479 | ret = mwifiex_11n_scan_and_dispatch(priv, rx_reor_tbl_ptr); | ||
480 | |||
481 | return ret; | ||
482 | } | ||
483 | |||
484 | /* | ||
485 | * This function deletes an entry for a given TID/TA pair. | ||
486 | * | ||
487 | * The TID/TA are taken from del BA event body. | ||
488 | */ | ||
489 | void | ||
490 | mwifiex_11n_delete_ba_stream_tbl(struct mwifiex_private *priv, int tid, | ||
491 | u8 *peer_mac, u8 type, int initiator) | ||
492 | { | ||
493 | struct mwifiex_rx_reorder_tbl *rx_reor_tbl_ptr; | ||
494 | struct mwifiex_tx_ba_stream_tbl *ptx_tbl; | ||
495 | u8 cleanup_rx_reorder_tbl; | ||
496 | unsigned long flags; | ||
497 | |||
498 | if (type == TYPE_DELBA_RECEIVE) | ||
499 | cleanup_rx_reorder_tbl = (initiator) ? true : false; | ||
500 | else | ||
501 | cleanup_rx_reorder_tbl = (initiator) ? false : true; | ||
502 | |||
503 | dev_dbg(priv->adapter->dev, "event: DELBA: %pM tid=%d, " | ||
504 | "initiator=%d\n", peer_mac, tid, initiator); | ||
505 | |||
506 | if (cleanup_rx_reorder_tbl) { | ||
507 | rx_reor_tbl_ptr = mwifiex_11n_get_rx_reorder_tbl(priv, tid, | ||
508 | peer_mac); | ||
509 | if (!rx_reor_tbl_ptr) { | ||
510 | dev_dbg(priv->adapter->dev, | ||
511 | "event: TID, TA not found in table\n"); | ||
512 | return; | ||
513 | } | ||
514 | mwifiex_11n_delete_rx_reorder_tbl_entry(priv, rx_reor_tbl_ptr); | ||
515 | } else { | ||
516 | ptx_tbl = mwifiex_11n_get_tx_ba_stream_tbl(priv, tid, peer_mac); | ||
517 | if (!ptx_tbl) { | ||
518 | dev_dbg(priv->adapter->dev, | ||
519 | "event: TID, RA not found in table\n"); | ||
520 | return; | ||
521 | } | ||
522 | |||
523 | spin_lock_irqsave(&priv->tx_ba_stream_tbl_lock, flags); | ||
524 | mwifiex_11n_delete_tx_ba_stream_tbl_entry(priv, ptx_tbl); | ||
525 | spin_unlock_irqrestore(&priv->tx_ba_stream_tbl_lock, flags); | ||
526 | } | ||
527 | } | ||
528 | |||
529 | /* | ||
530 | * This function handles the command response of an add BA response. | ||
531 | * | ||
532 | * Handling includes changing the header fields into CPU format and | ||
533 | * creating the stream, provided the add BA is accepted. | ||
534 | */ | ||
535 | int mwifiex_ret_11n_addba_resp(struct mwifiex_private *priv, | ||
536 | struct host_cmd_ds_command *resp) | ||
537 | { | ||
538 | struct host_cmd_ds_11n_addba_rsp *add_ba_rsp = | ||
539 | (struct host_cmd_ds_11n_addba_rsp *) | ||
540 | &resp->params.add_ba_rsp; | ||
541 | int tid, win_size; | ||
542 | struct mwifiex_rx_reorder_tbl *rx_reor_tbl_ptr; | ||
543 | uint16_t block_ack_param_set; | ||
544 | |||
545 | block_ack_param_set = le16_to_cpu(add_ba_rsp->block_ack_param_set); | ||
546 | |||
547 | tid = (block_ack_param_set & IEEE80211_ADDBA_PARAM_TID_MASK) | ||
548 | >> BLOCKACKPARAM_TID_POS; | ||
549 | /* | ||
550 | * Check if we had rejected the ADDBA, if yes then do not create | ||
551 | * the stream | ||
552 | */ | ||
553 | if (le16_to_cpu(add_ba_rsp->status_code) == BA_RESULT_SUCCESS) { | ||
554 | win_size = (block_ack_param_set & | ||
555 | IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK) | ||
556 | >> BLOCKACKPARAM_WINSIZE_POS; | ||
557 | |||
558 | dev_dbg(priv->adapter->dev, "cmd: ADDBA RSP: %pM" | ||
559 | " tid=%d ssn=%d win_size=%d\n", | ||
560 | add_ba_rsp->peer_mac_addr, | ||
561 | tid, add_ba_rsp->ssn, win_size); | ||
562 | } else { | ||
563 | dev_err(priv->adapter->dev, "ADDBA RSP: failed %pM tid=%d)\n", | ||
564 | add_ba_rsp->peer_mac_addr, tid); | ||
565 | |||
566 | rx_reor_tbl_ptr = mwifiex_11n_get_rx_reorder_tbl(priv, | ||
567 | tid, add_ba_rsp->peer_mac_addr); | ||
568 | if (rx_reor_tbl_ptr) | ||
569 | mwifiex_11n_delete_rx_reorder_tbl_entry(priv, | ||
570 | rx_reor_tbl_ptr); | ||
571 | } | ||
572 | |||
573 | return 0; | ||
574 | } | ||
575 | |||
576 | /* | ||
577 | * This function handles BA stream timeout event by preparing and sending | ||
578 | * a command to the firmware. | ||
579 | */ | ||
580 | void mwifiex_11n_ba_stream_timeout(struct mwifiex_private *priv, | ||
581 | struct host_cmd_ds_11n_batimeout *event) | ||
582 | { | ||
583 | struct host_cmd_ds_11n_delba delba; | ||
584 | |||
585 | memset(&delba, 0, sizeof(struct host_cmd_ds_11n_delba)); | ||
586 | memcpy(delba.peer_mac_addr, event->peer_mac_addr, ETH_ALEN); | ||
587 | |||
588 | delba.del_ba_param_set |= | ||
589 | cpu_to_le16((u16) event->tid << DELBA_TID_POS); | ||
590 | delba.del_ba_param_set |= cpu_to_le16( | ||
591 | (u16) event->origninator << DELBA_INITIATOR_POS); | ||
592 | delba.reason_code = cpu_to_le16(WLAN_REASON_QSTA_TIMEOUT); | ||
593 | mwifiex_send_cmd_async(priv, HostCmd_CMD_11N_DELBA, 0, 0, &delba); | ||
594 | } | ||
595 | |||
596 | /* | ||
597 | * This function cleans up the Rx reorder table by deleting all the entries | ||
598 | * and re-initializing. | ||
599 | */ | ||
600 | void mwifiex_11n_cleanup_reorder_tbl(struct mwifiex_private *priv) | ||
601 | { | ||
602 | struct mwifiex_rx_reorder_tbl *del_tbl_ptr, *tmp_node; | ||
603 | unsigned long flags; | ||
604 | |||
605 | spin_lock_irqsave(&priv->rx_reorder_tbl_lock, flags); | ||
606 | list_for_each_entry_safe(del_tbl_ptr, tmp_node, | ||
607 | &priv->rx_reorder_tbl_ptr, list) { | ||
608 | spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, flags); | ||
609 | mwifiex_11n_delete_rx_reorder_tbl_entry(priv, del_tbl_ptr); | ||
610 | spin_lock_irqsave(&priv->rx_reorder_tbl_lock, flags); | ||
611 | } | ||
612 | spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, flags); | ||
613 | |||
614 | INIT_LIST_HEAD(&priv->rx_reorder_tbl_ptr); | ||
615 | memset(priv->rx_seq, 0, sizeof(priv->rx_seq)); | ||
616 | } | ||
diff --git a/drivers/net/wireless/mwifiex/11n_rxreorder.h b/drivers/net/wireless/mwifiex/11n_rxreorder.h new file mode 100644 index 000000000000..f3ca8c8c18f9 --- /dev/null +++ b/drivers/net/wireless/mwifiex/11n_rxreorder.h | |||
@@ -0,0 +1,65 @@ | |||
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 host_cmd_ds_command *cmd, | ||
53 | void *data_buf); | ||
54 | int mwifiex_cmd_11n_addba_rsp_gen(struct mwifiex_private *priv, | ||
55 | struct host_cmd_ds_command | ||
56 | *cmd, void *data_buf); | ||
57 | int mwifiex_cmd_11n_addba_req(struct host_cmd_ds_command *cmd, | ||
58 | void *data_buf); | ||
59 | void mwifiex_11n_cleanup_reorder_tbl(struct mwifiex_private *priv); | ||
60 | struct mwifiex_rx_reorder_tbl *mwifiex_11n_get_rxreorder_tbl(struct | ||
61 | mwifiex_private | ||
62 | *priv, int tid, | ||
63 | u8 *ta); | ||
64 | |||
65 | #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..b55badef4660 --- /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 | cmd_pending = <number of cmd 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..687c1f223497 --- /dev/null +++ b/drivers/net/wireless/mwifiex/cfg80211.c | |||
@@ -0,0 +1,1419 @@ | |||
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 | switch (channel_type) { | ||
38 | case NL80211_CHAN_NO_HT: | ||
39 | case NL80211_CHAN_HT20: | ||
40 | return NO_SEC_CHANNEL; | ||
41 | case NL80211_CHAN_HT40PLUS: | ||
42 | return SEC_CHANNEL_ABOVE; | ||
43 | case NL80211_CHAN_HT40MINUS: | ||
44 | return SEC_CHANNEL_BELOW; | ||
45 | default: | ||
46 | return NO_SEC_CHANNEL; | ||
47 | } | ||
48 | } | ||
49 | |||
50 | /* | ||
51 | * This function maps the driver channel type into nl802.11 channel type. | ||
52 | * | ||
53 | * The mapping is as follows - | ||
54 | * NO_SEC_CHANNEL -> NL80211_CHAN_HT20 | ||
55 | * SEC_CHANNEL_ABOVE -> NL80211_CHAN_HT40PLUS | ||
56 | * SEC_CHANNEL_BELOW -> NL80211_CHAN_HT40MINUS | ||
57 | * Others -> NL80211_CHAN_HT20 | ||
58 | */ | ||
59 | static enum nl80211_channel_type | ||
60 | mwifiex_channels_to_cfg80211_channel_type(int channel_type) | ||
61 | { | ||
62 | switch (channel_type) { | ||
63 | case NO_SEC_CHANNEL: | ||
64 | return NL80211_CHAN_HT20; | ||
65 | case SEC_CHANNEL_ABOVE: | ||
66 | return NL80211_CHAN_HT40PLUS; | ||
67 | case SEC_CHANNEL_BELOW: | ||
68 | return NL80211_CHAN_HT40MINUS; | ||
69 | default: | ||
70 | return NL80211_CHAN_HT20; | ||
71 | } | ||
72 | } | ||
73 | |||
74 | /* | ||
75 | * This function checks whether WEP is set. | ||
76 | */ | ||
77 | static int | ||
78 | mwifiex_is_alg_wep(u32 cipher) | ||
79 | { | ||
80 | switch (cipher) { | ||
81 | case WLAN_CIPHER_SUITE_WEP40: | ||
82 | case WLAN_CIPHER_SUITE_WEP104: | ||
83 | return 1; | ||
84 | default: | ||
85 | break; | ||
86 | } | ||
87 | |||
88 | return 0; | ||
89 | } | ||
90 | |||
91 | /* | ||
92 | * This function retrieves the private structure from kernel wiphy structure. | ||
93 | */ | ||
94 | static void *mwifiex_cfg80211_get_priv(struct wiphy *wiphy) | ||
95 | { | ||
96 | return (void *) (*(unsigned long *) wiphy_priv(wiphy)); | ||
97 | } | ||
98 | |||
99 | /* | ||
100 | * CFG802.11 operation handler to delete a network key. | ||
101 | */ | ||
102 | static int | ||
103 | mwifiex_cfg80211_del_key(struct wiphy *wiphy, struct net_device *netdev, | ||
104 | u8 key_index, bool pairwise, const u8 *mac_addr) | ||
105 | { | ||
106 | struct mwifiex_private *priv = mwifiex_cfg80211_get_priv(wiphy); | ||
107 | |||
108 | if (mwifiex_set_encode(priv, NULL, 0, key_index, 1)) { | ||
109 | wiphy_err(wiphy, "deleting the crypto keys\n"); | ||
110 | return -EFAULT; | ||
111 | } | ||
112 | |||
113 | wiphy_dbg(wiphy, "info: crypto keys deleted\n"); | ||
114 | return 0; | ||
115 | } | ||
116 | |||
117 | /* | ||
118 | * CFG802.11 operation handler to set Tx power. | ||
119 | */ | ||
120 | static int | ||
121 | mwifiex_cfg80211_set_tx_power(struct wiphy *wiphy, | ||
122 | enum nl80211_tx_power_setting type, | ||
123 | int dbm) | ||
124 | { | ||
125 | struct mwifiex_private *priv = mwifiex_cfg80211_get_priv(wiphy); | ||
126 | struct mwifiex_power_cfg power_cfg; | ||
127 | |||
128 | if (type == NL80211_TX_POWER_FIXED) { | ||
129 | power_cfg.is_power_auto = 0; | ||
130 | power_cfg.power_level = dbm; | ||
131 | } else { | ||
132 | power_cfg.is_power_auto = 1; | ||
133 | } | ||
134 | |||
135 | return mwifiex_set_tx_power(priv, &power_cfg); | ||
136 | } | ||
137 | |||
138 | /* | ||
139 | * CFG802.11 operation handler to set Power Save option. | ||
140 | * | ||
141 | * The timeout value, if provided, is currently ignored. | ||
142 | */ | ||
143 | static int | ||
144 | mwifiex_cfg80211_set_power_mgmt(struct wiphy *wiphy, | ||
145 | struct net_device *dev, | ||
146 | bool enabled, int timeout) | ||
147 | { | ||
148 | struct mwifiex_private *priv = mwifiex_cfg80211_get_priv(wiphy); | ||
149 | u32 ps_mode; | ||
150 | |||
151 | if (timeout) | ||
152 | wiphy_dbg(wiphy, | ||
153 | "info: ignoring the timeout value" | ||
154 | " for IEEE power save\n"); | ||
155 | |||
156 | ps_mode = enabled; | ||
157 | |||
158 | return mwifiex_drv_set_power(priv, &ps_mode); | ||
159 | } | ||
160 | |||
161 | /* | ||
162 | * CFG802.11 operation handler to set the default network key. | ||
163 | */ | ||
164 | static int | ||
165 | mwifiex_cfg80211_set_default_key(struct wiphy *wiphy, struct net_device *netdev, | ||
166 | u8 key_index, bool unicast, | ||
167 | bool multicast) | ||
168 | { | ||
169 | struct mwifiex_private *priv = mwifiex_cfg80211_get_priv(wiphy); | ||
170 | |||
171 | /* Return if WEP key not configured */ | ||
172 | if (priv->sec_info.wep_status == MWIFIEX_802_11_WEP_DISABLED) | ||
173 | return 0; | ||
174 | |||
175 | if (mwifiex_set_encode(priv, NULL, 0, key_index, 0)) { | ||
176 | wiphy_err(wiphy, "set default Tx key index\n"); | ||
177 | return -EFAULT; | ||
178 | } | ||
179 | |||
180 | return 0; | ||
181 | } | ||
182 | |||
183 | /* | ||
184 | * CFG802.11 operation handler to add a network key. | ||
185 | */ | ||
186 | static int | ||
187 | mwifiex_cfg80211_add_key(struct wiphy *wiphy, struct net_device *netdev, | ||
188 | u8 key_index, bool pairwise, const u8 *mac_addr, | ||
189 | struct key_params *params) | ||
190 | { | ||
191 | struct mwifiex_private *priv = mwifiex_cfg80211_get_priv(wiphy); | ||
192 | |||
193 | if (mwifiex_set_encode(priv, params->key, params->key_len, | ||
194 | key_index, 0)) { | ||
195 | wiphy_err(wiphy, "crypto keys added\n"); | ||
196 | return -EFAULT; | ||
197 | } | ||
198 | |||
199 | return 0; | ||
200 | } | ||
201 | |||
202 | /* | ||
203 | * This function sends domain information to the firmware. | ||
204 | * | ||
205 | * The following information are passed to the firmware - | ||
206 | * - Country codes | ||
207 | * - Sub bands (first channel, number of channels, maximum Tx power) | ||
208 | */ | ||
209 | static int mwifiex_send_domain_info_cmd_fw(struct wiphy *wiphy) | ||
210 | { | ||
211 | u8 no_of_triplet = 0; | ||
212 | struct ieee80211_country_ie_triplet *t; | ||
213 | u8 no_of_parsed_chan = 0; | ||
214 | u8 first_chan = 0, next_chan = 0, max_pwr = 0; | ||
215 | u8 i, flag = 0; | ||
216 | enum ieee80211_band band; | ||
217 | struct ieee80211_supported_band *sband; | ||
218 | struct ieee80211_channel *ch; | ||
219 | struct mwifiex_private *priv = mwifiex_cfg80211_get_priv(wiphy); | ||
220 | struct mwifiex_adapter *adapter = priv->adapter; | ||
221 | struct mwifiex_802_11d_domain_reg *domain_info = &adapter->domain_reg; | ||
222 | |||
223 | /* Set country code */ | ||
224 | domain_info->country_code[0] = priv->country_code[0]; | ||
225 | domain_info->country_code[1] = priv->country_code[1]; | ||
226 | domain_info->country_code[2] = ' '; | ||
227 | |||
228 | band = mwifiex_band_to_radio_type(adapter->config_bands); | ||
229 | if (!wiphy->bands[band]) { | ||
230 | wiphy_err(wiphy, "11D: setting domain info in FW\n"); | ||
231 | return -1; | ||
232 | } | ||
233 | |||
234 | sband = wiphy->bands[band]; | ||
235 | |||
236 | for (i = 0; i < sband->n_channels ; i++) { | ||
237 | ch = &sband->channels[i]; | ||
238 | if (ch->flags & IEEE80211_CHAN_DISABLED) | ||
239 | continue; | ||
240 | |||
241 | if (!flag) { | ||
242 | flag = 1; | ||
243 | first_chan = (u32) ch->hw_value; | ||
244 | next_chan = first_chan; | ||
245 | max_pwr = ch->max_power; | ||
246 | no_of_parsed_chan = 1; | ||
247 | continue; | ||
248 | } | ||
249 | |||
250 | if (ch->hw_value == next_chan + 1 && | ||
251 | ch->max_power == max_pwr) { | ||
252 | next_chan++; | ||
253 | no_of_parsed_chan++; | ||
254 | } else { | ||
255 | t = &domain_info->triplet[no_of_triplet]; | ||
256 | t->chans.first_channel = first_chan; | ||
257 | t->chans.num_channels = no_of_parsed_chan; | ||
258 | t->chans.max_power = max_pwr; | ||
259 | no_of_triplet++; | ||
260 | first_chan = (u32) ch->hw_value; | ||
261 | next_chan = first_chan; | ||
262 | max_pwr = ch->max_power; | ||
263 | no_of_parsed_chan = 1; | ||
264 | } | ||
265 | } | ||
266 | |||
267 | if (flag) { | ||
268 | t = &domain_info->triplet[no_of_triplet]; | ||
269 | t->chans.first_channel = first_chan; | ||
270 | t->chans.num_channels = no_of_parsed_chan; | ||
271 | t->chans.max_power = max_pwr; | ||
272 | no_of_triplet++; | ||
273 | } | ||
274 | |||
275 | domain_info->no_of_triplet = no_of_triplet; | ||
276 | |||
277 | if (mwifiex_send_cmd_async(priv, HostCmd_CMD_802_11D_DOMAIN_INFO, | ||
278 | HostCmd_ACT_GEN_SET, 0, NULL)) { | ||
279 | wiphy_err(wiphy, "11D: setting domain info in FW\n"); | ||
280 | return -1; | ||
281 | } | ||
282 | |||
283 | return 0; | ||
284 | } | ||
285 | |||
286 | /* | ||
287 | * CFG802.11 regulatory domain callback function. | ||
288 | * | ||
289 | * This function is called when the regulatory domain is changed due to the | ||
290 | * following reasons - | ||
291 | * - Set by driver | ||
292 | * - Set by system core | ||
293 | * - Set by user | ||
294 | * - Set bt Country IE | ||
295 | */ | ||
296 | static int mwifiex_reg_notifier(struct wiphy *wiphy, | ||
297 | struct regulatory_request *request) | ||
298 | { | ||
299 | struct mwifiex_private *priv = mwifiex_cfg80211_get_priv(wiphy); | ||
300 | |||
301 | wiphy_dbg(wiphy, "info: cfg80211 regulatory domain callback for domain" | ||
302 | " %c%c\n", request->alpha2[0], request->alpha2[1]); | ||
303 | |||
304 | memcpy(priv->country_code, request->alpha2, sizeof(request->alpha2)); | ||
305 | |||
306 | switch (request->initiator) { | ||
307 | case NL80211_REGDOM_SET_BY_DRIVER: | ||
308 | case NL80211_REGDOM_SET_BY_CORE: | ||
309 | case NL80211_REGDOM_SET_BY_USER: | ||
310 | break; | ||
311 | /* Todo: apply driver specific changes in channel flags based | ||
312 | on the request initiator if necessary. */ | ||
313 | case NL80211_REGDOM_SET_BY_COUNTRY_IE: | ||
314 | break; | ||
315 | } | ||
316 | mwifiex_send_domain_info_cmd_fw(wiphy); | ||
317 | |||
318 | return 0; | ||
319 | } | ||
320 | |||
321 | /* | ||
322 | * This function sets the RF channel. | ||
323 | * | ||
324 | * This function creates multiple IOCTL requests, populates them accordingly | ||
325 | * and issues them to set the band/channel and frequency. | ||
326 | */ | ||
327 | static int | ||
328 | mwifiex_set_rf_channel(struct mwifiex_private *priv, | ||
329 | struct ieee80211_channel *chan, | ||
330 | enum nl80211_channel_type channel_type) | ||
331 | { | ||
332 | struct mwifiex_chan_freq_power cfp; | ||
333 | struct mwifiex_ds_band_cfg band_cfg; | ||
334 | u32 config_bands = 0; | ||
335 | struct wiphy *wiphy = priv->wdev->wiphy; | ||
336 | |||
337 | if (chan) { | ||
338 | memset(&band_cfg, 0, sizeof(band_cfg)); | ||
339 | /* Set appropriate bands */ | ||
340 | if (chan->band == IEEE80211_BAND_2GHZ) | ||
341 | config_bands = BAND_B | BAND_G | BAND_GN; | ||
342 | else | ||
343 | config_bands = BAND_AN | BAND_A; | ||
344 | if (priv->bss_mode == NL80211_IFTYPE_STATION | ||
345 | || priv->bss_mode == NL80211_IFTYPE_UNSPECIFIED) { | ||
346 | band_cfg.config_bands = config_bands; | ||
347 | } else if (priv->bss_mode == NL80211_IFTYPE_ADHOC) { | ||
348 | band_cfg.config_bands = config_bands; | ||
349 | band_cfg.adhoc_start_band = config_bands; | ||
350 | } | ||
351 | |||
352 | band_cfg.sec_chan_offset = | ||
353 | mwifiex_cfg80211_channel_type_to_mwifiex_channels | ||
354 | (channel_type); | ||
355 | |||
356 | if (mwifiex_set_radio_band_cfg(priv, &band_cfg)) | ||
357 | return -EFAULT; | ||
358 | |||
359 | mwifiex_send_domain_info_cmd_fw(wiphy); | ||
360 | } | ||
361 | |||
362 | wiphy_dbg(wiphy, "info: setting band %d, channel offset %d and " | ||
363 | "mode %d\n", config_bands, band_cfg.sec_chan_offset, | ||
364 | priv->bss_mode); | ||
365 | if (!chan) | ||
366 | return 0; | ||
367 | |||
368 | memset(&cfp, 0, sizeof(cfp)); | ||
369 | cfp.freq = chan->center_freq; | ||
370 | cfp.channel = ieee80211_frequency_to_channel(chan->center_freq); | ||
371 | |||
372 | if (mwifiex_bss_set_channel(priv, &cfp)) | ||
373 | return -EFAULT; | ||
374 | |||
375 | return mwifiex_drv_change_adhoc_chan(priv, cfp.channel); | ||
376 | } | ||
377 | |||
378 | /* | ||
379 | * CFG802.11 operation handler to set channel. | ||
380 | * | ||
381 | * This function can only be used when station is not connected. | ||
382 | */ | ||
383 | static int | ||
384 | mwifiex_cfg80211_set_channel(struct wiphy *wiphy, struct net_device *dev, | ||
385 | struct ieee80211_channel *chan, | ||
386 | enum nl80211_channel_type channel_type) | ||
387 | { | ||
388 | struct mwifiex_private *priv = mwifiex_cfg80211_get_priv(wiphy); | ||
389 | |||
390 | if (priv->media_connected) { | ||
391 | wiphy_err(wiphy, "This setting is valid only when station " | ||
392 | "is not connected\n"); | ||
393 | return -EINVAL; | ||
394 | } | ||
395 | |||
396 | return mwifiex_set_rf_channel(priv, chan, channel_type); | ||
397 | } | ||
398 | |||
399 | /* | ||
400 | * This function sets the fragmentation threshold. | ||
401 | * | ||
402 | * The fragmentation threshold value must lie between MWIFIEX_FRAG_MIN_VALUE | ||
403 | * and MWIFIEX_FRAG_MAX_VALUE. | ||
404 | */ | ||
405 | static int | ||
406 | mwifiex_set_frag(struct mwifiex_private *priv, u32 frag_thr) | ||
407 | { | ||
408 | int ret; | ||
409 | |||
410 | if (frag_thr < MWIFIEX_FRAG_MIN_VALUE | ||
411 | || frag_thr > MWIFIEX_FRAG_MAX_VALUE) | ||
412 | return -EINVAL; | ||
413 | |||
414 | /* Send request to firmware */ | ||
415 | ret = mwifiex_send_cmd_sync(priv, HostCmd_CMD_802_11_SNMP_MIB, | ||
416 | HostCmd_ACT_GEN_SET, FRAG_THRESH_I, | ||
417 | &frag_thr); | ||
418 | |||
419 | return ret; | ||
420 | } | ||
421 | |||
422 | /* | ||
423 | * This function sets the RTS threshold. | ||
424 | |||
425 | * The rts value must lie between MWIFIEX_RTS_MIN_VALUE | ||
426 | * and MWIFIEX_RTS_MAX_VALUE. | ||
427 | */ | ||
428 | static int | ||
429 | mwifiex_set_rts(struct mwifiex_private *priv, u32 rts_thr) | ||
430 | { | ||
431 | if (rts_thr < MWIFIEX_RTS_MIN_VALUE || rts_thr > MWIFIEX_RTS_MAX_VALUE) | ||
432 | rts_thr = MWIFIEX_RTS_MAX_VALUE; | ||
433 | |||
434 | return mwifiex_send_cmd_sync(priv, HostCmd_CMD_802_11_SNMP_MIB, | ||
435 | HostCmd_ACT_GEN_SET, RTS_THRESH_I, | ||
436 | &rts_thr); | ||
437 | } | ||
438 | |||
439 | /* | ||
440 | * CFG802.11 operation handler to set wiphy parameters. | ||
441 | * | ||
442 | * This function can be used to set the RTS threshold and the | ||
443 | * Fragmentation threshold of the driver. | ||
444 | */ | ||
445 | static int | ||
446 | mwifiex_cfg80211_set_wiphy_params(struct wiphy *wiphy, u32 changed) | ||
447 | { | ||
448 | struct mwifiex_private *priv = mwifiex_cfg80211_get_priv(wiphy); | ||
449 | int ret = 0; | ||
450 | |||
451 | if (changed & WIPHY_PARAM_RTS_THRESHOLD) { | ||
452 | ret = mwifiex_set_rts(priv, wiphy->rts_threshold); | ||
453 | if (ret) | ||
454 | return ret; | ||
455 | } | ||
456 | |||
457 | if (changed & WIPHY_PARAM_FRAG_THRESHOLD) | ||
458 | ret = mwifiex_set_frag(priv, wiphy->frag_threshold); | ||
459 | |||
460 | return ret; | ||
461 | } | ||
462 | |||
463 | /* | ||
464 | * CFG802.11 operation handler to change interface type. | ||
465 | */ | ||
466 | static int | ||
467 | mwifiex_cfg80211_change_virtual_intf(struct wiphy *wiphy, | ||
468 | struct net_device *dev, | ||
469 | enum nl80211_iftype type, u32 *flags, | ||
470 | struct vif_params *params) | ||
471 | { | ||
472 | int ret; | ||
473 | struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); | ||
474 | |||
475 | if (priv->bss_mode == type) { | ||
476 | wiphy_warn(wiphy, "already set to required type\n"); | ||
477 | return 0; | ||
478 | } | ||
479 | |||
480 | priv->bss_mode = type; | ||
481 | |||
482 | switch (type) { | ||
483 | case NL80211_IFTYPE_ADHOC: | ||
484 | dev->ieee80211_ptr->iftype = NL80211_IFTYPE_ADHOC; | ||
485 | wiphy_dbg(wiphy, "info: setting interface type to adhoc\n"); | ||
486 | break; | ||
487 | case NL80211_IFTYPE_STATION: | ||
488 | dev->ieee80211_ptr->iftype = NL80211_IFTYPE_STATION; | ||
489 | wiphy_dbg(wiphy, "info: setting interface type to managed\n"); | ||
490 | break; | ||
491 | case NL80211_IFTYPE_UNSPECIFIED: | ||
492 | dev->ieee80211_ptr->iftype = NL80211_IFTYPE_STATION; | ||
493 | wiphy_dbg(wiphy, "info: setting interface type to auto\n"); | ||
494 | return 0; | ||
495 | default: | ||
496 | wiphy_err(wiphy, "unknown interface type: %d\n", type); | ||
497 | return -EINVAL; | ||
498 | } | ||
499 | |||
500 | mwifiex_deauthenticate(priv, NULL); | ||
501 | |||
502 | priv->sec_info.authentication_mode = NL80211_AUTHTYPE_OPEN_SYSTEM; | ||
503 | |||
504 | ret = mwifiex_send_cmd_sync(priv, HostCmd_CMD_SET_BSS_MODE, | ||
505 | HostCmd_ACT_GEN_SET, 0, NULL); | ||
506 | |||
507 | return ret; | ||
508 | } | ||
509 | |||
510 | /* | ||
511 | * This function dumps the station information on a buffer. | ||
512 | * | ||
513 | * The following information are shown - | ||
514 | * - Total bytes transmitted | ||
515 | * - Total bytes received | ||
516 | * - Total packets transmitted | ||
517 | * - Total packets received | ||
518 | * - Signal quality level | ||
519 | * - Transmission rate | ||
520 | */ | ||
521 | static int | ||
522 | mwifiex_dump_station_info(struct mwifiex_private *priv, | ||
523 | struct station_info *sinfo) | ||
524 | { | ||
525 | struct mwifiex_ds_get_signal signal; | ||
526 | struct mwifiex_rate_cfg rate; | ||
527 | int ret = 0; | ||
528 | |||
529 | sinfo->filled = STATION_INFO_RX_BYTES | STATION_INFO_TX_BYTES | | ||
530 | STATION_INFO_RX_PACKETS | | ||
531 | STATION_INFO_TX_PACKETS | ||
532 | | STATION_INFO_SIGNAL | STATION_INFO_TX_BITRATE; | ||
533 | |||
534 | /* Get signal information from the firmware */ | ||
535 | memset(&signal, 0, sizeof(struct mwifiex_ds_get_signal)); | ||
536 | if (mwifiex_get_signal_info(priv, &signal)) { | ||
537 | dev_err(priv->adapter->dev, "getting signal information\n"); | ||
538 | ret = -EFAULT; | ||
539 | } | ||
540 | |||
541 | if (mwifiex_drv_get_data_rate(priv, &rate)) { | ||
542 | dev_err(priv->adapter->dev, "getting data rate\n"); | ||
543 | ret = -EFAULT; | ||
544 | } | ||
545 | |||
546 | sinfo->rx_bytes = priv->stats.rx_bytes; | ||
547 | sinfo->tx_bytes = priv->stats.tx_bytes; | ||
548 | sinfo->rx_packets = priv->stats.rx_packets; | ||
549 | sinfo->tx_packets = priv->stats.tx_packets; | ||
550 | sinfo->signal = priv->w_stats.qual.level; | ||
551 | sinfo->txrate.legacy = rate.rate; | ||
552 | |||
553 | return ret; | ||
554 | } | ||
555 | |||
556 | /* | ||
557 | * CFG802.11 operation handler to get station information. | ||
558 | * | ||
559 | * This function only works in connected mode, and dumps the | ||
560 | * requested station information, if available. | ||
561 | */ | ||
562 | static int | ||
563 | mwifiex_cfg80211_get_station(struct wiphy *wiphy, struct net_device *dev, | ||
564 | u8 *mac, struct station_info *sinfo) | ||
565 | { | ||
566 | struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); | ||
567 | |||
568 | mwifiex_dump_station_info(priv, sinfo); | ||
569 | |||
570 | if (!priv->media_connected) | ||
571 | return -ENOENT; | ||
572 | if (memcmp(mac, priv->cfg_bssid, ETH_ALEN)) | ||
573 | return -ENOENT; | ||
574 | |||
575 | return mwifiex_dump_station_info(priv, sinfo); | ||
576 | } | ||
577 | |||
578 | /* Supported rates to be advertised to the cfg80211 */ | ||
579 | |||
580 | static struct ieee80211_rate mwifiex_rates[] = { | ||
581 | {.bitrate = 10, .hw_value = 2, }, | ||
582 | {.bitrate = 20, .hw_value = 4, }, | ||
583 | {.bitrate = 55, .hw_value = 11, }, | ||
584 | {.bitrate = 110, .hw_value = 22, }, | ||
585 | {.bitrate = 220, .hw_value = 44, }, | ||
586 | {.bitrate = 60, .hw_value = 12, }, | ||
587 | {.bitrate = 90, .hw_value = 18, }, | ||
588 | {.bitrate = 120, .hw_value = 24, }, | ||
589 | {.bitrate = 180, .hw_value = 36, }, | ||
590 | {.bitrate = 240, .hw_value = 48, }, | ||
591 | {.bitrate = 360, .hw_value = 72, }, | ||
592 | {.bitrate = 480, .hw_value = 96, }, | ||
593 | {.bitrate = 540, .hw_value = 108, }, | ||
594 | {.bitrate = 720, .hw_value = 144, }, | ||
595 | }; | ||
596 | |||
597 | /* Channel definitions to be advertised to cfg80211 */ | ||
598 | |||
599 | static struct ieee80211_channel mwifiex_channels_2ghz[] = { | ||
600 | {.center_freq = 2412, .hw_value = 1, }, | ||
601 | {.center_freq = 2417, .hw_value = 2, }, | ||
602 | {.center_freq = 2422, .hw_value = 3, }, | ||
603 | {.center_freq = 2427, .hw_value = 4, }, | ||
604 | {.center_freq = 2432, .hw_value = 5, }, | ||
605 | {.center_freq = 2437, .hw_value = 6, }, | ||
606 | {.center_freq = 2442, .hw_value = 7, }, | ||
607 | {.center_freq = 2447, .hw_value = 8, }, | ||
608 | {.center_freq = 2452, .hw_value = 9, }, | ||
609 | {.center_freq = 2457, .hw_value = 10, }, | ||
610 | {.center_freq = 2462, .hw_value = 11, }, | ||
611 | {.center_freq = 2467, .hw_value = 12, }, | ||
612 | {.center_freq = 2472, .hw_value = 13, }, | ||
613 | {.center_freq = 2484, .hw_value = 14, }, | ||
614 | }; | ||
615 | |||
616 | static struct ieee80211_supported_band mwifiex_band_2ghz = { | ||
617 | .channels = mwifiex_channels_2ghz, | ||
618 | .n_channels = ARRAY_SIZE(mwifiex_channels_2ghz), | ||
619 | .bitrates = mwifiex_rates, | ||
620 | .n_bitrates = 14, | ||
621 | }; | ||
622 | |||
623 | static struct ieee80211_channel mwifiex_channels_5ghz[] = { | ||
624 | {.center_freq = 5040, .hw_value = 8, }, | ||
625 | {.center_freq = 5060, .hw_value = 12, }, | ||
626 | {.center_freq = 5080, .hw_value = 16, }, | ||
627 | {.center_freq = 5170, .hw_value = 34, }, | ||
628 | {.center_freq = 5190, .hw_value = 38, }, | ||
629 | {.center_freq = 5210, .hw_value = 42, }, | ||
630 | {.center_freq = 5230, .hw_value = 46, }, | ||
631 | {.center_freq = 5180, .hw_value = 36, }, | ||
632 | {.center_freq = 5200, .hw_value = 40, }, | ||
633 | {.center_freq = 5220, .hw_value = 44, }, | ||
634 | {.center_freq = 5240, .hw_value = 48, }, | ||
635 | {.center_freq = 5260, .hw_value = 52, }, | ||
636 | {.center_freq = 5280, .hw_value = 56, }, | ||
637 | {.center_freq = 5300, .hw_value = 60, }, | ||
638 | {.center_freq = 5320, .hw_value = 64, }, | ||
639 | {.center_freq = 5500, .hw_value = 100, }, | ||
640 | {.center_freq = 5520, .hw_value = 104, }, | ||
641 | {.center_freq = 5540, .hw_value = 108, }, | ||
642 | {.center_freq = 5560, .hw_value = 112, }, | ||
643 | {.center_freq = 5580, .hw_value = 116, }, | ||
644 | {.center_freq = 5600, .hw_value = 120, }, | ||
645 | {.center_freq = 5620, .hw_value = 124, }, | ||
646 | {.center_freq = 5640, .hw_value = 128, }, | ||
647 | {.center_freq = 5660, .hw_value = 132, }, | ||
648 | {.center_freq = 5680, .hw_value = 136, }, | ||
649 | {.center_freq = 5700, .hw_value = 140, }, | ||
650 | {.center_freq = 5745, .hw_value = 149, }, | ||
651 | {.center_freq = 5765, .hw_value = 153, }, | ||
652 | {.center_freq = 5785, .hw_value = 157, }, | ||
653 | {.center_freq = 5805, .hw_value = 161, }, | ||
654 | {.center_freq = 5825, .hw_value = 165, }, | ||
655 | }; | ||
656 | |||
657 | static struct ieee80211_supported_band mwifiex_band_5ghz = { | ||
658 | .channels = mwifiex_channels_5ghz, | ||
659 | .n_channels = ARRAY_SIZE(mwifiex_channels_5ghz), | ||
660 | .bitrates = mwifiex_rates - 4, | ||
661 | .n_bitrates = ARRAY_SIZE(mwifiex_rates) + 4, | ||
662 | }; | ||
663 | |||
664 | |||
665 | /* Supported crypto cipher suits to be advertised to cfg80211 */ | ||
666 | |||
667 | static const u32 mwifiex_cipher_suites[] = { | ||
668 | WLAN_CIPHER_SUITE_WEP40, | ||
669 | WLAN_CIPHER_SUITE_WEP104, | ||
670 | WLAN_CIPHER_SUITE_TKIP, | ||
671 | WLAN_CIPHER_SUITE_CCMP, | ||
672 | }; | ||
673 | |||
674 | /* | ||
675 | * CFG802.11 operation handler for disconnection request. | ||
676 | * | ||
677 | * This function does not work when there is already a disconnection | ||
678 | * procedure going on. | ||
679 | */ | ||
680 | static int | ||
681 | mwifiex_cfg80211_disconnect(struct wiphy *wiphy, struct net_device *dev, | ||
682 | u16 reason_code) | ||
683 | { | ||
684 | struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); | ||
685 | |||
686 | if (priv->disconnect) | ||
687 | return -EBUSY; | ||
688 | |||
689 | priv->disconnect = 1; | ||
690 | if (mwifiex_deauthenticate(priv, NULL)) | ||
691 | return -EFAULT; | ||
692 | |||
693 | wiphy_dbg(wiphy, "info: successfully disconnected from %pM:" | ||
694 | " reason code %d\n", priv->cfg_bssid, reason_code); | ||
695 | |||
696 | queue_work(priv->workqueue, &priv->cfg_workqueue); | ||
697 | |||
698 | return 0; | ||
699 | } | ||
700 | |||
701 | /* | ||
702 | * This function informs the CFG802.11 subsystem of a new IBSS. | ||
703 | * | ||
704 | * The following information are sent to the CFG802.11 subsystem | ||
705 | * to register the new IBSS. If we do not register the new IBSS, | ||
706 | * a kernel panic will result. | ||
707 | * - SSID | ||
708 | * - SSID length | ||
709 | * - BSSID | ||
710 | * - Channel | ||
711 | */ | ||
712 | static int mwifiex_cfg80211_inform_ibss_bss(struct mwifiex_private *priv) | ||
713 | { | ||
714 | struct ieee80211_channel *chan; | ||
715 | struct mwifiex_bss_info bss_info; | ||
716 | int ie_len; | ||
717 | u8 ie_buf[IEEE80211_MAX_SSID_LEN + sizeof(struct ieee_types_header)]; | ||
718 | |||
719 | if (mwifiex_get_bss_info(priv, &bss_info)) | ||
720 | return -1; | ||
721 | |||
722 | ie_buf[0] = WLAN_EID_SSID; | ||
723 | ie_buf[1] = bss_info.ssid.ssid_len; | ||
724 | |||
725 | memcpy(&ie_buf[sizeof(struct ieee_types_header)], | ||
726 | &bss_info.ssid.ssid, | ||
727 | bss_info.ssid.ssid_len); | ||
728 | ie_len = ie_buf[1] + sizeof(struct ieee_types_header); | ||
729 | |||
730 | chan = __ieee80211_get_channel(priv->wdev->wiphy, | ||
731 | ieee80211_channel_to_frequency(bss_info.bss_chan, | ||
732 | priv->curr_bss_params.band)); | ||
733 | |||
734 | cfg80211_inform_bss(priv->wdev->wiphy, chan, | ||
735 | bss_info.bssid, 0, WLAN_CAPABILITY_IBSS, | ||
736 | 0, ie_buf, ie_len, 0, GFP_KERNEL); | ||
737 | memcpy(priv->cfg_bssid, bss_info.bssid, ETH_ALEN); | ||
738 | |||
739 | return 0; | ||
740 | } | ||
741 | |||
742 | /* | ||
743 | * This function informs the CFG802.11 subsystem of a new BSS connection. | ||
744 | * | ||
745 | * The following information are sent to the CFG802.11 subsystem | ||
746 | * to register the new BSS connection. If we do not register the new BSS, | ||
747 | * a kernel panic will result. | ||
748 | * - MAC address | ||
749 | * - Capabilities | ||
750 | * - Beacon period | ||
751 | * - RSSI value | ||
752 | * - Channel | ||
753 | * - Supported rates IE | ||
754 | * - Extended capabilities IE | ||
755 | * - DS parameter set IE | ||
756 | * - HT Capability IE | ||
757 | * - Vendor Specific IE (221) | ||
758 | * - WPA IE | ||
759 | * - RSN IE | ||
760 | */ | ||
761 | static int mwifiex_inform_bss_from_scan_result(struct mwifiex_private *priv, | ||
762 | struct mwifiex_802_11_ssid *ssid) | ||
763 | { | ||
764 | struct mwifiex_bssdescriptor *scan_table; | ||
765 | int i, j; | ||
766 | struct ieee80211_channel *chan; | ||
767 | u8 *ie, *ie_buf; | ||
768 | u32 ie_len; | ||
769 | u8 *beacon; | ||
770 | int beacon_size; | ||
771 | u8 element_id, element_len; | ||
772 | |||
773 | #define MAX_IE_BUF 2048 | ||
774 | ie_buf = kzalloc(MAX_IE_BUF, GFP_KERNEL); | ||
775 | if (!ie_buf) { | ||
776 | dev_err(priv->adapter->dev, "%s: failed to alloc ie_buf\n", | ||
777 | __func__); | ||
778 | return -ENOMEM; | ||
779 | } | ||
780 | |||
781 | scan_table = priv->adapter->scan_table; | ||
782 | for (i = 0; i < priv->adapter->num_in_scan_table; i++) { | ||
783 | if (ssid) { | ||
784 | /* Inform specific BSS only */ | ||
785 | if (memcmp(ssid->ssid, scan_table[i].ssid.ssid, | ||
786 | ssid->ssid_len)) | ||
787 | continue; | ||
788 | } | ||
789 | memset(ie_buf, 0, MAX_IE_BUF); | ||
790 | ie_buf[0] = WLAN_EID_SSID; | ||
791 | ie_buf[1] = scan_table[i].ssid.ssid_len; | ||
792 | memcpy(&ie_buf[sizeof(struct ieee_types_header)], | ||
793 | scan_table[i].ssid.ssid, ie_buf[1]); | ||
794 | |||
795 | ie = ie_buf + ie_buf[1] + sizeof(struct ieee_types_header); | ||
796 | ie_len = ie_buf[1] + sizeof(struct ieee_types_header); | ||
797 | |||
798 | ie[0] = WLAN_EID_SUPP_RATES; | ||
799 | |||
800 | for (j = 0; j < sizeof(scan_table[i].supported_rates); j++) { | ||
801 | if (!scan_table[i].supported_rates[j]) | ||
802 | break; | ||
803 | else | ||
804 | ie[j + sizeof(struct ieee_types_header)] = | ||
805 | scan_table[i].supported_rates[j]; | ||
806 | } | ||
807 | |||
808 | ie[1] = j; | ||
809 | ie_len += ie[1] + sizeof(struct ieee_types_header); | ||
810 | |||
811 | beacon = scan_table[i].beacon_buf; | ||
812 | beacon_size = scan_table[i].beacon_buf_size; | ||
813 | |||
814 | /* Skip time stamp, beacon interval and capability */ | ||
815 | |||
816 | if (beacon) { | ||
817 | beacon += sizeof(scan_table[i].beacon_period) | ||
818 | + sizeof(scan_table[i].time_stamp) + | ||
819 | +sizeof(scan_table[i].cap_info_bitmap); | ||
820 | |||
821 | beacon_size -= sizeof(scan_table[i].beacon_period) | ||
822 | + sizeof(scan_table[i].time_stamp) | ||
823 | + sizeof(scan_table[i].cap_info_bitmap); | ||
824 | } | ||
825 | |||
826 | while (beacon_size >= sizeof(struct ieee_types_header)) { | ||
827 | ie = ie_buf + ie_len; | ||
828 | element_id = *beacon; | ||
829 | element_len = *(beacon + 1); | ||
830 | if (beacon_size < (int) element_len + | ||
831 | sizeof(struct ieee_types_header)) { | ||
832 | dev_err(priv->adapter->dev, "%s: in processing" | ||
833 | " IE, bytes left < IE length\n", | ||
834 | __func__); | ||
835 | break; | ||
836 | } | ||
837 | switch (element_id) { | ||
838 | case WLAN_EID_EXT_CAPABILITY: | ||
839 | case WLAN_EID_DS_PARAMS: | ||
840 | case WLAN_EID_HT_CAPABILITY: | ||
841 | case WLAN_EID_VENDOR_SPECIFIC: | ||
842 | case WLAN_EID_RSN: | ||
843 | case WLAN_EID_BSS_AC_ACCESS_DELAY: | ||
844 | ie[0] = element_id; | ||
845 | ie[1] = element_len; | ||
846 | memcpy(&ie[sizeof(struct ieee_types_header)], | ||
847 | (u8 *) beacon | ||
848 | + sizeof(struct ieee_types_header), | ||
849 | element_len); | ||
850 | ie_len += ie[1] + | ||
851 | sizeof(struct ieee_types_header); | ||
852 | break; | ||
853 | default: | ||
854 | break; | ||
855 | } | ||
856 | beacon += element_len + | ||
857 | sizeof(struct ieee_types_header); | ||
858 | beacon_size -= element_len + | ||
859 | sizeof(struct ieee_types_header); | ||
860 | } | ||
861 | chan = ieee80211_get_channel(priv->wdev->wiphy, | ||
862 | scan_table[i].freq); | ||
863 | cfg80211_inform_bss(priv->wdev->wiphy, chan, | ||
864 | scan_table[i].mac_address, | ||
865 | 0, scan_table[i].cap_info_bitmap, | ||
866 | scan_table[i].beacon_period, | ||
867 | ie_buf, ie_len, | ||
868 | scan_table[i].rssi, GFP_KERNEL); | ||
869 | } | ||
870 | |||
871 | kfree(ie_buf); | ||
872 | return 0; | ||
873 | } | ||
874 | |||
875 | /* | ||
876 | * This function connects with a BSS. | ||
877 | * | ||
878 | * This function handles both Infra and Ad-Hoc modes. It also performs | ||
879 | * validity checking on the provided parameters, disconnects from the | ||
880 | * current BSS (if any), sets up the association/scan parameters, | ||
881 | * including security settings, and performs specific SSID scan before | ||
882 | * trying to connect. | ||
883 | * | ||
884 | * For Infra mode, the function returns failure if the specified SSID | ||
885 | * is not found in scan table. However, for Ad-Hoc mode, it can create | ||
886 | * the IBSS if it does not exist. On successful completion in either case, | ||
887 | * the function notifies the CFG802.11 subsystem of the new BSS connection, | ||
888 | * otherwise the kernel will panic. | ||
889 | */ | ||
890 | static int | ||
891 | mwifiex_cfg80211_assoc(struct mwifiex_private *priv, size_t ssid_len, u8 *ssid, | ||
892 | u8 *bssid, int mode, struct ieee80211_channel *channel, | ||
893 | struct cfg80211_connect_params *sme, bool privacy) | ||
894 | { | ||
895 | struct mwifiex_802_11_ssid req_ssid; | ||
896 | struct mwifiex_ssid_bssid ssid_bssid; | ||
897 | int ret, auth_type = 0; | ||
898 | |||
899 | memset(&req_ssid, 0, sizeof(struct mwifiex_802_11_ssid)); | ||
900 | memset(&ssid_bssid, 0, sizeof(struct mwifiex_ssid_bssid)); | ||
901 | |||
902 | req_ssid.ssid_len = ssid_len; | ||
903 | if (ssid_len > IEEE80211_MAX_SSID_LEN) { | ||
904 | dev_err(priv->adapter->dev, "invalid SSID - aborting\n"); | ||
905 | return -EINVAL; | ||
906 | } | ||
907 | |||
908 | memcpy(req_ssid.ssid, ssid, ssid_len); | ||
909 | if (!req_ssid.ssid_len || req_ssid.ssid[0] < 0x20) { | ||
910 | dev_err(priv->adapter->dev, "invalid SSID - aborting\n"); | ||
911 | return -EINVAL; | ||
912 | } | ||
913 | |||
914 | /* disconnect before try to associate */ | ||
915 | mwifiex_deauthenticate(priv, NULL); | ||
916 | |||
917 | if (channel) | ||
918 | ret = mwifiex_set_rf_channel(priv, channel, | ||
919 | mwifiex_channels_to_cfg80211_channel_type | ||
920 | (priv->adapter->chan_offset)); | ||
921 | |||
922 | ret = mwifiex_set_encode(priv, NULL, 0, 0, 1); /* Disable keys */ | ||
923 | |||
924 | if (mode == NL80211_IFTYPE_ADHOC) { | ||
925 | /* "privacy" is set only for ad-hoc mode */ | ||
926 | if (privacy) { | ||
927 | /* | ||
928 | * Keep WLAN_CIPHER_SUITE_WEP104 for now so that | ||
929 | * the firmware can find a matching network from the | ||
930 | * scan. The cfg80211 does not give us the encryption | ||
931 | * mode at this stage so just setting it to WEP here. | ||
932 | */ | ||
933 | priv->sec_info.encryption_mode = | ||
934 | WLAN_CIPHER_SUITE_WEP104; | ||
935 | priv->sec_info.authentication_mode = | ||
936 | NL80211_AUTHTYPE_OPEN_SYSTEM; | ||
937 | } | ||
938 | |||
939 | goto done; | ||
940 | } | ||
941 | |||
942 | /* Now handle infra mode. "sme" is valid for infra mode only */ | ||
943 | if (sme->auth_type == NL80211_AUTHTYPE_AUTOMATIC | ||
944 | || sme->auth_type == NL80211_AUTHTYPE_OPEN_SYSTEM) | ||
945 | auth_type = NL80211_AUTHTYPE_OPEN_SYSTEM; | ||
946 | else if (sme->auth_type == NL80211_AUTHTYPE_SHARED_KEY) | ||
947 | auth_type = NL80211_AUTHTYPE_SHARED_KEY; | ||
948 | |||
949 | if (sme->crypto.n_ciphers_pairwise) { | ||
950 | priv->sec_info.encryption_mode = | ||
951 | sme->crypto.ciphers_pairwise[0]; | ||
952 | priv->sec_info.authentication_mode = auth_type; | ||
953 | } | ||
954 | |||
955 | if (sme->crypto.cipher_group) { | ||
956 | priv->sec_info.encryption_mode = sme->crypto.cipher_group; | ||
957 | priv->sec_info.authentication_mode = auth_type; | ||
958 | } | ||
959 | if (sme->ie) | ||
960 | ret = mwifiex_set_gen_ie(priv, sme->ie, sme->ie_len); | ||
961 | |||
962 | if (sme->key) { | ||
963 | if (mwifiex_is_alg_wep(0) | mwifiex_is_alg_wep(0)) { | ||
964 | dev_dbg(priv->adapter->dev, | ||
965 | "info: setting wep encryption" | ||
966 | " with key len %d\n", sme->key_len); | ||
967 | ret = mwifiex_set_encode(priv, sme->key, sme->key_len, | ||
968 | sme->key_idx, 0); | ||
969 | } | ||
970 | } | ||
971 | done: | ||
972 | /* Do specific SSID scanning */ | ||
973 | if (mwifiex_request_scan(priv, &req_ssid)) { | ||
974 | dev_err(priv->adapter->dev, "scan error\n"); | ||
975 | return -EFAULT; | ||
976 | } | ||
977 | |||
978 | |||
979 | memcpy(&ssid_bssid.ssid, &req_ssid, sizeof(struct mwifiex_802_11_ssid)); | ||
980 | |||
981 | if (mode != NL80211_IFTYPE_ADHOC) { | ||
982 | if (mwifiex_find_best_bss(priv, &ssid_bssid)) | ||
983 | return -EFAULT; | ||
984 | /* Inform the BSS information to kernel, otherwise | ||
985 | * kernel will give a panic after successful assoc */ | ||
986 | if (mwifiex_inform_bss_from_scan_result(priv, &req_ssid)) | ||
987 | return -EFAULT; | ||
988 | } | ||
989 | |||
990 | dev_dbg(priv->adapter->dev, "info: trying to associate to %s and bssid %pM\n", | ||
991 | (char *) req_ssid.ssid, ssid_bssid.bssid); | ||
992 | |||
993 | memcpy(&priv->cfg_bssid, ssid_bssid.bssid, 6); | ||
994 | |||
995 | /* Connect to BSS by ESSID */ | ||
996 | memset(&ssid_bssid.bssid, 0, ETH_ALEN); | ||
997 | |||
998 | if (!netif_queue_stopped(priv->netdev)) | ||
999 | netif_stop_queue(priv->netdev); | ||
1000 | |||
1001 | if (mwifiex_bss_start(priv, &ssid_bssid)) | ||
1002 | return -EFAULT; | ||
1003 | |||
1004 | if (mode == NL80211_IFTYPE_ADHOC) { | ||
1005 | /* Inform the BSS information to kernel, otherwise | ||
1006 | * kernel will give a panic after successful assoc */ | ||
1007 | if (mwifiex_cfg80211_inform_ibss_bss(priv)) | ||
1008 | return -EFAULT; | ||
1009 | } | ||
1010 | |||
1011 | return ret; | ||
1012 | } | ||
1013 | |||
1014 | /* | ||
1015 | * CFG802.11 operation handler for association request. | ||
1016 | * | ||
1017 | * This function does not work when the current mode is set to Ad-Hoc, or | ||
1018 | * when there is already an association procedure going on. The given BSS | ||
1019 | * information is used to associate. | ||
1020 | */ | ||
1021 | static int | ||
1022 | mwifiex_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev, | ||
1023 | struct cfg80211_connect_params *sme) | ||
1024 | { | ||
1025 | struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); | ||
1026 | int ret = 0; | ||
1027 | |||
1028 | if (priv->assoc_request) | ||
1029 | return -EBUSY; | ||
1030 | |||
1031 | if (priv->bss_mode == NL80211_IFTYPE_ADHOC) { | ||
1032 | wiphy_err(wiphy, "received infra assoc request " | ||
1033 | "when station is in ibss mode\n"); | ||
1034 | goto done; | ||
1035 | } | ||
1036 | |||
1037 | priv->assoc_request = -EINPROGRESS; | ||
1038 | |||
1039 | wiphy_dbg(wiphy, "info: Trying to associate to %s and bssid %pM\n", | ||
1040 | (char *) sme->ssid, sme->bssid); | ||
1041 | |||
1042 | ret = mwifiex_cfg80211_assoc(priv, sme->ssid_len, sme->ssid, sme->bssid, | ||
1043 | priv->bss_mode, sme->channel, sme, 0); | ||
1044 | |||
1045 | priv->assoc_request = 1; | ||
1046 | done: | ||
1047 | priv->assoc_result = ret; | ||
1048 | queue_work(priv->workqueue, &priv->cfg_workqueue); | ||
1049 | return ret; | ||
1050 | } | ||
1051 | |||
1052 | /* | ||
1053 | * CFG802.11 operation handler to join an IBSS. | ||
1054 | * | ||
1055 | * This function does not work in any mode other than Ad-Hoc, or if | ||
1056 | * a join operation is already in progress. | ||
1057 | */ | ||
1058 | static int | ||
1059 | mwifiex_cfg80211_join_ibss(struct wiphy *wiphy, struct net_device *dev, | ||
1060 | struct cfg80211_ibss_params *params) | ||
1061 | { | ||
1062 | struct mwifiex_private *priv = mwifiex_cfg80211_get_priv(wiphy); | ||
1063 | int ret = 0; | ||
1064 | |||
1065 | if (priv->ibss_join_request) | ||
1066 | return -EBUSY; | ||
1067 | |||
1068 | if (priv->bss_mode != NL80211_IFTYPE_ADHOC) { | ||
1069 | wiphy_err(wiphy, "request to join ibss received " | ||
1070 | "when station is not in ibss mode\n"); | ||
1071 | goto done; | ||
1072 | } | ||
1073 | |||
1074 | priv->ibss_join_request = -EINPROGRESS; | ||
1075 | |||
1076 | wiphy_dbg(wiphy, "info: trying to join to %s and bssid %pM\n", | ||
1077 | (char *) params->ssid, params->bssid); | ||
1078 | |||
1079 | ret = mwifiex_cfg80211_assoc(priv, params->ssid_len, params->ssid, | ||
1080 | params->bssid, priv->bss_mode, | ||
1081 | params->channel, NULL, params->privacy); | ||
1082 | |||
1083 | priv->ibss_join_request = 1; | ||
1084 | done: | ||
1085 | priv->ibss_join_result = ret; | ||
1086 | queue_work(priv->workqueue, &priv->cfg_workqueue); | ||
1087 | return ret; | ||
1088 | } | ||
1089 | |||
1090 | /* | ||
1091 | * CFG802.11 operation handler to leave an IBSS. | ||
1092 | * | ||
1093 | * This function does not work if a leave operation is | ||
1094 | * already in progress. | ||
1095 | */ | ||
1096 | static int | ||
1097 | mwifiex_cfg80211_leave_ibss(struct wiphy *wiphy, struct net_device *dev) | ||
1098 | { | ||
1099 | struct mwifiex_private *priv = mwifiex_cfg80211_get_priv(wiphy); | ||
1100 | |||
1101 | if (priv->disconnect) | ||
1102 | return -EBUSY; | ||
1103 | |||
1104 | priv->disconnect = 1; | ||
1105 | |||
1106 | wiphy_dbg(wiphy, "info: disconnecting from essid %pM\n", | ||
1107 | priv->cfg_bssid); | ||
1108 | if (mwifiex_deauthenticate(priv, NULL)) | ||
1109 | return -EFAULT; | ||
1110 | |||
1111 | queue_work(priv->workqueue, &priv->cfg_workqueue); | ||
1112 | |||
1113 | return 0; | ||
1114 | } | ||
1115 | |||
1116 | /* | ||
1117 | * CFG802.11 operation handler for scan request. | ||
1118 | * | ||
1119 | * This function issues a scan request to the firmware based upon | ||
1120 | * the user specified scan configuration. On successfull completion, | ||
1121 | * it also informs the results. | ||
1122 | */ | ||
1123 | static int | ||
1124 | mwifiex_cfg80211_scan(struct wiphy *wiphy, struct net_device *dev, | ||
1125 | struct cfg80211_scan_request *request) | ||
1126 | { | ||
1127 | struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); | ||
1128 | |||
1129 | wiphy_dbg(wiphy, "info: received scan request on %s\n", dev->name); | ||
1130 | |||
1131 | if (priv->scan_request && priv->scan_request != request) | ||
1132 | return -EBUSY; | ||
1133 | |||
1134 | priv->scan_request = request; | ||
1135 | |||
1136 | queue_work(priv->workqueue, &priv->cfg_workqueue); | ||
1137 | return 0; | ||
1138 | } | ||
1139 | |||
1140 | /* | ||
1141 | * This function sets up the CFG802.11 specific HT capability fields | ||
1142 | * with default values. | ||
1143 | * | ||
1144 | * The following default values are set - | ||
1145 | * - HT Supported = True | ||
1146 | * - Maximum AMPDU length factor = IEEE80211_HT_MAX_AMPDU_64K | ||
1147 | * - Minimum AMPDU spacing = IEEE80211_HT_MPDU_DENSITY_NONE | ||
1148 | * - HT Capabilities supported by firmware | ||
1149 | * - MCS information, Rx mask = 0xff | ||
1150 | * - MCD information, Tx parameters = IEEE80211_HT_MCS_TX_DEFINED (0x01) | ||
1151 | */ | ||
1152 | static void | ||
1153 | mwifiex_setup_ht_caps(struct ieee80211_sta_ht_cap *ht_info, | ||
1154 | struct mwifiex_private *priv) | ||
1155 | { | ||
1156 | int rx_mcs_supp; | ||
1157 | struct ieee80211_mcs_info mcs_set; | ||
1158 | u8 *mcs = (u8 *)&mcs_set; | ||
1159 | struct mwifiex_adapter *adapter = priv->adapter; | ||
1160 | |||
1161 | ht_info->ht_supported = true; | ||
1162 | ht_info->ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K; | ||
1163 | ht_info->ampdu_density = IEEE80211_HT_MPDU_DENSITY_NONE; | ||
1164 | |||
1165 | memset(&ht_info->mcs, 0, sizeof(ht_info->mcs)); | ||
1166 | |||
1167 | /* Fill HT capability information */ | ||
1168 | if (ISSUPP_CHANWIDTH40(adapter->hw_dot_11n_dev_cap)) | ||
1169 | ht_info->cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40; | ||
1170 | else | ||
1171 | ht_info->cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40; | ||
1172 | |||
1173 | if (ISSUPP_SHORTGI20(adapter->hw_dot_11n_dev_cap)) | ||
1174 | ht_info->cap |= IEEE80211_HT_CAP_SGI_20; | ||
1175 | else | ||
1176 | ht_info->cap &= ~IEEE80211_HT_CAP_SGI_20; | ||
1177 | |||
1178 | if (ISSUPP_SHORTGI40(adapter->hw_dot_11n_dev_cap)) | ||
1179 | ht_info->cap |= IEEE80211_HT_CAP_SGI_40; | ||
1180 | else | ||
1181 | ht_info->cap &= ~IEEE80211_HT_CAP_SGI_40; | ||
1182 | |||
1183 | if (ISSUPP_RXSTBC(adapter->hw_dot_11n_dev_cap)) | ||
1184 | ht_info->cap |= 1 << IEEE80211_HT_CAP_RX_STBC_SHIFT; | ||
1185 | else | ||
1186 | ht_info->cap &= ~(3 << IEEE80211_HT_CAP_RX_STBC_SHIFT); | ||
1187 | |||
1188 | if (ISSUPP_TXSTBC(adapter->hw_dot_11n_dev_cap)) | ||
1189 | ht_info->cap |= IEEE80211_HT_CAP_TX_STBC; | ||
1190 | else | ||
1191 | ht_info->cap &= ~IEEE80211_HT_CAP_TX_STBC; | ||
1192 | |||
1193 | ht_info->cap &= ~IEEE80211_HT_CAP_MAX_AMSDU; | ||
1194 | ht_info->cap |= IEEE80211_HT_CAP_SM_PS; | ||
1195 | |||
1196 | rx_mcs_supp = GET_RXMCSSUPP(adapter->hw_dev_mcs_support); | ||
1197 | /* Set MCS for 1x1 */ | ||
1198 | memset(mcs, 0xff, rx_mcs_supp); | ||
1199 | /* Clear all the other values */ | ||
1200 | memset(&mcs[rx_mcs_supp], 0, | ||
1201 | sizeof(struct ieee80211_mcs_info) - rx_mcs_supp); | ||
1202 | if (priv->bss_mode == NL80211_IFTYPE_STATION || | ||
1203 | ISSUPP_CHANWIDTH40(adapter->hw_dot_11n_dev_cap)) | ||
1204 | /* Set MCS32 for infra mode or ad-hoc mode with 40MHz support */ | ||
1205 | SETHT_MCS32(mcs_set.rx_mask); | ||
1206 | |||
1207 | memcpy((u8 *) &ht_info->mcs, mcs, sizeof(struct ieee80211_mcs_info)); | ||
1208 | |||
1209 | ht_info->mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED; | ||
1210 | } | ||
1211 | |||
1212 | /* station cfg80211 operations */ | ||
1213 | static struct cfg80211_ops mwifiex_cfg80211_ops = { | ||
1214 | .change_virtual_intf = mwifiex_cfg80211_change_virtual_intf, | ||
1215 | .scan = mwifiex_cfg80211_scan, | ||
1216 | .connect = mwifiex_cfg80211_connect, | ||
1217 | .disconnect = mwifiex_cfg80211_disconnect, | ||
1218 | .get_station = mwifiex_cfg80211_get_station, | ||
1219 | .set_wiphy_params = mwifiex_cfg80211_set_wiphy_params, | ||
1220 | .set_channel = mwifiex_cfg80211_set_channel, | ||
1221 | .join_ibss = mwifiex_cfg80211_join_ibss, | ||
1222 | .leave_ibss = mwifiex_cfg80211_leave_ibss, | ||
1223 | .add_key = mwifiex_cfg80211_add_key, | ||
1224 | .del_key = mwifiex_cfg80211_del_key, | ||
1225 | .set_default_key = mwifiex_cfg80211_set_default_key, | ||
1226 | .set_power_mgmt = mwifiex_cfg80211_set_power_mgmt, | ||
1227 | .set_tx_power = mwifiex_cfg80211_set_tx_power, | ||
1228 | }; | ||
1229 | |||
1230 | /* | ||
1231 | * This function registers the device with CFG802.11 subsystem. | ||
1232 | * | ||
1233 | * The function creates the wireless device/wiphy, populates it with | ||
1234 | * default parameters and handler function pointers, and finally | ||
1235 | * registers the device. | ||
1236 | */ | ||
1237 | int mwifiex_register_cfg80211(struct net_device *dev, u8 *mac, | ||
1238 | struct mwifiex_private *priv) | ||
1239 | { | ||
1240 | int ret; | ||
1241 | void *wdev_priv; | ||
1242 | struct wireless_dev *wdev; | ||
1243 | |||
1244 | wdev = kzalloc(sizeof(struct wireless_dev), GFP_KERNEL); | ||
1245 | if (!wdev) { | ||
1246 | dev_err(priv->adapter->dev, "%s: allocating wireless device\n", | ||
1247 | __func__); | ||
1248 | return -ENOMEM; | ||
1249 | } | ||
1250 | wdev->wiphy = | ||
1251 | wiphy_new(&mwifiex_cfg80211_ops, | ||
1252 | sizeof(struct mwifiex_private *)); | ||
1253 | if (!wdev->wiphy) { | ||
1254 | kfree(wdev); | ||
1255 | return -ENOMEM; | ||
1256 | } | ||
1257 | wdev->iftype = NL80211_IFTYPE_STATION; | ||
1258 | wdev->wiphy->max_scan_ssids = 10; | ||
1259 | wdev->wiphy->interface_modes = | ||
1260 | BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_ADHOC); | ||
1261 | |||
1262 | wdev->wiphy->bands[IEEE80211_BAND_2GHZ] = &mwifiex_band_2ghz; | ||
1263 | mwifiex_setup_ht_caps( | ||
1264 | &wdev->wiphy->bands[IEEE80211_BAND_2GHZ]->ht_cap, priv); | ||
1265 | |||
1266 | if (priv->adapter->config_bands & BAND_A) { | ||
1267 | wdev->wiphy->bands[IEEE80211_BAND_5GHZ] = &mwifiex_band_5ghz; | ||
1268 | mwifiex_setup_ht_caps( | ||
1269 | &wdev->wiphy->bands[IEEE80211_BAND_5GHZ]->ht_cap, priv); | ||
1270 | } else { | ||
1271 | wdev->wiphy->bands[IEEE80211_BAND_5GHZ] = NULL; | ||
1272 | } | ||
1273 | |||
1274 | /* Initialize cipher suits */ | ||
1275 | wdev->wiphy->cipher_suites = mwifiex_cipher_suites; | ||
1276 | wdev->wiphy->n_cipher_suites = ARRAY_SIZE(mwifiex_cipher_suites); | ||
1277 | |||
1278 | memcpy(wdev->wiphy->perm_addr, mac, 6); | ||
1279 | wdev->wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM; | ||
1280 | |||
1281 | /* We are using custom domains */ | ||
1282 | wdev->wiphy->flags |= WIPHY_FLAG_CUSTOM_REGULATORY; | ||
1283 | |||
1284 | wdev->wiphy->reg_notifier = mwifiex_reg_notifier; | ||
1285 | |||
1286 | /* Set struct mwifiex_private pointer in wiphy_priv */ | ||
1287 | wdev_priv = wiphy_priv(wdev->wiphy); | ||
1288 | |||
1289 | *(unsigned long *) wdev_priv = (unsigned long) priv; | ||
1290 | |||
1291 | set_wiphy_dev(wdev->wiphy, (struct device *) priv->adapter->dev); | ||
1292 | |||
1293 | ret = wiphy_register(wdev->wiphy); | ||
1294 | if (ret < 0) { | ||
1295 | dev_err(priv->adapter->dev, "%s: registering cfg80211 device\n", | ||
1296 | __func__); | ||
1297 | wiphy_free(wdev->wiphy); | ||
1298 | kfree(wdev); | ||
1299 | return ret; | ||
1300 | } else { | ||
1301 | dev_dbg(priv->adapter->dev, | ||
1302 | "info: successfully registered wiphy device\n"); | ||
1303 | } | ||
1304 | |||
1305 | dev_net_set(dev, wiphy_net(wdev->wiphy)); | ||
1306 | dev->ieee80211_ptr = wdev; | ||
1307 | memcpy(dev->dev_addr, wdev->wiphy->perm_addr, 6); | ||
1308 | memcpy(dev->perm_addr, wdev->wiphy->perm_addr, 6); | ||
1309 | SET_NETDEV_DEV(dev, wiphy_dev(wdev->wiphy)); | ||
1310 | priv->wdev = wdev; | ||
1311 | |||
1312 | dev->flags |= IFF_BROADCAST | IFF_MULTICAST; | ||
1313 | dev->watchdog_timeo = MWIFIEX_DEFAULT_WATCHDOG_TIMEOUT; | ||
1314 | dev->hard_header_len += MWIFIEX_MIN_DATA_HEADER_LEN; | ||
1315 | |||
1316 | return ret; | ||
1317 | } | ||
1318 | |||
1319 | /* | ||
1320 | * This function handles the result of different pending network operations. | ||
1321 | * | ||
1322 | * The following operations are handled and CFG802.11 subsystem is | ||
1323 | * notified accordingly - | ||
1324 | * - Scan request completion | ||
1325 | * - Association request completion | ||
1326 | * - IBSS join request completion | ||
1327 | * - Disconnect request completion | ||
1328 | */ | ||
1329 | void | ||
1330 | mwifiex_cfg80211_results(struct work_struct *work) | ||
1331 | { | ||
1332 | struct mwifiex_private *priv = | ||
1333 | container_of(work, struct mwifiex_private, cfg_workqueue); | ||
1334 | struct mwifiex_user_scan_cfg *scan_req; | ||
1335 | int ret = 0, i; | ||
1336 | struct ieee80211_channel *chan; | ||
1337 | |||
1338 | if (priv->scan_request) { | ||
1339 | scan_req = kzalloc(sizeof(struct mwifiex_user_scan_cfg), | ||
1340 | GFP_KERNEL); | ||
1341 | if (!scan_req) { | ||
1342 | dev_err(priv->adapter->dev, "failed to alloc " | ||
1343 | "scan_req\n"); | ||
1344 | return; | ||
1345 | } | ||
1346 | for (i = 0; i < priv->scan_request->n_ssids; i++) { | ||
1347 | memcpy(scan_req->ssid_list[i].ssid, | ||
1348 | priv->scan_request->ssids[i].ssid, | ||
1349 | priv->scan_request->ssids[i].ssid_len); | ||
1350 | scan_req->ssid_list[i].max_len = | ||
1351 | priv->scan_request->ssids[i].ssid_len; | ||
1352 | } | ||
1353 | for (i = 0; i < priv->scan_request->n_channels; i++) { | ||
1354 | chan = priv->scan_request->channels[i]; | ||
1355 | scan_req->chan_list[i].chan_number = chan->hw_value; | ||
1356 | scan_req->chan_list[i].radio_type = chan->band; | ||
1357 | if (chan->flags & IEEE80211_CHAN_DISABLED) | ||
1358 | scan_req->chan_list[i].scan_type = | ||
1359 | MWIFIEX_SCAN_TYPE_PASSIVE; | ||
1360 | else | ||
1361 | scan_req->chan_list[i].scan_type = | ||
1362 | MWIFIEX_SCAN_TYPE_ACTIVE; | ||
1363 | scan_req->chan_list[i].scan_time = 0; | ||
1364 | } | ||
1365 | if (mwifiex_set_user_scan_ioctl(priv, scan_req)) { | ||
1366 | ret = -EFAULT; | ||
1367 | goto done; | ||
1368 | } | ||
1369 | if (mwifiex_inform_bss_from_scan_result(priv, NULL)) | ||
1370 | ret = -EFAULT; | ||
1371 | done: | ||
1372 | priv->scan_result_status = ret; | ||
1373 | dev_dbg(priv->adapter->dev, "info: %s: sending scan results\n", | ||
1374 | __func__); | ||
1375 | cfg80211_scan_done(priv->scan_request, | ||
1376 | (priv->scan_result_status < 0)); | ||
1377 | priv->scan_request = NULL; | ||
1378 | kfree(scan_req); | ||
1379 | } | ||
1380 | |||
1381 | if (priv->assoc_request == 1) { | ||
1382 | if (!priv->assoc_result) { | ||
1383 | cfg80211_connect_result(priv->netdev, priv->cfg_bssid, | ||
1384 | NULL, 0, NULL, 0, | ||
1385 | WLAN_STATUS_SUCCESS, | ||
1386 | GFP_KERNEL); | ||
1387 | dev_dbg(priv->adapter->dev, | ||
1388 | "info: associated to bssid %pM successfully\n", | ||
1389 | priv->cfg_bssid); | ||
1390 | } else { | ||
1391 | dev_dbg(priv->adapter->dev, | ||
1392 | "info: association to bssid %pM failed\n", | ||
1393 | priv->cfg_bssid); | ||
1394 | memset(priv->cfg_bssid, 0, ETH_ALEN); | ||
1395 | } | ||
1396 | priv->assoc_request = 0; | ||
1397 | priv->assoc_result = 0; | ||
1398 | } | ||
1399 | |||
1400 | if (priv->ibss_join_request == 1) { | ||
1401 | if (!priv->ibss_join_result) { | ||
1402 | cfg80211_ibss_joined(priv->netdev, priv->cfg_bssid, | ||
1403 | GFP_KERNEL); | ||
1404 | dev_dbg(priv->adapter->dev, | ||
1405 | "info: joined/created adhoc network with bssid" | ||
1406 | " %pM successfully\n", priv->cfg_bssid); | ||
1407 | } else { | ||
1408 | dev_dbg(priv->adapter->dev, | ||
1409 | "info: failed creating/joining adhoc network\n"); | ||
1410 | } | ||
1411 | priv->ibss_join_request = 0; | ||
1412 | priv->ibss_join_result = 0; | ||
1413 | } | ||
1414 | |||
1415 | if (priv->disconnect) { | ||
1416 | memset(priv->cfg_bssid, 0, ETH_ALEN); | ||
1417 | priv->disconnect = 0; | ||
1418 | } | ||
1419 | } | ||
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..d0cada5a29a0 --- /dev/null +++ b/drivers/net/wireless/mwifiex/cfp.c | |||
@@ -0,0 +1,360 @@ | |||
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(u8 index, u8 ht_info) | ||
79 | { | ||
80 | u16 mcs_rate[4][8] = { | ||
81 | {0x1b, 0x36, 0x51, 0x6c, 0xa2, 0xd8, 0xf3, 0x10e} | ||
82 | , /* LG 40M */ | ||
83 | {0x1e, 0x3c, 0x5a, 0x78, 0xb4, 0xf0, 0x10e, 0x12c} | ||
84 | , /* SG 40M */ | ||
85 | {0x0d, 0x1a, 0x27, 0x34, 0x4e, 0x68, 0x75, 0x82} | ||
86 | , /* LG 20M */ | ||
87 | {0x0e, 0x1c, 0x2b, 0x39, 0x56, 0x73, 0x82, 0x90} | ||
88 | }; /* SG 20M */ | ||
89 | |||
90 | u32 rate; | ||
91 | |||
92 | if (ht_info & BIT(0)) { | ||
93 | if (index == MWIFIEX_RATE_BITMAP_MCS0) { | ||
94 | if (ht_info & BIT(2)) | ||
95 | rate = 0x0D; /* MCS 32 SGI rate */ | ||
96 | else | ||
97 | rate = 0x0C; /* MCS 32 LGI rate */ | ||
98 | } else if (index < 8) { | ||
99 | if (ht_info & BIT(1)) { | ||
100 | if (ht_info & BIT(2)) | ||
101 | /* SGI, 40M */ | ||
102 | rate = mcs_rate[1][index]; | ||
103 | else | ||
104 | /* LGI, 40M */ | ||
105 | rate = mcs_rate[0][index]; | ||
106 | } else { | ||
107 | if (ht_info & BIT(2)) | ||
108 | /* SGI, 20M */ | ||
109 | rate = mcs_rate[3][index]; | ||
110 | else | ||
111 | /* LGI, 20M */ | ||
112 | rate = mcs_rate[2][index]; | ||
113 | } | ||
114 | } else | ||
115 | rate = mwifiex_data_rates[0]; | ||
116 | } else { | ||
117 | if (index >= MWIFIEX_SUPPORTED_RATES_EXT) | ||
118 | index = 0; | ||
119 | rate = mwifiex_data_rates[index]; | ||
120 | } | ||
121 | return rate; | ||
122 | } | ||
123 | |||
124 | /* | ||
125 | * This function maps a data rate value into corresponding index in supported | ||
126 | * rates table. | ||
127 | */ | ||
128 | u8 mwifiex_data_rate_to_index(u32 rate) | ||
129 | { | ||
130 | u16 *ptr; | ||
131 | |||
132 | if (rate) { | ||
133 | ptr = memchr(mwifiex_data_rates, rate, | ||
134 | sizeof(mwifiex_data_rates)); | ||
135 | if (ptr) | ||
136 | return (u8) (ptr - mwifiex_data_rates); | ||
137 | } | ||
138 | return 0; | ||
139 | } | ||
140 | |||
141 | /* | ||
142 | * This function returns the current active data rates. | ||
143 | * | ||
144 | * The result may vary depending upon connection status. | ||
145 | */ | ||
146 | u32 mwifiex_get_active_data_rates(struct mwifiex_private *priv, u8 *rates) | ||
147 | { | ||
148 | if (!priv->media_connected) | ||
149 | return mwifiex_get_supported_rates(priv, rates); | ||
150 | else | ||
151 | return mwifiex_copy_rates(rates, 0, | ||
152 | priv->curr_bss_params.data_rates, | ||
153 | priv->curr_bss_params.num_of_rates); | ||
154 | } | ||
155 | |||
156 | /* | ||
157 | * This function locates the Channel-Frequency-Power triplet based upon | ||
158 | * band and channel parameters. | ||
159 | */ | ||
160 | struct mwifiex_chan_freq_power * | ||
161 | mwifiex_get_cfp_by_band_and_channel_from_cfg80211(struct mwifiex_private | ||
162 | *priv, u8 band, u16 channel) | ||
163 | { | ||
164 | struct mwifiex_chan_freq_power *cfp = NULL; | ||
165 | struct ieee80211_supported_band *sband; | ||
166 | struct ieee80211_channel *ch; | ||
167 | int i; | ||
168 | |||
169 | if (mwifiex_band_to_radio_type(band) == HostCmd_SCAN_RADIO_TYPE_BG) | ||
170 | sband = priv->wdev->wiphy->bands[IEEE80211_BAND_2GHZ]; | ||
171 | else | ||
172 | sband = priv->wdev->wiphy->bands[IEEE80211_BAND_5GHZ]; | ||
173 | |||
174 | if (!sband) { | ||
175 | dev_err(priv->adapter->dev, "%s: cannot find cfp by band %d" | ||
176 | " & channel %d\n", __func__, band, channel); | ||
177 | return cfp; | ||
178 | } | ||
179 | |||
180 | for (i = 0; i < sband->n_channels; i++) { | ||
181 | ch = &sband->channels[i]; | ||
182 | if (((ch->hw_value == channel) || | ||
183 | (channel == FIRST_VALID_CHANNEL)) | ||
184 | && !(ch->flags & IEEE80211_CHAN_DISABLED)) { | ||
185 | priv->cfp.channel = channel; | ||
186 | priv->cfp.freq = ch->center_freq; | ||
187 | priv->cfp.max_tx_power = ch->max_power; | ||
188 | cfp = &priv->cfp; | ||
189 | break; | ||
190 | } | ||
191 | } | ||
192 | if (i == sband->n_channels) | ||
193 | dev_err(priv->adapter->dev, "%s: cannot find cfp by band %d" | ||
194 | " & channel %d\n", __func__, band, channel); | ||
195 | |||
196 | return cfp; | ||
197 | } | ||
198 | |||
199 | /* | ||
200 | * This function locates the Channel-Frequency-Power triplet based upon | ||
201 | * band and frequency parameters. | ||
202 | */ | ||
203 | struct mwifiex_chan_freq_power * | ||
204 | mwifiex_get_cfp_by_band_and_freq_from_cfg80211(struct mwifiex_private *priv, | ||
205 | u8 band, u32 freq) | ||
206 | { | ||
207 | struct mwifiex_chan_freq_power *cfp = NULL; | ||
208 | struct ieee80211_supported_band *sband; | ||
209 | struct ieee80211_channel *ch; | ||
210 | int i; | ||
211 | |||
212 | if (mwifiex_band_to_radio_type(band) == HostCmd_SCAN_RADIO_TYPE_BG) | ||
213 | sband = priv->wdev->wiphy->bands[IEEE80211_BAND_2GHZ]; | ||
214 | else | ||
215 | sband = priv->wdev->wiphy->bands[IEEE80211_BAND_5GHZ]; | ||
216 | |||
217 | if (!sband) { | ||
218 | dev_err(priv->adapter->dev, "%s: cannot find cfp by band %d" | ||
219 | " & freq %d\n", __func__, band, freq); | ||
220 | return cfp; | ||
221 | } | ||
222 | |||
223 | for (i = 0; i < sband->n_channels; i++) { | ||
224 | ch = &sband->channels[i]; | ||
225 | if ((ch->center_freq == freq) && | ||
226 | !(ch->flags & IEEE80211_CHAN_DISABLED)) { | ||
227 | priv->cfp.channel = ch->hw_value; | ||
228 | priv->cfp.freq = freq; | ||
229 | priv->cfp.max_tx_power = ch->max_power; | ||
230 | cfp = &priv->cfp; | ||
231 | break; | ||
232 | } | ||
233 | } | ||
234 | if (i == sband->n_channels) | ||
235 | dev_err(priv->adapter->dev, "%s: cannot find cfp by band %d" | ||
236 | " & freq %d\n", __func__, band, freq); | ||
237 | |||
238 | return cfp; | ||
239 | } | ||
240 | |||
241 | /* | ||
242 | * This function checks if the data rate is set to auto. | ||
243 | */ | ||
244 | u8 | ||
245 | mwifiex_is_rate_auto(struct mwifiex_private *priv) | ||
246 | { | ||
247 | u32 i; | ||
248 | int rate_num = 0; | ||
249 | |||
250 | for (i = 0; i < ARRAY_SIZE(priv->bitmap_rates); i++) | ||
251 | if (priv->bitmap_rates[i]) | ||
252 | rate_num++; | ||
253 | |||
254 | if (rate_num > 1) | ||
255 | return true; | ||
256 | else | ||
257 | return false; | ||
258 | } | ||
259 | |||
260 | /* | ||
261 | * This function converts rate bitmap into rate index. | ||
262 | */ | ||
263 | int mwifiex_get_rate_index(u16 *rate_bitmap, int size) | ||
264 | { | ||
265 | int i; | ||
266 | |||
267 | for (i = 0; i < size * 8; i++) | ||
268 | if (rate_bitmap[i / 16] & (1 << (i % 16))) | ||
269 | return i; | ||
270 | |||
271 | return 0; | ||
272 | } | ||
273 | |||
274 | /* | ||
275 | * This function gets the supported data rates. | ||
276 | * | ||
277 | * The function works in both Ad-Hoc and infra mode by printing the | ||
278 | * band and returning the data rates. | ||
279 | */ | ||
280 | u32 mwifiex_get_supported_rates(struct mwifiex_private *priv, u8 *rates) | ||
281 | { | ||
282 | u32 k = 0; | ||
283 | struct mwifiex_adapter *adapter = priv->adapter; | ||
284 | if (priv->bss_mode == NL80211_IFTYPE_STATION) { | ||
285 | switch (adapter->config_bands) { | ||
286 | case BAND_B: | ||
287 | dev_dbg(adapter->dev, "info: infra band=%d " | ||
288 | "supported_rates_b\n", adapter->config_bands); | ||
289 | k = mwifiex_copy_rates(rates, k, supported_rates_b, | ||
290 | sizeof(supported_rates_b)); | ||
291 | break; | ||
292 | case BAND_G: | ||
293 | case BAND_G | BAND_GN: | ||
294 | dev_dbg(adapter->dev, "info: infra band=%d " | ||
295 | "supported_rates_g\n", adapter->config_bands); | ||
296 | k = mwifiex_copy_rates(rates, k, supported_rates_g, | ||
297 | sizeof(supported_rates_g)); | ||
298 | break; | ||
299 | case BAND_B | BAND_G: | ||
300 | case BAND_A | BAND_B | BAND_G: | ||
301 | case BAND_A | BAND_B: | ||
302 | case BAND_A | BAND_B | BAND_G | BAND_GN | BAND_AN: | ||
303 | case BAND_B | BAND_G | BAND_GN: | ||
304 | dev_dbg(adapter->dev, "info: infra band=%d " | ||
305 | "supported_rates_bg\n", adapter->config_bands); | ||
306 | k = mwifiex_copy_rates(rates, k, supported_rates_bg, | ||
307 | sizeof(supported_rates_bg)); | ||
308 | break; | ||
309 | case BAND_A: | ||
310 | case BAND_A | BAND_G: | ||
311 | dev_dbg(adapter->dev, "info: infra band=%d " | ||
312 | "supported_rates_a\n", adapter->config_bands); | ||
313 | k = mwifiex_copy_rates(rates, k, supported_rates_a, | ||
314 | sizeof(supported_rates_a)); | ||
315 | break; | ||
316 | case BAND_A | BAND_AN: | ||
317 | case BAND_A | BAND_G | BAND_AN | BAND_GN: | ||
318 | dev_dbg(adapter->dev, "info: infra band=%d " | ||
319 | "supported_rates_a\n", adapter->config_bands); | ||
320 | k = mwifiex_copy_rates(rates, k, supported_rates_a, | ||
321 | sizeof(supported_rates_a)); | ||
322 | break; | ||
323 | case BAND_GN: | ||
324 | dev_dbg(adapter->dev, "info: infra band=%d " | ||
325 | "supported_rates_n\n", adapter->config_bands); | ||
326 | k = mwifiex_copy_rates(rates, k, supported_rates_n, | ||
327 | sizeof(supported_rates_n)); | ||
328 | break; | ||
329 | } | ||
330 | } else { | ||
331 | /* Ad-hoc mode */ | ||
332 | switch (adapter->adhoc_start_band) { | ||
333 | case BAND_B: | ||
334 | dev_dbg(adapter->dev, "info: adhoc B\n"); | ||
335 | k = mwifiex_copy_rates(rates, k, adhoc_rates_b, | ||
336 | sizeof(adhoc_rates_b)); | ||
337 | break; | ||
338 | case BAND_G: | ||
339 | case BAND_G | BAND_GN: | ||
340 | dev_dbg(adapter->dev, "info: adhoc G only\n"); | ||
341 | k = mwifiex_copy_rates(rates, k, adhoc_rates_g, | ||
342 | sizeof(adhoc_rates_g)); | ||
343 | break; | ||
344 | case BAND_B | BAND_G: | ||
345 | case BAND_B | BAND_G | BAND_GN: | ||
346 | dev_dbg(adapter->dev, "info: adhoc BG\n"); | ||
347 | k = mwifiex_copy_rates(rates, k, adhoc_rates_bg, | ||
348 | sizeof(adhoc_rates_bg)); | ||
349 | break; | ||
350 | case BAND_A: | ||
351 | case BAND_A | BAND_AN: | ||
352 | dev_dbg(adapter->dev, "info: adhoc A\n"); | ||
353 | k = mwifiex_copy_rates(rates, k, adhoc_rates_a, | ||
354 | sizeof(adhoc_rates_a)); | ||
355 | break; | ||
356 | } | ||
357 | } | ||
358 | |||
359 | return k; | ||
360 | } | ||
diff --git a/drivers/net/wireless/mwifiex/cmdevt.c b/drivers/net/wireless/mwifiex/cmdevt.c new file mode 100644 index 000000000000..cd89fed206ae --- /dev/null +++ b/drivers/net/wireless/mwifiex/cmdevt.c | |||
@@ -0,0 +1,1414 @@ | |||
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 *data_buf) | ||
40 | { | ||
41 | cmd_node->priv = priv; | ||
42 | cmd_node->cmd_oid = cmd_oid; | ||
43 | cmd_node->wait_q_enabled = priv->adapter->cmd_wait_q_required; | ||
44 | priv->adapter->cmd_wait_q_required = false; | ||
45 | cmd_node->data_buf = data_buf; | ||
46 | cmd_node->cmd_skb = cmd_node->skb; | ||
47 | } | ||
48 | |||
49 | /* | ||
50 | * This function returns a command node from the free queue depending upon | ||
51 | * availability. | ||
52 | */ | ||
53 | static struct cmd_ctrl_node * | ||
54 | mwifiex_get_cmd_node(struct mwifiex_adapter *adapter) | ||
55 | { | ||
56 | struct cmd_ctrl_node *cmd_node; | ||
57 | unsigned long flags; | ||
58 | |||
59 | spin_lock_irqsave(&adapter->cmd_free_q_lock, flags); | ||
60 | if (list_empty(&adapter->cmd_free_q)) { | ||
61 | dev_err(adapter->dev, "GET_CMD_NODE: cmd node not available\n"); | ||
62 | spin_unlock_irqrestore(&adapter->cmd_free_q_lock, flags); | ||
63 | return NULL; | ||
64 | } | ||
65 | cmd_node = list_first_entry(&adapter->cmd_free_q, | ||
66 | struct cmd_ctrl_node, list); | ||
67 | list_del(&cmd_node->list); | ||
68 | spin_unlock_irqrestore(&adapter->cmd_free_q_lock, flags); | ||
69 | |||
70 | return cmd_node; | ||
71 | } | ||
72 | |||
73 | /* | ||
74 | * This function cleans up a command node. | ||
75 | * | ||
76 | * The function resets the fields including the buffer pointers. | ||
77 | * This function does not try to free the buffers. They must be | ||
78 | * freed before calling this function. | ||
79 | * | ||
80 | * This function will however call the receive completion callback | ||
81 | * in case a response buffer is still available before resetting | ||
82 | * the pointer. | ||
83 | */ | ||
84 | static void | ||
85 | mwifiex_clean_cmd_node(struct mwifiex_adapter *adapter, | ||
86 | struct cmd_ctrl_node *cmd_node) | ||
87 | { | ||
88 | cmd_node->cmd_oid = 0; | ||
89 | cmd_node->cmd_flag = 0; | ||
90 | cmd_node->data_buf = NULL; | ||
91 | cmd_node->wait_q_enabled = false; | ||
92 | |||
93 | if (cmd_node->resp_skb) { | ||
94 | dev_kfree_skb_any(cmd_node->resp_skb); | ||
95 | cmd_node->resp_skb = NULL; | ||
96 | } | ||
97 | } | ||
98 | |||
99 | /* | ||
100 | * This function sends a host command to the firmware. | ||
101 | * | ||
102 | * The function copies the host command into the driver command | ||
103 | * buffer, which will be transferred to the firmware later by the | ||
104 | * main thread. | ||
105 | */ | ||
106 | static int mwifiex_cmd_host_cmd(struct mwifiex_private *priv, | ||
107 | struct host_cmd_ds_command *cmd, void *data_buf) | ||
108 | { | ||
109 | struct mwifiex_ds_misc_cmd *pcmd_ptr = | ||
110 | (struct mwifiex_ds_misc_cmd *) data_buf; | ||
111 | |||
112 | /* Copy the HOST command to command buffer */ | ||
113 | memcpy((void *) cmd, pcmd_ptr->cmd, pcmd_ptr->len); | ||
114 | dev_dbg(priv->adapter->dev, "cmd: host cmd size = %d\n", pcmd_ptr->len); | ||
115 | return 0; | ||
116 | } | ||
117 | |||
118 | /* | ||
119 | * This function downloads a command to the firmware. | ||
120 | * | ||
121 | * The function performs sanity tests, sets the command sequence | ||
122 | * number and size, converts the header fields to CPU format before | ||
123 | * sending. Afterwards, it logs the command ID and action for debugging | ||
124 | * and sets up the command timeout timer. | ||
125 | */ | ||
126 | static int mwifiex_dnld_cmd_to_fw(struct mwifiex_private *priv, | ||
127 | struct cmd_ctrl_node *cmd_node) | ||
128 | { | ||
129 | |||
130 | struct mwifiex_adapter *adapter = priv->adapter; | ||
131 | int ret; | ||
132 | struct host_cmd_ds_command *host_cmd; | ||
133 | uint16_t cmd_code; | ||
134 | uint16_t cmd_size; | ||
135 | struct timeval tstamp; | ||
136 | unsigned long flags; | ||
137 | |||
138 | if (!adapter || !cmd_node) | ||
139 | return -1; | ||
140 | |||
141 | host_cmd = (struct host_cmd_ds_command *) (cmd_node->cmd_skb->data); | ||
142 | |||
143 | /* Sanity test */ | ||
144 | if (host_cmd == NULL || host_cmd->size == 0) { | ||
145 | dev_err(adapter->dev, "DNLD_CMD: host_cmd is null" | ||
146 | " or cmd size is 0, not sending\n"); | ||
147 | if (cmd_node->wait_q_enabled) | ||
148 | adapter->cmd_wait_q.status = -1; | ||
149 | mwifiex_insert_cmd_to_free_q(adapter, cmd_node); | ||
150 | return -1; | ||
151 | } | ||
152 | |||
153 | /* Set command sequence number */ | ||
154 | adapter->seq_num++; | ||
155 | host_cmd->seq_num = cpu_to_le16(HostCmd_SET_SEQ_NO_BSS_INFO | ||
156 | (adapter->seq_num, cmd_node->priv->bss_num, | ||
157 | cmd_node->priv->bss_type)); | ||
158 | |||
159 | spin_lock_irqsave(&adapter->mwifiex_cmd_lock, flags); | ||
160 | adapter->curr_cmd = cmd_node; | ||
161 | spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, flags); | ||
162 | |||
163 | cmd_code = le16_to_cpu(host_cmd->command); | ||
164 | cmd_size = le16_to_cpu(host_cmd->size); | ||
165 | |||
166 | skb_trim(cmd_node->cmd_skb, cmd_size); | ||
167 | |||
168 | do_gettimeofday(&tstamp); | ||
169 | dev_dbg(adapter->dev, "cmd: DNLD_CMD: (%lu.%lu): %#x, act %#x, len %d," | ||
170 | " seqno %#x\n", | ||
171 | tstamp.tv_sec, tstamp.tv_usec, cmd_code, | ||
172 | le16_to_cpu(*(__le16 *) ((u8 *) host_cmd + S_DS_GEN)), cmd_size, | ||
173 | le16_to_cpu(host_cmd->seq_num)); | ||
174 | |||
175 | skb_push(cmd_node->cmd_skb, INTF_HEADER_LEN); | ||
176 | |||
177 | ret = adapter->if_ops.host_to_card(adapter, MWIFIEX_TYPE_CMD, | ||
178 | cmd_node->cmd_skb->data, | ||
179 | cmd_node->cmd_skb->len, NULL); | ||
180 | |||
181 | skb_pull(cmd_node->cmd_skb, INTF_HEADER_LEN); | ||
182 | |||
183 | if (ret == -1) { | ||
184 | dev_err(adapter->dev, "DNLD_CMD: host to card failed\n"); | ||
185 | if (cmd_node->wait_q_enabled) | ||
186 | adapter->cmd_wait_q.status = -1; | ||
187 | mwifiex_insert_cmd_to_free_q(adapter, adapter->curr_cmd); | ||
188 | |||
189 | spin_lock_irqsave(&adapter->mwifiex_cmd_lock, flags); | ||
190 | adapter->curr_cmd = NULL; | ||
191 | spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, flags); | ||
192 | |||
193 | adapter->dbg.num_cmd_host_to_card_failure++; | ||
194 | return -1; | ||
195 | } | ||
196 | |||
197 | /* Save the last command id and action to debug log */ | ||
198 | adapter->dbg.last_cmd_index = | ||
199 | (adapter->dbg.last_cmd_index + 1) % DBG_CMD_NUM; | ||
200 | adapter->dbg.last_cmd_id[adapter->dbg.last_cmd_index] = cmd_code; | ||
201 | adapter->dbg.last_cmd_act[adapter->dbg.last_cmd_index] = | ||
202 | le16_to_cpu(*(__le16 *) ((u8 *) host_cmd + S_DS_GEN)); | ||
203 | |||
204 | /* Clear BSS_NO_BITS from HostCmd */ | ||
205 | cmd_code &= HostCmd_CMD_ID_MASK; | ||
206 | |||
207 | /* Setup the timer after transmit command */ | ||
208 | mod_timer(&adapter->cmd_timer, | ||
209 | jiffies + (MWIFIEX_TIMER_10S * HZ) / 1000); | ||
210 | |||
211 | return 0; | ||
212 | } | ||
213 | |||
214 | /* | ||
215 | * This function downloads a sleep confirm command to the firmware. | ||
216 | * | ||
217 | * The function performs sanity tests, sets the command sequence | ||
218 | * number and size, converts the header fields to CPU format before | ||
219 | * sending. | ||
220 | * | ||
221 | * No responses are needed for sleep confirm command. | ||
222 | */ | ||
223 | static int mwifiex_dnld_sleep_confirm_cmd(struct mwifiex_adapter *adapter) | ||
224 | { | ||
225 | int ret; | ||
226 | struct mwifiex_private *priv; | ||
227 | struct mwifiex_opt_sleep_confirm *sleep_cfm_buf = | ||
228 | (struct mwifiex_opt_sleep_confirm *) | ||
229 | adapter->sleep_cfm->data; | ||
230 | priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY); | ||
231 | |||
232 | sleep_cfm_buf->seq_num = | ||
233 | cpu_to_le16((HostCmd_SET_SEQ_NO_BSS_INFO | ||
234 | (adapter->seq_num, priv->bss_num, | ||
235 | priv->bss_type))); | ||
236 | adapter->seq_num++; | ||
237 | |||
238 | skb_push(adapter->sleep_cfm, INTF_HEADER_LEN); | ||
239 | ret = adapter->if_ops.host_to_card(adapter, MWIFIEX_TYPE_CMD, | ||
240 | adapter->sleep_cfm->data, | ||
241 | adapter->sleep_cfm->len, NULL); | ||
242 | skb_pull(adapter->sleep_cfm, INTF_HEADER_LEN); | ||
243 | |||
244 | if (ret == -1) { | ||
245 | dev_err(adapter->dev, "SLEEP_CFM: failed\n"); | ||
246 | adapter->dbg.num_cmd_sleep_cfm_host_to_card_failure++; | ||
247 | return -1; | ||
248 | } | ||
249 | if (GET_BSS_ROLE(mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY)) | ||
250 | == MWIFIEX_BSS_ROLE_STA) { | ||
251 | if (!sleep_cfm_buf->resp_ctrl) | ||
252 | /* Response is not needed for sleep | ||
253 | confirm command */ | ||
254 | adapter->ps_state = PS_STATE_SLEEP; | ||
255 | else | ||
256 | adapter->ps_state = PS_STATE_SLEEP_CFM; | ||
257 | |||
258 | if (!sleep_cfm_buf->resp_ctrl | ||
259 | && (adapter->is_hs_configured | ||
260 | && !adapter->sleep_period.period)) { | ||
261 | adapter->pm_wakeup_card_req = true; | ||
262 | mwifiex_hs_activated_event(mwifiex_get_priv(adapter, | ||
263 | MWIFIEX_BSS_ROLE_STA), true); | ||
264 | } | ||
265 | } | ||
266 | |||
267 | return ret; | ||
268 | } | ||
269 | |||
270 | /* | ||
271 | * This function allocates the command buffers and links them to | ||
272 | * the command free queue. | ||
273 | * | ||
274 | * The driver uses a pre allocated number of command buffers, which | ||
275 | * are created at driver initializations and freed at driver cleanup. | ||
276 | * Every command needs to obtain a command buffer from this pool before | ||
277 | * it can be issued. The command free queue lists the command buffers | ||
278 | * currently free to use, while the command pending queue lists the | ||
279 | * command buffers already in use and awaiting handling. Command buffers | ||
280 | * are returned to the free queue after use. | ||
281 | */ | ||
282 | int mwifiex_alloc_cmd_buffer(struct mwifiex_adapter *adapter) | ||
283 | { | ||
284 | struct cmd_ctrl_node *cmd_array; | ||
285 | u32 buf_size; | ||
286 | u32 i; | ||
287 | |||
288 | /* Allocate and initialize struct cmd_ctrl_node */ | ||
289 | buf_size = sizeof(struct cmd_ctrl_node) * MWIFIEX_NUM_OF_CMD_BUFFER; | ||
290 | cmd_array = kzalloc(buf_size, GFP_KERNEL); | ||
291 | if (!cmd_array) { | ||
292 | dev_err(adapter->dev, "%s: failed to alloc cmd_array\n", | ||
293 | __func__); | ||
294 | return -ENOMEM; | ||
295 | } | ||
296 | |||
297 | adapter->cmd_pool = cmd_array; | ||
298 | memset(adapter->cmd_pool, 0, buf_size); | ||
299 | |||
300 | /* Allocate and initialize command buffers */ | ||
301 | for (i = 0; i < MWIFIEX_NUM_OF_CMD_BUFFER; i++) { | ||
302 | cmd_array[i].skb = dev_alloc_skb(MWIFIEX_SIZE_OF_CMD_BUFFER); | ||
303 | if (!cmd_array[i].skb) { | ||
304 | dev_err(adapter->dev, "ALLOC_CMD_BUF: out of memory\n"); | ||
305 | return -1; | ||
306 | } | ||
307 | } | ||
308 | |||
309 | for (i = 0; i < MWIFIEX_NUM_OF_CMD_BUFFER; i++) | ||
310 | mwifiex_insert_cmd_to_free_q(adapter, &cmd_array[i]); | ||
311 | |||
312 | return 0; | ||
313 | } | ||
314 | |||
315 | /* | ||
316 | * This function frees the command buffers. | ||
317 | * | ||
318 | * The function calls the completion callback for all the command | ||
319 | * buffers that still have response buffers associated with them. | ||
320 | */ | ||
321 | int mwifiex_free_cmd_buffer(struct mwifiex_adapter *adapter) | ||
322 | { | ||
323 | struct cmd_ctrl_node *cmd_array; | ||
324 | u32 i; | ||
325 | |||
326 | /* Need to check if cmd pool is allocated or not */ | ||
327 | if (!adapter->cmd_pool) { | ||
328 | dev_dbg(adapter->dev, "info: FREE_CMD_BUF: cmd_pool is null\n"); | ||
329 | return 0; | ||
330 | } | ||
331 | |||
332 | cmd_array = adapter->cmd_pool; | ||
333 | |||
334 | /* Release shared memory buffers */ | ||
335 | for (i = 0; i < MWIFIEX_NUM_OF_CMD_BUFFER; i++) { | ||
336 | if (cmd_array[i].skb) { | ||
337 | dev_dbg(adapter->dev, "cmd: free cmd buffer %d\n", i); | ||
338 | dev_kfree_skb_any(cmd_array[i].skb); | ||
339 | } | ||
340 | if (!cmd_array[i].resp_skb) | ||
341 | continue; | ||
342 | dev_kfree_skb_any(cmd_array[i].resp_skb); | ||
343 | } | ||
344 | /* Release struct cmd_ctrl_node */ | ||
345 | if (adapter->cmd_pool) { | ||
346 | dev_dbg(adapter->dev, "cmd: free cmd pool\n"); | ||
347 | kfree(adapter->cmd_pool); | ||
348 | adapter->cmd_pool = NULL; | ||
349 | } | ||
350 | |||
351 | return 0; | ||
352 | } | ||
353 | |||
354 | /* | ||
355 | * This function handles events generated by firmware. | ||
356 | * | ||
357 | * Event body of events received from firmware are not used (though they are | ||
358 | * saved), only the event ID is used. Some events are re-invoked by | ||
359 | * the driver, with a new event body. | ||
360 | * | ||
361 | * After processing, the function calls the completion callback | ||
362 | * for cleanup. | ||
363 | */ | ||
364 | int mwifiex_process_event(struct mwifiex_adapter *adapter) | ||
365 | { | ||
366 | int ret; | ||
367 | struct mwifiex_private *priv = | ||
368 | mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY); | ||
369 | struct sk_buff *skb = adapter->event_skb; | ||
370 | u32 eventcause = adapter->event_cause; | ||
371 | struct timeval tstamp; | ||
372 | struct mwifiex_rxinfo *rx_info; | ||
373 | |||
374 | /* Save the last event to debug log */ | ||
375 | adapter->dbg.last_event_index = | ||
376 | (adapter->dbg.last_event_index + 1) % DBG_CMD_NUM; | ||
377 | adapter->dbg.last_event[adapter->dbg.last_event_index] = | ||
378 | (u16) eventcause; | ||
379 | |||
380 | /* Get BSS number and corresponding priv */ | ||
381 | priv = mwifiex_get_priv_by_id(adapter, EVENT_GET_BSS_NUM(eventcause), | ||
382 | EVENT_GET_BSS_TYPE(eventcause)); | ||
383 | if (!priv) | ||
384 | priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY); | ||
385 | /* Clear BSS_NO_BITS from event */ | ||
386 | eventcause &= EVENT_ID_MASK; | ||
387 | adapter->event_cause = eventcause; | ||
388 | |||
389 | if (skb) { | ||
390 | rx_info = MWIFIEX_SKB_RXCB(skb); | ||
391 | rx_info->bss_index = priv->bss_index; | ||
392 | } | ||
393 | |||
394 | if (eventcause != EVENT_PS_SLEEP && eventcause != EVENT_PS_AWAKE) { | ||
395 | do_gettimeofday(&tstamp); | ||
396 | dev_dbg(adapter->dev, "event: %lu.%lu: cause: %#x\n", | ||
397 | tstamp.tv_sec, tstamp.tv_usec, eventcause); | ||
398 | } | ||
399 | |||
400 | ret = mwifiex_process_sta_event(priv); | ||
401 | |||
402 | adapter->event_cause = 0; | ||
403 | adapter->event_skb = NULL; | ||
404 | |||
405 | dev_kfree_skb_any(skb); | ||
406 | |||
407 | return ret; | ||
408 | } | ||
409 | |||
410 | /* | ||
411 | * This function is used to send synchronous command to the firmware. | ||
412 | * | ||
413 | * it allocates a wait queue for the command and wait for the command | ||
414 | * response. | ||
415 | */ | ||
416 | int mwifiex_send_cmd_sync(struct mwifiex_private *priv, uint16_t cmd_no, | ||
417 | u16 cmd_action, u32 cmd_oid, void *data_buf) | ||
418 | { | ||
419 | int ret = 0; | ||
420 | struct mwifiex_adapter *adapter = priv->adapter; | ||
421 | |||
422 | adapter->cmd_wait_q_required = true; | ||
423 | adapter->cmd_wait_q.condition = false; | ||
424 | |||
425 | ret = mwifiex_send_cmd_async(priv, cmd_no, cmd_action, cmd_oid, | ||
426 | data_buf); | ||
427 | if (!ret) | ||
428 | ret = mwifiex_wait_queue_complete(adapter); | ||
429 | |||
430 | return ret; | ||
431 | } | ||
432 | |||
433 | |||
434 | /* | ||
435 | * This function prepares a command and asynchronously send it to the firmware. | ||
436 | * | ||
437 | * Preparation includes - | ||
438 | * - Sanity tests to make sure the card is still present or the FW | ||
439 | * is not reset | ||
440 | * - Getting a new command node from the command free queue | ||
441 | * - Initializing the command node for default parameters | ||
442 | * - Fill up the non-default parameters and buffer pointers | ||
443 | * - Add the command to pending queue | ||
444 | */ | ||
445 | int mwifiex_send_cmd_async(struct mwifiex_private *priv, uint16_t cmd_no, | ||
446 | u16 cmd_action, u32 cmd_oid, void *data_buf) | ||
447 | { | ||
448 | int ret; | ||
449 | struct mwifiex_adapter *adapter = priv->adapter; | ||
450 | struct cmd_ctrl_node *cmd_node; | ||
451 | struct host_cmd_ds_command *cmd_ptr; | ||
452 | |||
453 | if (!adapter) { | ||
454 | pr_err("PREP_CMD: adapter is NULL\n"); | ||
455 | return -1; | ||
456 | } | ||
457 | |||
458 | if (adapter->is_suspended) { | ||
459 | dev_err(adapter->dev, "PREP_CMD: device in suspended state\n"); | ||
460 | return -1; | ||
461 | } | ||
462 | |||
463 | if (adapter->surprise_removed) { | ||
464 | dev_err(adapter->dev, "PREP_CMD: card is removed\n"); | ||
465 | return -1; | ||
466 | } | ||
467 | |||
468 | if (adapter->hw_status == MWIFIEX_HW_STATUS_RESET) { | ||
469 | if (cmd_no != HostCmd_CMD_FUNC_INIT) { | ||
470 | dev_err(adapter->dev, "PREP_CMD: FW in reset state\n"); | ||
471 | return -1; | ||
472 | } | ||
473 | } | ||
474 | |||
475 | /* Get a new command node */ | ||
476 | cmd_node = mwifiex_get_cmd_node(adapter); | ||
477 | |||
478 | if (!cmd_node) { | ||
479 | dev_err(adapter->dev, "PREP_CMD: no free cmd node\n"); | ||
480 | return -1; | ||
481 | } | ||
482 | |||
483 | /* Initialize the command node */ | ||
484 | mwifiex_init_cmd_node(priv, cmd_node, cmd_oid, data_buf); | ||
485 | |||
486 | if (!cmd_node->cmd_skb) { | ||
487 | dev_err(adapter->dev, "PREP_CMD: no free cmd buf\n"); | ||
488 | return -1; | ||
489 | } | ||
490 | |||
491 | memset(skb_put(cmd_node->cmd_skb, sizeof(struct host_cmd_ds_command)), | ||
492 | 0, sizeof(struct host_cmd_ds_command)); | ||
493 | |||
494 | cmd_ptr = (struct host_cmd_ds_command *) (cmd_node->cmd_skb->data); | ||
495 | cmd_ptr->command = cpu_to_le16(cmd_no); | ||
496 | cmd_ptr->result = 0; | ||
497 | |||
498 | /* Prepare command */ | ||
499 | if (cmd_no) { | ||
500 | ret = mwifiex_sta_prepare_cmd(priv, cmd_no, cmd_action, | ||
501 | cmd_oid, data_buf, cmd_ptr); | ||
502 | } else { | ||
503 | ret = mwifiex_cmd_host_cmd(priv, cmd_ptr, data_buf); | ||
504 | cmd_node->cmd_flag |= CMD_F_HOSTCMD; | ||
505 | } | ||
506 | |||
507 | /* Return error, since the command preparation failed */ | ||
508 | if (ret) { | ||
509 | dev_err(adapter->dev, "PREP_CMD: cmd %#x preparation failed\n", | ||
510 | cmd_no); | ||
511 | mwifiex_insert_cmd_to_free_q(adapter, cmd_node); | ||
512 | return -1; | ||
513 | } | ||
514 | |||
515 | /* Send command */ | ||
516 | if (cmd_no == HostCmd_CMD_802_11_SCAN) | ||
517 | mwifiex_queue_scan_cmd(priv, cmd_node); | ||
518 | else | ||
519 | mwifiex_insert_cmd_to_pending_q(adapter, cmd_node, true); | ||
520 | |||
521 | return ret; | ||
522 | } | ||
523 | |||
524 | /* | ||
525 | * This function returns a command to the command free queue. | ||
526 | * | ||
527 | * The function also calls the completion callback if required, before | ||
528 | * cleaning the command node and re-inserting it into the free queue. | ||
529 | */ | ||
530 | void | ||
531 | mwifiex_insert_cmd_to_free_q(struct mwifiex_adapter *adapter, | ||
532 | struct cmd_ctrl_node *cmd_node) | ||
533 | { | ||
534 | unsigned long flags; | ||
535 | |||
536 | if (!cmd_node) | ||
537 | return; | ||
538 | |||
539 | if (cmd_node->wait_q_enabled) | ||
540 | mwifiex_complete_cmd(adapter); | ||
541 | /* Clean the node */ | ||
542 | mwifiex_clean_cmd_node(adapter, cmd_node); | ||
543 | |||
544 | /* Insert node into cmd_free_q */ | ||
545 | spin_lock_irqsave(&adapter->cmd_free_q_lock, flags); | ||
546 | list_add_tail(&cmd_node->list, &adapter->cmd_free_q); | ||
547 | spin_unlock_irqrestore(&adapter->cmd_free_q_lock, flags); | ||
548 | } | ||
549 | |||
550 | /* | ||
551 | * This function queues a command to the command pending queue. | ||
552 | * | ||
553 | * This in effect adds the command to the command list to be executed. | ||
554 | * Exit PS command is handled specially, by placing it always to the | ||
555 | * front of the command queue. | ||
556 | */ | ||
557 | void | ||
558 | mwifiex_insert_cmd_to_pending_q(struct mwifiex_adapter *adapter, | ||
559 | struct cmd_ctrl_node *cmd_node, u32 add_tail) | ||
560 | { | ||
561 | struct host_cmd_ds_command *host_cmd = NULL; | ||
562 | u16 command; | ||
563 | unsigned long flags; | ||
564 | |||
565 | host_cmd = (struct host_cmd_ds_command *) (cmd_node->cmd_skb->data); | ||
566 | if (!host_cmd) { | ||
567 | dev_err(adapter->dev, "QUEUE_CMD: host_cmd is NULL\n"); | ||
568 | return; | ||
569 | } | ||
570 | |||
571 | command = le16_to_cpu(host_cmd->command); | ||
572 | |||
573 | /* Exit_PS command needs to be queued in the header always. */ | ||
574 | if (command == HostCmd_CMD_802_11_PS_MODE_ENH) { | ||
575 | struct host_cmd_ds_802_11_ps_mode_enh *pm = | ||
576 | &host_cmd->params.psmode_enh; | ||
577 | if ((le16_to_cpu(pm->action) == DIS_PS) | ||
578 | || (le16_to_cpu(pm->action) == DIS_AUTO_PS)) { | ||
579 | if (adapter->ps_state != PS_STATE_AWAKE) | ||
580 | add_tail = false; | ||
581 | } | ||
582 | } | ||
583 | |||
584 | spin_lock_irqsave(&adapter->cmd_pending_q_lock, flags); | ||
585 | if (add_tail) | ||
586 | list_add_tail(&cmd_node->list, &adapter->cmd_pending_q); | ||
587 | else | ||
588 | list_add(&cmd_node->list, &adapter->cmd_pending_q); | ||
589 | spin_unlock_irqrestore(&adapter->cmd_pending_q_lock, flags); | ||
590 | |||
591 | dev_dbg(adapter->dev, "cmd: QUEUE_CMD: cmd=%#x is queued\n", command); | ||
592 | } | ||
593 | |||
594 | /* | ||
595 | * This function executes the next command in command pending queue. | ||
596 | * | ||
597 | * This function will fail if a command is already in processing stage, | ||
598 | * otherwise it will dequeue the first command from the command pending | ||
599 | * queue and send to the firmware. | ||
600 | * | ||
601 | * If the device is currently in host sleep mode, any commands, except the | ||
602 | * host sleep configuration command will de-activate the host sleep. For PS | ||
603 | * mode, the function will put the firmware back to sleep if applicable. | ||
604 | */ | ||
605 | int mwifiex_exec_next_cmd(struct mwifiex_adapter *adapter) | ||
606 | { | ||
607 | struct mwifiex_private *priv; | ||
608 | struct cmd_ctrl_node *cmd_node; | ||
609 | int ret = 0; | ||
610 | struct host_cmd_ds_command *host_cmd; | ||
611 | unsigned long cmd_flags; | ||
612 | unsigned long cmd_pending_q_flags; | ||
613 | |||
614 | /* Check if already in processing */ | ||
615 | if (adapter->curr_cmd) { | ||
616 | dev_err(adapter->dev, "EXEC_NEXT_CMD: cmd in processing\n"); | ||
617 | return -1; | ||
618 | } | ||
619 | |||
620 | spin_lock_irqsave(&adapter->mwifiex_cmd_lock, cmd_flags); | ||
621 | /* Check if any command is pending */ | ||
622 | spin_lock_irqsave(&adapter->cmd_pending_q_lock, cmd_pending_q_flags); | ||
623 | if (list_empty(&adapter->cmd_pending_q)) { | ||
624 | spin_unlock_irqrestore(&adapter->cmd_pending_q_lock, | ||
625 | cmd_pending_q_flags); | ||
626 | spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, cmd_flags); | ||
627 | return 0; | ||
628 | } | ||
629 | cmd_node = list_first_entry(&adapter->cmd_pending_q, | ||
630 | struct cmd_ctrl_node, list); | ||
631 | spin_unlock_irqrestore(&adapter->cmd_pending_q_lock, | ||
632 | cmd_pending_q_flags); | ||
633 | |||
634 | host_cmd = (struct host_cmd_ds_command *) (cmd_node->cmd_skb->data); | ||
635 | priv = cmd_node->priv; | ||
636 | |||
637 | if (adapter->ps_state != PS_STATE_AWAKE) { | ||
638 | dev_err(adapter->dev, "%s: cannot send cmd in sleep state," | ||
639 | " this should not happen\n", __func__); | ||
640 | spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, cmd_flags); | ||
641 | return ret; | ||
642 | } | ||
643 | |||
644 | spin_lock_irqsave(&adapter->cmd_pending_q_lock, cmd_pending_q_flags); | ||
645 | list_del(&cmd_node->list); | ||
646 | spin_unlock_irqrestore(&adapter->cmd_pending_q_lock, | ||
647 | cmd_pending_q_flags); | ||
648 | |||
649 | spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, cmd_flags); | ||
650 | ret = mwifiex_dnld_cmd_to_fw(priv, cmd_node); | ||
651 | priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY); | ||
652 | /* Any command sent to the firmware when host is in sleep | ||
653 | * mode should de-configure host sleep. We should skip the | ||
654 | * host sleep configuration command itself though | ||
655 | */ | ||
656 | if (priv && (host_cmd->command != | ||
657 | cpu_to_le16(HostCmd_CMD_802_11_HS_CFG_ENH))) { | ||
658 | if (adapter->hs_activated) { | ||
659 | adapter->is_hs_configured = false; | ||
660 | mwifiex_hs_activated_event(priv, false); | ||
661 | } | ||
662 | } | ||
663 | |||
664 | return ret; | ||
665 | } | ||
666 | |||
667 | /* | ||
668 | * This function handles the command response. | ||
669 | * | ||
670 | * After processing, the function cleans the command node and puts | ||
671 | * it back to the command free queue. | ||
672 | */ | ||
673 | int mwifiex_process_cmdresp(struct mwifiex_adapter *adapter) | ||
674 | { | ||
675 | struct host_cmd_ds_command *resp; | ||
676 | struct mwifiex_private *priv = | ||
677 | mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY); | ||
678 | int ret = 0; | ||
679 | uint16_t orig_cmdresp_no; | ||
680 | uint16_t cmdresp_no; | ||
681 | uint16_t cmdresp_result; | ||
682 | struct timeval tstamp; | ||
683 | unsigned long flags; | ||
684 | |||
685 | /* Now we got response from FW, cancel the command timer */ | ||
686 | del_timer(&adapter->cmd_timer); | ||
687 | |||
688 | if (!adapter->curr_cmd || !adapter->curr_cmd->resp_skb) { | ||
689 | resp = (struct host_cmd_ds_command *) adapter->upld_buf; | ||
690 | dev_err(adapter->dev, "CMD_RESP: NULL curr_cmd, %#x\n", | ||
691 | le16_to_cpu(resp->command)); | ||
692 | return -1; | ||
693 | } | ||
694 | |||
695 | adapter->num_cmd_timeout = 0; | ||
696 | |||
697 | resp = (struct host_cmd_ds_command *) adapter->curr_cmd->resp_skb->data; | ||
698 | if (adapter->curr_cmd->cmd_flag & CMD_F_CANCELED) { | ||
699 | dev_err(adapter->dev, "CMD_RESP: %#x been canceled\n", | ||
700 | le16_to_cpu(resp->command)); | ||
701 | mwifiex_insert_cmd_to_free_q(adapter, adapter->curr_cmd); | ||
702 | spin_lock_irqsave(&adapter->mwifiex_cmd_lock, flags); | ||
703 | adapter->curr_cmd = NULL; | ||
704 | spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, flags); | ||
705 | return -1; | ||
706 | } | ||
707 | |||
708 | if (adapter->curr_cmd->cmd_flag & CMD_F_HOSTCMD) { | ||
709 | /* Copy original response back to response buffer */ | ||
710 | struct mwifiex_ds_misc_cmd *hostcmd = NULL; | ||
711 | uint16_t size = le16_to_cpu(resp->size); | ||
712 | dev_dbg(adapter->dev, "info: host cmd resp size = %d\n", size); | ||
713 | size = min_t(u16, size, MWIFIEX_SIZE_OF_CMD_BUFFER); | ||
714 | if (adapter->curr_cmd->data_buf) { | ||
715 | hostcmd = (struct mwifiex_ds_misc_cmd *) | ||
716 | adapter->curr_cmd->data_buf; | ||
717 | hostcmd->len = size; | ||
718 | memcpy(hostcmd->cmd, (void *) resp, size); | ||
719 | } | ||
720 | } | ||
721 | orig_cmdresp_no = le16_to_cpu(resp->command); | ||
722 | |||
723 | /* Get BSS number and corresponding priv */ | ||
724 | priv = mwifiex_get_priv_by_id(adapter, | ||
725 | HostCmd_GET_BSS_NO(le16_to_cpu(resp->seq_num)), | ||
726 | HostCmd_GET_BSS_TYPE(le16_to_cpu(resp->seq_num))); | ||
727 | if (!priv) | ||
728 | priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY); | ||
729 | /* Clear RET_BIT from HostCmd */ | ||
730 | resp->command = cpu_to_le16(orig_cmdresp_no & HostCmd_CMD_ID_MASK); | ||
731 | |||
732 | cmdresp_no = le16_to_cpu(resp->command); | ||
733 | cmdresp_result = le16_to_cpu(resp->result); | ||
734 | |||
735 | /* Save the last command response to debug log */ | ||
736 | adapter->dbg.last_cmd_resp_index = | ||
737 | (adapter->dbg.last_cmd_resp_index + 1) % DBG_CMD_NUM; | ||
738 | adapter->dbg.last_cmd_resp_id[adapter->dbg.last_cmd_resp_index] = | ||
739 | orig_cmdresp_no; | ||
740 | |||
741 | do_gettimeofday(&tstamp); | ||
742 | dev_dbg(adapter->dev, "cmd: CMD_RESP: (%lu.%lu): 0x%x, result %d," | ||
743 | " len %d, seqno 0x%x\n", | ||
744 | tstamp.tv_sec, tstamp.tv_usec, orig_cmdresp_no, cmdresp_result, | ||
745 | le16_to_cpu(resp->size), le16_to_cpu(resp->seq_num)); | ||
746 | |||
747 | if (!(orig_cmdresp_no & HostCmd_RET_BIT)) { | ||
748 | dev_err(adapter->dev, "CMD_RESP: invalid cmd resp\n"); | ||
749 | if (adapter->curr_cmd->wait_q_enabled) | ||
750 | adapter->cmd_wait_q.status = -1; | ||
751 | |||
752 | mwifiex_insert_cmd_to_free_q(adapter, adapter->curr_cmd); | ||
753 | spin_lock_irqsave(&adapter->mwifiex_cmd_lock, flags); | ||
754 | adapter->curr_cmd = NULL; | ||
755 | spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, flags); | ||
756 | return -1; | ||
757 | } | ||
758 | |||
759 | if (adapter->curr_cmd->cmd_flag & CMD_F_HOSTCMD) { | ||
760 | adapter->curr_cmd->cmd_flag &= ~CMD_F_HOSTCMD; | ||
761 | if ((cmdresp_result == HostCmd_RESULT_OK) | ||
762 | && (cmdresp_no == HostCmd_CMD_802_11_HS_CFG_ENH)) | ||
763 | ret = mwifiex_ret_802_11_hs_cfg(priv, resp); | ||
764 | } else { | ||
765 | /* handle response */ | ||
766 | ret = mwifiex_process_sta_cmdresp(priv, cmdresp_no, resp); | ||
767 | } | ||
768 | |||
769 | /* Check init command response */ | ||
770 | if (adapter->hw_status == MWIFIEX_HW_STATUS_INITIALIZING) { | ||
771 | if (ret == -1) { | ||
772 | dev_err(adapter->dev, "%s: cmd %#x failed during " | ||
773 | "initialization\n", __func__, cmdresp_no); | ||
774 | mwifiex_init_fw_complete(adapter); | ||
775 | return -1; | ||
776 | } else if (adapter->last_init_cmd == cmdresp_no) | ||
777 | adapter->hw_status = MWIFIEX_HW_STATUS_INIT_DONE; | ||
778 | } | ||
779 | |||
780 | if (adapter->curr_cmd) { | ||
781 | if (adapter->curr_cmd->wait_q_enabled && (!ret)) | ||
782 | adapter->cmd_wait_q.status = 0; | ||
783 | else if (adapter->curr_cmd->wait_q_enabled && (ret == -1)) | ||
784 | adapter->cmd_wait_q.status = -1; | ||
785 | |||
786 | /* Clean up and put current command back to cmd_free_q */ | ||
787 | mwifiex_insert_cmd_to_free_q(adapter, adapter->curr_cmd); | ||
788 | |||
789 | spin_lock_irqsave(&adapter->mwifiex_cmd_lock, flags); | ||
790 | adapter->curr_cmd = NULL; | ||
791 | spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, flags); | ||
792 | } | ||
793 | |||
794 | return ret; | ||
795 | } | ||
796 | |||
797 | /* | ||
798 | * This function handles the timeout of command sending. | ||
799 | * | ||
800 | * It will re-send the same command again. | ||
801 | */ | ||
802 | void | ||
803 | mwifiex_cmd_timeout_func(unsigned long function_context) | ||
804 | { | ||
805 | struct mwifiex_adapter *adapter = | ||
806 | (struct mwifiex_adapter *) function_context; | ||
807 | struct cmd_ctrl_node *cmd_node; | ||
808 | struct timeval tstamp; | ||
809 | |||
810 | adapter->num_cmd_timeout++; | ||
811 | adapter->dbg.num_cmd_timeout++; | ||
812 | if (!adapter->curr_cmd) { | ||
813 | dev_dbg(adapter->dev, "cmd: empty curr_cmd\n"); | ||
814 | return; | ||
815 | } | ||
816 | cmd_node = adapter->curr_cmd; | ||
817 | if (cmd_node->wait_q_enabled) | ||
818 | adapter->cmd_wait_q.status = -ETIMEDOUT; | ||
819 | |||
820 | if (cmd_node) { | ||
821 | adapter->dbg.timeout_cmd_id = | ||
822 | adapter->dbg.last_cmd_id[adapter->dbg.last_cmd_index]; | ||
823 | adapter->dbg.timeout_cmd_act = | ||
824 | adapter->dbg.last_cmd_act[adapter->dbg.last_cmd_index]; | ||
825 | do_gettimeofday(&tstamp); | ||
826 | dev_err(adapter->dev, "%s: Timeout cmd id (%lu.%lu) = %#x," | ||
827 | " act = %#x\n", __func__, | ||
828 | tstamp.tv_sec, tstamp.tv_usec, | ||
829 | adapter->dbg.timeout_cmd_id, | ||
830 | adapter->dbg.timeout_cmd_act); | ||
831 | |||
832 | dev_err(adapter->dev, "num_data_h2c_failure = %d\n", | ||
833 | adapter->dbg.num_tx_host_to_card_failure); | ||
834 | dev_err(adapter->dev, "num_cmd_h2c_failure = %d\n", | ||
835 | adapter->dbg.num_cmd_host_to_card_failure); | ||
836 | |||
837 | dev_err(adapter->dev, "num_cmd_timeout = %d\n", | ||
838 | adapter->dbg.num_cmd_timeout); | ||
839 | dev_err(adapter->dev, "num_tx_timeout = %d\n", | ||
840 | adapter->dbg.num_tx_timeout); | ||
841 | |||
842 | dev_err(adapter->dev, "last_cmd_index = %d\n", | ||
843 | adapter->dbg.last_cmd_index); | ||
844 | print_hex_dump_bytes("last_cmd_id: ", DUMP_PREFIX_OFFSET, | ||
845 | adapter->dbg.last_cmd_id, DBG_CMD_NUM); | ||
846 | print_hex_dump_bytes("last_cmd_act: ", DUMP_PREFIX_OFFSET, | ||
847 | adapter->dbg.last_cmd_act, DBG_CMD_NUM); | ||
848 | |||
849 | dev_err(adapter->dev, "last_cmd_resp_index = %d\n", | ||
850 | adapter->dbg.last_cmd_resp_index); | ||
851 | print_hex_dump_bytes("last_cmd_resp_id: ", DUMP_PREFIX_OFFSET, | ||
852 | adapter->dbg.last_cmd_resp_id, DBG_CMD_NUM); | ||
853 | |||
854 | dev_err(adapter->dev, "last_event_index = %d\n", | ||
855 | adapter->dbg.last_event_index); | ||
856 | print_hex_dump_bytes("last_event: ", DUMP_PREFIX_OFFSET, | ||
857 | adapter->dbg.last_event, DBG_CMD_NUM); | ||
858 | |||
859 | dev_err(adapter->dev, "data_sent=%d cmd_sent=%d\n", | ||
860 | adapter->data_sent, adapter->cmd_sent); | ||
861 | |||
862 | dev_err(adapter->dev, "ps_mode=%d ps_state=%d\n", | ||
863 | adapter->ps_mode, adapter->ps_state); | ||
864 | } | ||
865 | if (adapter->hw_status == MWIFIEX_HW_STATUS_INITIALIZING) | ||
866 | mwifiex_init_fw_complete(adapter); | ||
867 | } | ||
868 | |||
869 | /* | ||
870 | * This function cancels all the pending commands. | ||
871 | * | ||
872 | * The current command, all commands in command pending queue and all scan | ||
873 | * commands in scan pending queue are cancelled. All the completion callbacks | ||
874 | * are called with failure status to ensure cleanup. | ||
875 | */ | ||
876 | void | ||
877 | mwifiex_cancel_all_pending_cmd(struct mwifiex_adapter *adapter) | ||
878 | { | ||
879 | struct cmd_ctrl_node *cmd_node = NULL, *tmp_node; | ||
880 | unsigned long flags; | ||
881 | |||
882 | /* Cancel current cmd */ | ||
883 | if ((adapter->curr_cmd) && (adapter->curr_cmd->wait_q_enabled)) { | ||
884 | spin_lock_irqsave(&adapter->mwifiex_cmd_lock, flags); | ||
885 | adapter->curr_cmd->wait_q_enabled = false; | ||
886 | spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, flags); | ||
887 | adapter->cmd_wait_q.status = -1; | ||
888 | mwifiex_complete_cmd(adapter); | ||
889 | } | ||
890 | /* Cancel all pending command */ | ||
891 | spin_lock_irqsave(&adapter->cmd_pending_q_lock, flags); | ||
892 | list_for_each_entry_safe(cmd_node, tmp_node, | ||
893 | &adapter->cmd_pending_q, list) { | ||
894 | list_del(&cmd_node->list); | ||
895 | spin_unlock_irqrestore(&adapter->cmd_pending_q_lock, flags); | ||
896 | |||
897 | if (cmd_node->wait_q_enabled) { | ||
898 | adapter->cmd_wait_q.status = -1; | ||
899 | mwifiex_complete_cmd(adapter); | ||
900 | cmd_node->wait_q_enabled = false; | ||
901 | } | ||
902 | mwifiex_insert_cmd_to_free_q(adapter, cmd_node); | ||
903 | spin_lock_irqsave(&adapter->cmd_pending_q_lock, flags); | ||
904 | } | ||
905 | spin_unlock_irqrestore(&adapter->cmd_pending_q_lock, flags); | ||
906 | |||
907 | /* Cancel all pending scan command */ | ||
908 | spin_lock_irqsave(&adapter->scan_pending_q_lock, flags); | ||
909 | list_for_each_entry_safe(cmd_node, tmp_node, | ||
910 | &adapter->scan_pending_q, list) { | ||
911 | list_del(&cmd_node->list); | ||
912 | spin_unlock_irqrestore(&adapter->scan_pending_q_lock, flags); | ||
913 | |||
914 | cmd_node->wait_q_enabled = false; | ||
915 | mwifiex_insert_cmd_to_free_q(adapter, cmd_node); | ||
916 | spin_lock_irqsave(&adapter->scan_pending_q_lock, flags); | ||
917 | } | ||
918 | spin_unlock_irqrestore(&adapter->scan_pending_q_lock, flags); | ||
919 | |||
920 | spin_lock_irqsave(&adapter->mwifiex_cmd_lock, flags); | ||
921 | adapter->scan_processing = false; | ||
922 | spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, flags); | ||
923 | } | ||
924 | |||
925 | /* | ||
926 | * This function cancels all pending commands that matches with | ||
927 | * the given IOCTL request. | ||
928 | * | ||
929 | * Both the current command buffer and the pending command queue are | ||
930 | * searched for matching IOCTL request. The completion callback of | ||
931 | * the matched command is called with failure status to ensure cleanup. | ||
932 | * In case of scan commands, all pending commands in scan pending queue | ||
933 | * are cancelled. | ||
934 | */ | ||
935 | void | ||
936 | mwifiex_cancel_pending_ioctl(struct mwifiex_adapter *adapter) | ||
937 | { | ||
938 | struct cmd_ctrl_node *cmd_node = NULL, *tmp_node = NULL; | ||
939 | unsigned long cmd_flags; | ||
940 | unsigned long cmd_pending_q_flags; | ||
941 | unsigned long scan_pending_q_flags; | ||
942 | uint16_t cancel_scan_cmd = false; | ||
943 | |||
944 | if ((adapter->curr_cmd) && | ||
945 | (adapter->curr_cmd->wait_q_enabled)) { | ||
946 | spin_lock_irqsave(&adapter->mwifiex_cmd_lock, cmd_flags); | ||
947 | cmd_node = adapter->curr_cmd; | ||
948 | cmd_node->wait_q_enabled = false; | ||
949 | cmd_node->cmd_flag |= CMD_F_CANCELED; | ||
950 | spin_lock_irqsave(&adapter->cmd_pending_q_lock, | ||
951 | cmd_pending_q_flags); | ||
952 | list_del(&cmd_node->list); | ||
953 | spin_unlock_irqrestore(&adapter->cmd_pending_q_lock, | ||
954 | cmd_pending_q_flags); | ||
955 | mwifiex_insert_cmd_to_free_q(adapter, cmd_node); | ||
956 | spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, cmd_flags); | ||
957 | } | ||
958 | |||
959 | /* Cancel all pending scan command */ | ||
960 | spin_lock_irqsave(&adapter->scan_pending_q_lock, | ||
961 | scan_pending_q_flags); | ||
962 | list_for_each_entry_safe(cmd_node, tmp_node, | ||
963 | &adapter->scan_pending_q, list) { | ||
964 | list_del(&cmd_node->list); | ||
965 | spin_unlock_irqrestore(&adapter->scan_pending_q_lock, | ||
966 | scan_pending_q_flags); | ||
967 | cmd_node->wait_q_enabled = false; | ||
968 | mwifiex_insert_cmd_to_free_q(adapter, cmd_node); | ||
969 | spin_lock_irqsave(&adapter->scan_pending_q_lock, | ||
970 | scan_pending_q_flags); | ||
971 | cancel_scan_cmd = true; | ||
972 | } | ||
973 | spin_unlock_irqrestore(&adapter->scan_pending_q_lock, | ||
974 | scan_pending_q_flags); | ||
975 | |||
976 | if (cancel_scan_cmd) { | ||
977 | spin_lock_irqsave(&adapter->mwifiex_cmd_lock, cmd_flags); | ||
978 | adapter->scan_processing = false; | ||
979 | spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, cmd_flags); | ||
980 | } | ||
981 | adapter->cmd_wait_q.status = -1; | ||
982 | mwifiex_complete_cmd(adapter); | ||
983 | } | ||
984 | |||
985 | /* | ||
986 | * This function sends the sleep confirm command to firmware, if | ||
987 | * possible. | ||
988 | * | ||
989 | * The sleep confirm command cannot be issued if command response, | ||
990 | * data response or event response is awaiting handling, or if we | ||
991 | * are in the middle of sending a command, or expecting a command | ||
992 | * response. | ||
993 | */ | ||
994 | void | ||
995 | mwifiex_check_ps_cond(struct mwifiex_adapter *adapter) | ||
996 | { | ||
997 | if (!adapter->cmd_sent && | ||
998 | !adapter->curr_cmd && !IS_CARD_RX_RCVD(adapter)) | ||
999 | mwifiex_dnld_sleep_confirm_cmd(adapter); | ||
1000 | else | ||
1001 | dev_dbg(adapter->dev, | ||
1002 | "cmd: Delay Sleep Confirm (%s%s%s)\n", | ||
1003 | (adapter->cmd_sent) ? "D" : "", | ||
1004 | (adapter->curr_cmd) ? "C" : "", | ||
1005 | (IS_CARD_RX_RCVD(adapter)) ? "R" : ""); | ||
1006 | } | ||
1007 | |||
1008 | /* | ||
1009 | * This function sends a Host Sleep activated event to applications. | ||
1010 | * | ||
1011 | * This event is generated by the driver, with a blank event body. | ||
1012 | */ | ||
1013 | void | ||
1014 | mwifiex_hs_activated_event(struct mwifiex_private *priv, u8 activated) | ||
1015 | { | ||
1016 | if (activated) { | ||
1017 | if (priv->adapter->is_hs_configured) { | ||
1018 | priv->adapter->hs_activated = true; | ||
1019 | dev_dbg(priv->adapter->dev, "event: hs_activated\n"); | ||
1020 | priv->adapter->hs_activate_wait_q_woken = true; | ||
1021 | wake_up_interruptible( | ||
1022 | &priv->adapter->hs_activate_wait_q); | ||
1023 | } else { | ||
1024 | dev_dbg(priv->adapter->dev, "event: HS not configured\n"); | ||
1025 | } | ||
1026 | } else { | ||
1027 | dev_dbg(priv->adapter->dev, "event: hs_deactivated\n"); | ||
1028 | priv->adapter->hs_activated = false; | ||
1029 | } | ||
1030 | } | ||
1031 | |||
1032 | /* | ||
1033 | * This function handles the command response of a Host Sleep configuration | ||
1034 | * command. | ||
1035 | * | ||
1036 | * Handling includes changing the header fields into CPU format | ||
1037 | * and setting the current host sleep activation status in driver. | ||
1038 | * | ||
1039 | * In case host sleep status change, the function generates an event to | ||
1040 | * notify the applications. | ||
1041 | */ | ||
1042 | int mwifiex_ret_802_11_hs_cfg(struct mwifiex_private *priv, | ||
1043 | struct host_cmd_ds_command *resp) | ||
1044 | { | ||
1045 | struct mwifiex_adapter *adapter = priv->adapter; | ||
1046 | struct host_cmd_ds_802_11_hs_cfg_enh *phs_cfg = | ||
1047 | &resp->params.opt_hs_cfg; | ||
1048 | uint32_t conditions = le32_to_cpu(phs_cfg->params.hs_config.conditions); | ||
1049 | |||
1050 | if (phs_cfg->action == cpu_to_le16(HS_ACTIVATE)) { | ||
1051 | mwifiex_hs_activated_event(priv, true); | ||
1052 | return 0; | ||
1053 | } else { | ||
1054 | dev_dbg(adapter->dev, "cmd: CMD_RESP: HS_CFG cmd reply" | ||
1055 | " result=%#x, conditions=0x%x gpio=0x%x gap=0x%x\n", | ||
1056 | resp->result, conditions, | ||
1057 | phs_cfg->params.hs_config.gpio, | ||
1058 | phs_cfg->params.hs_config.gap); | ||
1059 | } | ||
1060 | if (conditions != HOST_SLEEP_CFG_CANCEL) { | ||
1061 | adapter->is_hs_configured = true; | ||
1062 | } else { | ||
1063 | adapter->is_hs_configured = false; | ||
1064 | if (adapter->hs_activated) | ||
1065 | mwifiex_hs_activated_event(priv, false); | ||
1066 | } | ||
1067 | |||
1068 | return 0; | ||
1069 | } | ||
1070 | |||
1071 | /* | ||
1072 | * This function wakes up the adapter and generates a Host Sleep | ||
1073 | * cancel event on receiving the power up interrupt. | ||
1074 | */ | ||
1075 | void | ||
1076 | mwifiex_process_hs_config(struct mwifiex_adapter *adapter) | ||
1077 | { | ||
1078 | dev_dbg(adapter->dev, "info: %s: auto cancelling host sleep" | ||
1079 | " since there is interrupt from the firmware\n", __func__); | ||
1080 | |||
1081 | adapter->if_ops.wakeup(adapter); | ||
1082 | adapter->hs_activated = false; | ||
1083 | adapter->is_hs_configured = false; | ||
1084 | mwifiex_hs_activated_event(mwifiex_get_priv(adapter, | ||
1085 | MWIFIEX_BSS_ROLE_ANY), false); | ||
1086 | } | ||
1087 | |||
1088 | /* | ||
1089 | * This function handles the command response of a sleep confirm command. | ||
1090 | * | ||
1091 | * The function sets the card state to SLEEP if the response indicates success. | ||
1092 | */ | ||
1093 | void | ||
1094 | mwifiex_process_sleep_confirm_resp(struct mwifiex_adapter *adapter, | ||
1095 | u8 *pbuf, u32 upld_len) | ||
1096 | { | ||
1097 | struct host_cmd_ds_command *cmd = (struct host_cmd_ds_command *) pbuf; | ||
1098 | struct mwifiex_private *priv = | ||
1099 | mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY); | ||
1100 | uint16_t result = le16_to_cpu(cmd->result); | ||
1101 | uint16_t command = le16_to_cpu(cmd->command); | ||
1102 | uint16_t seq_num = le16_to_cpu(cmd->seq_num); | ||
1103 | |||
1104 | if (!upld_len) { | ||
1105 | dev_err(adapter->dev, "%s: cmd size is 0\n", __func__); | ||
1106 | return; | ||
1107 | } | ||
1108 | |||
1109 | /* Get BSS number and corresponding priv */ | ||
1110 | priv = mwifiex_get_priv_by_id(adapter, HostCmd_GET_BSS_NO(seq_num), | ||
1111 | HostCmd_GET_BSS_TYPE(seq_num)); | ||
1112 | if (!priv) | ||
1113 | priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY); | ||
1114 | |||
1115 | /* Update sequence number */ | ||
1116 | seq_num = HostCmd_GET_SEQ_NO(seq_num); | ||
1117 | /* Clear RET_BIT from HostCmd */ | ||
1118 | command &= HostCmd_CMD_ID_MASK; | ||
1119 | |||
1120 | if (command != HostCmd_CMD_802_11_PS_MODE_ENH) { | ||
1121 | dev_err(adapter->dev, "%s: received unexpected response for" | ||
1122 | " cmd %x, result = %x\n", __func__, command, result); | ||
1123 | return; | ||
1124 | } | ||
1125 | |||
1126 | if (result) { | ||
1127 | dev_err(adapter->dev, "%s: sleep confirm cmd failed\n", | ||
1128 | __func__); | ||
1129 | adapter->pm_wakeup_card_req = false; | ||
1130 | adapter->ps_state = PS_STATE_AWAKE; | ||
1131 | return; | ||
1132 | } | ||
1133 | adapter->pm_wakeup_card_req = true; | ||
1134 | if (adapter->is_hs_configured) | ||
1135 | mwifiex_hs_activated_event(mwifiex_get_priv(adapter, | ||
1136 | MWIFIEX_BSS_ROLE_ANY), true); | ||
1137 | adapter->ps_state = PS_STATE_SLEEP; | ||
1138 | cmd->command = cpu_to_le16(command); | ||
1139 | cmd->seq_num = cpu_to_le16(seq_num); | ||
1140 | } | ||
1141 | EXPORT_SYMBOL_GPL(mwifiex_process_sleep_confirm_resp); | ||
1142 | |||
1143 | /* | ||
1144 | * This function prepares an enhanced power mode command. | ||
1145 | * | ||
1146 | * This function can be used to disable power save or to configure | ||
1147 | * power save with auto PS or STA PS or auto deep sleep. | ||
1148 | * | ||
1149 | * Preparation includes - | ||
1150 | * - Setting command ID, action and proper size | ||
1151 | * - Setting Power Save bitmap, PS parameters TLV, PS mode TLV, | ||
1152 | * auto deep sleep TLV (as required) | ||
1153 | * - Ensuring correct endian-ness | ||
1154 | */ | ||
1155 | int mwifiex_cmd_enh_power_mode(struct mwifiex_private *priv, | ||
1156 | struct host_cmd_ds_command *cmd, | ||
1157 | u16 cmd_action, uint16_t ps_bitmap, | ||
1158 | void *data_buf) | ||
1159 | { | ||
1160 | struct host_cmd_ds_802_11_ps_mode_enh *psmode_enh = | ||
1161 | &cmd->params.psmode_enh; | ||
1162 | u8 *tlv; | ||
1163 | u16 cmd_size = 0; | ||
1164 | |||
1165 | cmd->command = cpu_to_le16(HostCmd_CMD_802_11_PS_MODE_ENH); | ||
1166 | if (cmd_action == DIS_AUTO_PS) { | ||
1167 | psmode_enh->action = cpu_to_le16(DIS_AUTO_PS); | ||
1168 | psmode_enh->params.ps_bitmap = cpu_to_le16(ps_bitmap); | ||
1169 | cmd->size = cpu_to_le16(S_DS_GEN + sizeof(psmode_enh->action) + | ||
1170 | sizeof(psmode_enh->params.ps_bitmap)); | ||
1171 | } else if (cmd_action == GET_PS) { | ||
1172 | psmode_enh->action = cpu_to_le16(GET_PS); | ||
1173 | psmode_enh->params.ps_bitmap = cpu_to_le16(ps_bitmap); | ||
1174 | cmd->size = cpu_to_le16(S_DS_GEN + sizeof(psmode_enh->action) + | ||
1175 | sizeof(psmode_enh->params.ps_bitmap)); | ||
1176 | } else if (cmd_action == EN_AUTO_PS) { | ||
1177 | psmode_enh->action = cpu_to_le16(EN_AUTO_PS); | ||
1178 | psmode_enh->params.ps_bitmap = cpu_to_le16(ps_bitmap); | ||
1179 | cmd_size = S_DS_GEN + sizeof(psmode_enh->action) + | ||
1180 | sizeof(psmode_enh->params.ps_bitmap); | ||
1181 | tlv = (u8 *) cmd + cmd_size; | ||
1182 | if (ps_bitmap & BITMAP_STA_PS) { | ||
1183 | struct mwifiex_adapter *adapter = priv->adapter; | ||
1184 | struct mwifiex_ie_types_ps_param *ps_tlv = | ||
1185 | (struct mwifiex_ie_types_ps_param *) tlv; | ||
1186 | struct mwifiex_ps_param *ps_mode = &ps_tlv->param; | ||
1187 | ps_tlv->header.type = cpu_to_le16(TLV_TYPE_PS_PARAM); | ||
1188 | ps_tlv->header.len = cpu_to_le16(sizeof(*ps_tlv) - | ||
1189 | sizeof(struct mwifiex_ie_types_header)); | ||
1190 | cmd_size += sizeof(*ps_tlv); | ||
1191 | tlv += sizeof(*ps_tlv); | ||
1192 | dev_dbg(adapter->dev, "cmd: PS Command: Enter PS\n"); | ||
1193 | ps_mode->null_pkt_interval = | ||
1194 | cpu_to_le16(adapter->null_pkt_interval); | ||
1195 | ps_mode->multiple_dtims = | ||
1196 | cpu_to_le16(adapter->multiple_dtim); | ||
1197 | ps_mode->bcn_miss_timeout = | ||
1198 | cpu_to_le16(adapter->bcn_miss_time_out); | ||
1199 | ps_mode->local_listen_interval = | ||
1200 | cpu_to_le16(adapter->local_listen_interval); | ||
1201 | ps_mode->adhoc_wake_period = | ||
1202 | cpu_to_le16(adapter->adhoc_awake_period); | ||
1203 | ps_mode->delay_to_ps = | ||
1204 | cpu_to_le16(adapter->delay_to_ps); | ||
1205 | ps_mode->mode = | ||
1206 | cpu_to_le16(adapter->enhanced_ps_mode); | ||
1207 | |||
1208 | } | ||
1209 | if (ps_bitmap & BITMAP_AUTO_DS) { | ||
1210 | struct mwifiex_ie_types_auto_ds_param *auto_ds_tlv = | ||
1211 | (struct mwifiex_ie_types_auto_ds_param *) tlv; | ||
1212 | u16 idletime = 0; | ||
1213 | |||
1214 | auto_ds_tlv->header.type = | ||
1215 | cpu_to_le16(TLV_TYPE_AUTO_DS_PARAM); | ||
1216 | auto_ds_tlv->header.len = | ||
1217 | cpu_to_le16(sizeof(*auto_ds_tlv) - | ||
1218 | sizeof(struct mwifiex_ie_types_header)); | ||
1219 | cmd_size += sizeof(*auto_ds_tlv); | ||
1220 | tlv += sizeof(*auto_ds_tlv); | ||
1221 | if (data_buf) | ||
1222 | idletime = ((struct mwifiex_ds_auto_ds *) | ||
1223 | data_buf)->idle_time; | ||
1224 | dev_dbg(priv->adapter->dev, | ||
1225 | "cmd: PS Command: Enter Auto Deep Sleep\n"); | ||
1226 | auto_ds_tlv->deep_sleep_timeout = cpu_to_le16(idletime); | ||
1227 | } | ||
1228 | cmd->size = cpu_to_le16(cmd_size); | ||
1229 | } | ||
1230 | return 0; | ||
1231 | } | ||
1232 | |||
1233 | /* | ||
1234 | * This function handles the command response of an enhanced power mode | ||
1235 | * command. | ||
1236 | * | ||
1237 | * Handling includes changing the header fields into CPU format | ||
1238 | * and setting the current enhanced power mode in driver. | ||
1239 | */ | ||
1240 | int mwifiex_ret_enh_power_mode(struct mwifiex_private *priv, | ||
1241 | struct host_cmd_ds_command *resp, | ||
1242 | void *data_buf) | ||
1243 | { | ||
1244 | struct mwifiex_adapter *adapter = priv->adapter; | ||
1245 | struct host_cmd_ds_802_11_ps_mode_enh *ps_mode = | ||
1246 | &resp->params.psmode_enh; | ||
1247 | uint16_t action = le16_to_cpu(ps_mode->action); | ||
1248 | uint16_t ps_bitmap = le16_to_cpu(ps_mode->params.ps_bitmap); | ||
1249 | uint16_t auto_ps_bitmap = | ||
1250 | le16_to_cpu(ps_mode->params.ps_bitmap); | ||
1251 | |||
1252 | dev_dbg(adapter->dev, "info: %s: PS_MODE cmd reply result=%#x action=%#X\n", | ||
1253 | __func__, resp->result, action); | ||
1254 | if (action == EN_AUTO_PS) { | ||
1255 | if (auto_ps_bitmap & BITMAP_AUTO_DS) { | ||
1256 | dev_dbg(adapter->dev, "cmd: Enabled auto deep sleep\n"); | ||
1257 | priv->adapter->is_deep_sleep = true; | ||
1258 | } | ||
1259 | if (auto_ps_bitmap & BITMAP_STA_PS) { | ||
1260 | dev_dbg(adapter->dev, "cmd: Enabled STA power save\n"); | ||
1261 | if (adapter->sleep_period.period) | ||
1262 | dev_dbg(adapter->dev, "cmd: set to uapsd/pps mode\n"); | ||
1263 | } | ||
1264 | } else if (action == DIS_AUTO_PS) { | ||
1265 | if (ps_bitmap & BITMAP_AUTO_DS) { | ||
1266 | priv->adapter->is_deep_sleep = false; | ||
1267 | dev_dbg(adapter->dev, "cmd: Disabled auto deep sleep\n"); | ||
1268 | } | ||
1269 | if (ps_bitmap & BITMAP_STA_PS) { | ||
1270 | dev_dbg(adapter->dev, "cmd: Disabled STA power save\n"); | ||
1271 | if (adapter->sleep_period.period) { | ||
1272 | adapter->delay_null_pkt = false; | ||
1273 | adapter->tx_lock_flag = false; | ||
1274 | adapter->pps_uapsd_mode = false; | ||
1275 | } | ||
1276 | } | ||
1277 | } else if (action == GET_PS) { | ||
1278 | if (ps_bitmap & BITMAP_STA_PS) | ||
1279 | adapter->ps_mode = MWIFIEX_802_11_POWER_MODE_PSP; | ||
1280 | else | ||
1281 | adapter->ps_mode = MWIFIEX_802_11_POWER_MODE_CAM; | ||
1282 | |||
1283 | dev_dbg(adapter->dev, "cmd: ps_bitmap=%#x\n", ps_bitmap); | ||
1284 | |||
1285 | if (data_buf) { | ||
1286 | /* This section is for get power save mode */ | ||
1287 | struct mwifiex_ds_pm_cfg *pm_cfg = | ||
1288 | (struct mwifiex_ds_pm_cfg *)data_buf; | ||
1289 | if (ps_bitmap & BITMAP_STA_PS) | ||
1290 | pm_cfg->param.ps_mode = 1; | ||
1291 | else | ||
1292 | pm_cfg->param.ps_mode = 0; | ||
1293 | } | ||
1294 | } | ||
1295 | return 0; | ||
1296 | } | ||
1297 | |||
1298 | /* | ||
1299 | * This function prepares command to get hardware specifications. | ||
1300 | * | ||
1301 | * Preparation includes - | ||
1302 | * - Setting command ID, action and proper size | ||
1303 | * - Setting permanent address parameter | ||
1304 | * - Ensuring correct endian-ness | ||
1305 | */ | ||
1306 | int mwifiex_cmd_get_hw_spec(struct mwifiex_private *priv, | ||
1307 | struct host_cmd_ds_command *cmd) | ||
1308 | { | ||
1309 | struct host_cmd_ds_get_hw_spec *hw_spec = &cmd->params.hw_spec; | ||
1310 | |||
1311 | cmd->command = cpu_to_le16(HostCmd_CMD_GET_HW_SPEC); | ||
1312 | cmd->size = | ||
1313 | cpu_to_le16(sizeof(struct host_cmd_ds_get_hw_spec) + S_DS_GEN); | ||
1314 | memcpy(hw_spec->permanent_addr, priv->curr_addr, ETH_ALEN); | ||
1315 | |||
1316 | return 0; | ||
1317 | } | ||
1318 | |||
1319 | /* | ||
1320 | * This function handles the command response of get hardware | ||
1321 | * specifications. | ||
1322 | * | ||
1323 | * Handling includes changing the header fields into CPU format | ||
1324 | * and saving/updating the following parameters in driver - | ||
1325 | * - Firmware capability information | ||
1326 | * - Firmware band settings | ||
1327 | * - Ad-hoc start band and channel | ||
1328 | * - Ad-hoc 11n activation status | ||
1329 | * - Firmware release number | ||
1330 | * - Number of antennas | ||
1331 | * - Hardware address | ||
1332 | * - Hardware interface version | ||
1333 | * - Firmware version | ||
1334 | * - Region code | ||
1335 | * - 11n capabilities | ||
1336 | * - MCS support fields | ||
1337 | * - MP end port | ||
1338 | */ | ||
1339 | int mwifiex_ret_get_hw_spec(struct mwifiex_private *priv, | ||
1340 | struct host_cmd_ds_command *resp) | ||
1341 | { | ||
1342 | struct host_cmd_ds_get_hw_spec *hw_spec = &resp->params.hw_spec; | ||
1343 | struct mwifiex_adapter *adapter = priv->adapter; | ||
1344 | int i; | ||
1345 | |||
1346 | adapter->fw_cap_info = le32_to_cpu(hw_spec->fw_cap_info); | ||
1347 | |||
1348 | if (IS_SUPPORT_MULTI_BANDS(adapter)) | ||
1349 | adapter->fw_bands = (u8) GET_FW_DEFAULT_BANDS(adapter); | ||
1350 | else | ||
1351 | adapter->fw_bands = BAND_B; | ||
1352 | |||
1353 | adapter->config_bands = adapter->fw_bands; | ||
1354 | |||
1355 | if (adapter->fw_bands & BAND_A) { | ||
1356 | if (adapter->fw_bands & BAND_GN) { | ||
1357 | adapter->config_bands |= BAND_AN; | ||
1358 | adapter->fw_bands |= BAND_AN; | ||
1359 | } | ||
1360 | if (adapter->fw_bands & BAND_AN) { | ||
1361 | adapter->adhoc_start_band = BAND_A | BAND_AN; | ||
1362 | adapter->adhoc_11n_enabled = true; | ||
1363 | } else { | ||
1364 | adapter->adhoc_start_band = BAND_A; | ||
1365 | } | ||
1366 | priv->adhoc_channel = DEFAULT_AD_HOC_CHANNEL_A; | ||
1367 | } else if (adapter->fw_bands & BAND_GN) { | ||
1368 | adapter->adhoc_start_band = BAND_G | BAND_B | BAND_GN; | ||
1369 | priv->adhoc_channel = DEFAULT_AD_HOC_CHANNEL; | ||
1370 | adapter->adhoc_11n_enabled = true; | ||
1371 | } else if (adapter->fw_bands & BAND_G) { | ||
1372 | adapter->adhoc_start_band = BAND_G | BAND_B; | ||
1373 | priv->adhoc_channel = DEFAULT_AD_HOC_CHANNEL; | ||
1374 | } else if (adapter->fw_bands & BAND_B) { | ||
1375 | adapter->adhoc_start_band = BAND_B; | ||
1376 | priv->adhoc_channel = DEFAULT_AD_HOC_CHANNEL; | ||
1377 | } | ||
1378 | |||
1379 | adapter->fw_release_number = le32_to_cpu(hw_spec->fw_release_number); | ||
1380 | adapter->number_of_antenna = le16_to_cpu(hw_spec->number_of_antenna); | ||
1381 | |||
1382 | dev_dbg(adapter->dev, "info: GET_HW_SPEC: fw_release_number- %#x\n", | ||
1383 | adapter->fw_release_number); | ||
1384 | dev_dbg(adapter->dev, "info: GET_HW_SPEC: permanent addr: %pM\n", | ||
1385 | hw_spec->permanent_addr); | ||
1386 | dev_dbg(adapter->dev, "info: GET_HW_SPEC: hw_if_version=%#x version=%#x\n", | ||
1387 | le16_to_cpu(hw_spec->hw_if_version), | ||
1388 | le16_to_cpu(hw_spec->version)); | ||
1389 | |||
1390 | if (priv->curr_addr[0] == 0xff) | ||
1391 | memmove(priv->curr_addr, hw_spec->permanent_addr, ETH_ALEN); | ||
1392 | |||
1393 | adapter->region_code = le16_to_cpu(hw_spec->region_code); | ||
1394 | |||
1395 | for (i = 0; i < MWIFIEX_MAX_REGION_CODE; i++) | ||
1396 | /* Use the region code to search for the index */ | ||
1397 | if (adapter->region_code == region_code_index[i]) | ||
1398 | break; | ||
1399 | |||
1400 | /* If it's unidentified region code, use the default (USA) */ | ||
1401 | if (i >= MWIFIEX_MAX_REGION_CODE) { | ||
1402 | adapter->region_code = 0x10; | ||
1403 | dev_dbg(adapter->dev, "cmd: unknown region code, use default (USA)\n"); | ||
1404 | } | ||
1405 | |||
1406 | adapter->hw_dot_11n_dev_cap = le32_to_cpu(hw_spec->dot_11n_dev_cap); | ||
1407 | adapter->hw_dev_mcs_support = hw_spec->dev_mcs_support; | ||
1408 | |||
1409 | if (adapter->if_ops.update_mp_end_port) | ||
1410 | adapter->if_ops.update_mp_end_port(adapter, | ||
1411 | le16_to_cpu(hw_spec->mp_end_port)); | ||
1412 | |||
1413 | return 0; | ||
1414 | } | ||
diff --git a/drivers/net/wireless/mwifiex/debugfs.c b/drivers/net/wireless/mwifiex/debugfs.c new file mode 100644 index 000000000000..46d65e02c7ba --- /dev/null +++ b/drivers/net/wireless/mwifiex/debugfs.c | |||
@@ -0,0 +1,770 @@ | |||
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 | {"cmd_pending", adapter_item_size(cmd_pending), | ||
133 | adapter_item_addr(cmd_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; | ||
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; | ||
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; | ||
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; | ||
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; | ||
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 | |||
740 | /* | ||
741 | * This function removes the debug FS directory structure and the files. | ||
742 | */ | ||
743 | void | ||
744 | mwifiex_dev_debugfs_remove(struct mwifiex_private *priv) | ||
745 | { | ||
746 | if (!priv) | ||
747 | return; | ||
748 | |||
749 | debugfs_remove_recursive(priv->dfs_dev_dir); | ||
750 | } | ||
751 | |||
752 | /* | ||
753 | * This function creates the top level proc directory. | ||
754 | */ | ||
755 | void | ||
756 | mwifiex_debugfs_init(void) | ||
757 | { | ||
758 | if (!mwifiex_dfs_dir) | ||
759 | mwifiex_dfs_dir = debugfs_create_dir("mwifiex", NULL); | ||
760 | } | ||
761 | |||
762 | /* | ||
763 | * This function removes the top level proc directory. | ||
764 | */ | ||
765 | void | ||
766 | mwifiex_debugfs_remove(void) | ||
767 | { | ||
768 | if (mwifiex_dfs_dir) | ||
769 | debugfs_remove(mwifiex_dfs_dir); | ||
770 | } | ||
diff --git a/drivers/net/wireless/mwifiex/decl.h b/drivers/net/wireless/mwifiex/decl.h new file mode 100644 index 000000000000..0e90b0986ed8 --- /dev/null +++ b/drivers/net/wireless/mwifiex/decl.h | |||
@@ -0,0 +1,129 @@ | |||
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 | |||
55 | #define MWIFIEX_RTS_MIN_VALUE (0) | ||
56 | #define MWIFIEX_RTS_MAX_VALUE (2347) | ||
57 | #define MWIFIEX_FRAG_MIN_VALUE (256) | ||
58 | #define MWIFIEX_FRAG_MAX_VALUE (2346) | ||
59 | |||
60 | #define MWIFIEX_SDIO_BLOCK_SIZE 256 | ||
61 | |||
62 | #define MWIFIEX_BUF_FLAG_REQUEUED_PKT BIT(0) | ||
63 | |||
64 | enum mwifiex_bss_type { | ||
65 | MWIFIEX_BSS_TYPE_STA = 0, | ||
66 | MWIFIEX_BSS_TYPE_UAP = 1, | ||
67 | MWIFIEX_BSS_TYPE_ANY = 0xff, | ||
68 | }; | ||
69 | |||
70 | enum mwifiex_bss_role { | ||
71 | MWIFIEX_BSS_ROLE_STA = 0, | ||
72 | MWIFIEX_BSS_ROLE_UAP = 1, | ||
73 | MWIFIEX_BSS_ROLE_ANY = 0xff, | ||
74 | }; | ||
75 | |||
76 | #define BSS_ROLE_BIT_MASK BIT(0) | ||
77 | |||
78 | #define GET_BSS_ROLE(priv) ((priv)->bss_role & BSS_ROLE_BIT_MASK) | ||
79 | |||
80 | enum mwifiex_data_frame_type { | ||
81 | MWIFIEX_DATA_FRAME_TYPE_ETH_II = 0, | ||
82 | MWIFIEX_DATA_FRAME_TYPE_802_11, | ||
83 | }; | ||
84 | |||
85 | struct mwifiex_fw_image { | ||
86 | u8 *helper_buf; | ||
87 | u32 helper_len; | ||
88 | u8 *fw_buf; | ||
89 | u32 fw_len; | ||
90 | }; | ||
91 | |||
92 | struct mwifiex_802_11_ssid { | ||
93 | u32 ssid_len; | ||
94 | u8 ssid[IEEE80211_MAX_SSID_LEN]; | ||
95 | }; | ||
96 | |||
97 | struct mwifiex_wait_queue { | ||
98 | wait_queue_head_t wait; | ||
99 | u16 condition; | ||
100 | int status; | ||
101 | }; | ||
102 | |||
103 | struct mwifiex_rxinfo { | ||
104 | u8 bss_index; | ||
105 | struct sk_buff *parent; | ||
106 | u8 use_count; | ||
107 | }; | ||
108 | |||
109 | struct mwifiex_txinfo { | ||
110 | u32 status_code; | ||
111 | u8 flags; | ||
112 | u8 bss_index; | ||
113 | }; | ||
114 | |||
115 | struct mwifiex_bss_attr { | ||
116 | u8 bss_type; | ||
117 | u8 frame_type; | ||
118 | u8 active; | ||
119 | u8 bss_priority; | ||
120 | u8 bss_num; | ||
121 | }; | ||
122 | |||
123 | enum mwifiex_wmm_ac_e { | ||
124 | WMM_AC_BK, | ||
125 | WMM_AC_BE, | ||
126 | WMM_AC_VI, | ||
127 | WMM_AC_VO | ||
128 | } __packed; | ||
129 | #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..afdd145dff0b --- /dev/null +++ b/drivers/net/wireless/mwifiex/fw.h | |||
@@ -0,0 +1,1187 @@ | |||
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 | extern u8 supported_rates_b[B_SUPPORTED_RATES]; | ||
61 | extern u8 supported_rates_g[G_SUPPORTED_RATES]; | ||
62 | extern u8 supported_rates_bg[BG_SUPPORTED_RATES]; | ||
63 | extern u8 supported_rates_a[A_SUPPORTED_RATES]; | ||
64 | extern u8 supported_rates_n[N_SUPPORTED_RATES]; | ||
65 | |||
66 | #define HostCmd_WEP_KEY_INDEX_MASK 0x3fff | ||
67 | |||
68 | #define KEY_INFO_ENABLED 0x01 | ||
69 | enum KEY_TYPE_ID { | ||
70 | KEY_TYPE_ID_WEP = 0, | ||
71 | KEY_TYPE_ID_TKIP, | ||
72 | KEY_TYPE_ID_AES, | ||
73 | KEY_TYPE_ID_WAPI, | ||
74 | }; | ||
75 | #define KEY_MCAST BIT(0) | ||
76 | #define KEY_UNICAST BIT(1) | ||
77 | #define KEY_ENABLED BIT(2) | ||
78 | |||
79 | #define WAPI_KEY_LEN 50 | ||
80 | |||
81 | #define MAX_POLL_TRIES 100 | ||
82 | |||
83 | #define MAX_MULTI_INTERFACE_POLL_TRIES 1000 | ||
84 | |||
85 | #define MAX_FIRMWARE_POLL_TRIES 100 | ||
86 | |||
87 | #define FIRMWARE_READY 0xfedc | ||
88 | |||
89 | enum MWIFIEX_802_11_PRIVACY_FILTER { | ||
90 | MWIFIEX_802_11_PRIV_FILTER_ACCEPT_ALL, | ||
91 | MWIFIEX_802_11_PRIV_FILTER_8021X_WEP | ||
92 | }; | ||
93 | |||
94 | enum MWIFIEX_802_11_WEP_STATUS { | ||
95 | MWIFIEX_802_11_WEP_ENABLED, | ||
96 | MWIFIEX_802_11_WEP_DISABLED, | ||
97 | }; | ||
98 | |||
99 | #define CAL_SNR(RSSI, NF) ((s16)((s16)(RSSI)-(s16)(NF))) | ||
100 | |||
101 | #define PROPRIETARY_TLV_BASE_ID 0x0100 | ||
102 | #define TLV_TYPE_KEY_MATERIAL (PROPRIETARY_TLV_BASE_ID + 0) | ||
103 | #define TLV_TYPE_CHANLIST (PROPRIETARY_TLV_BASE_ID + 1) | ||
104 | #define TLV_TYPE_NUMPROBES (PROPRIETARY_TLV_BASE_ID + 2) | ||
105 | #define TLV_TYPE_PASSTHROUGH (PROPRIETARY_TLV_BASE_ID + 10) | ||
106 | #define TLV_TYPE_WMMQSTATUS (PROPRIETARY_TLV_BASE_ID + 16) | ||
107 | #define TLV_TYPE_WILDCARDSSID (PROPRIETARY_TLV_BASE_ID + 18) | ||
108 | #define TLV_TYPE_TSFTIMESTAMP (PROPRIETARY_TLV_BASE_ID + 19) | ||
109 | #define TLV_TYPE_AUTH_TYPE (PROPRIETARY_TLV_BASE_ID + 31) | ||
110 | #define TLV_TYPE_CHANNELBANDLIST (PROPRIETARY_TLV_BASE_ID + 42) | ||
111 | #define TLV_TYPE_RATE_DROP_CONTROL (PROPRIETARY_TLV_BASE_ID + 82) | ||
112 | #define TLV_TYPE_RATE_SCOPE (PROPRIETARY_TLV_BASE_ID + 83) | ||
113 | #define TLV_TYPE_POWER_GROUP (PROPRIETARY_TLV_BASE_ID + 84) | ||
114 | #define TLV_TYPE_WAPI_IE (PROPRIETARY_TLV_BASE_ID + 94) | ||
115 | #define TLV_TYPE_AUTO_DS_PARAM (PROPRIETARY_TLV_BASE_ID + 113) | ||
116 | #define TLV_TYPE_PS_PARAM (PROPRIETARY_TLV_BASE_ID + 114) | ||
117 | |||
118 | #define MWIFIEX_TX_DATA_BUF_SIZE_2K 2048 | ||
119 | |||
120 | #define SSN_MASK 0xfff0 | ||
121 | |||
122 | #define BA_RESULT_SUCCESS 0x0 | ||
123 | #define BA_RESULT_TIMEOUT 0x2 | ||
124 | |||
125 | #define IS_BASTREAM_SETUP(ptr) (ptr->ba_status) | ||
126 | |||
127 | #define BA_STREAM_NOT_ALLOWED 0xff | ||
128 | |||
129 | #define IS_11N_ENABLED(priv) ((priv->adapter->config_bands & BAND_GN || \ | ||
130 | priv->adapter->config_bands & BAND_AN) \ | ||
131 | && priv->curr_bss_params.bss_descriptor.bcn_ht_cap) | ||
132 | #define INITIATOR_BIT(DelBAParamSet) (((DelBAParamSet) &\ | ||
133 | BIT(DELBA_INITIATOR_POS)) >> DELBA_INITIATOR_POS) | ||
134 | |||
135 | #define MWIFIEX_TX_DATA_BUF_SIZE_4K 4096 | ||
136 | #define MWIFIEX_TX_DATA_BUF_SIZE_8K 8192 | ||
137 | |||
138 | #define ISSUPP_11NENABLED(FwCapInfo) (FwCapInfo & BIT(11)) | ||
139 | |||
140 | /* dev_cap bitmap | ||
141 | * BIT | ||
142 | * 0-16 reserved | ||
143 | * 17 IEEE80211_HT_CAP_SUP_WIDTH_20_40 | ||
144 | * 18-22 reserved | ||
145 | * 23 IEEE80211_HT_CAP_SGI_20 | ||
146 | * 24 IEEE80211_HT_CAP_SGI_40 | ||
147 | * 25 IEEE80211_HT_CAP_TX_STBC | ||
148 | * 26 IEEE80211_HT_CAP_RX_STBC | ||
149 | * 27-28 reserved | ||
150 | * 29 IEEE80211_HT_CAP_GRN_FLD | ||
151 | * 30-31 reserved | ||
152 | */ | ||
153 | #define ISSUPP_CHANWIDTH40(Dot11nDevCap) (Dot11nDevCap & BIT(17)) | ||
154 | #define ISSUPP_SHORTGI20(Dot11nDevCap) (Dot11nDevCap & BIT(23)) | ||
155 | #define ISSUPP_SHORTGI40(Dot11nDevCap) (Dot11nDevCap & BIT(24)) | ||
156 | #define ISSUPP_TXSTBC(Dot11nDevCap) (Dot11nDevCap & BIT(25)) | ||
157 | #define ISSUPP_RXSTBC(Dot11nDevCap) (Dot11nDevCap & BIT(26)) | ||
158 | #define ISSUPP_GREENFIELD(Dot11nDevCap) (Dot11nDevCap & BIT(29)) | ||
159 | |||
160 | #define GET_RXMCSSUPP(DevMCSSupported) (DevMCSSupported & 0x0f) | ||
161 | #define SETHT_MCS32(x) (x[4] |= 1) | ||
162 | |||
163 | #define SET_SECONDARYCHAN(RadioType, SECCHAN) (RadioType |= (SECCHAN << 4)) | ||
164 | |||
165 | #define LLC_SNAP_LEN 8 | ||
166 | |||
167 | #define MOD_CLASS_HR_DSSS 0x03 | ||
168 | #define MOD_CLASS_OFDM 0x07 | ||
169 | #define MOD_CLASS_HT 0x08 | ||
170 | #define HT_BW_20 0 | ||
171 | #define HT_BW_40 1 | ||
172 | |||
173 | #define HostCmd_CMD_GET_HW_SPEC 0x0003 | ||
174 | #define HostCmd_CMD_802_11_SCAN 0x0006 | ||
175 | #define HostCmd_CMD_802_11_GET_LOG 0x000b | ||
176 | #define HostCmd_CMD_MAC_MULTICAST_ADR 0x0010 | ||
177 | #define HostCmd_CMD_802_11_EEPROM_ACCESS 0x0059 | ||
178 | #define HostCmd_CMD_802_11_ASSOCIATE 0x0012 | ||
179 | #define HostCmd_CMD_802_11_SNMP_MIB 0x0016 | ||
180 | #define HostCmd_CMD_MAC_REG_ACCESS 0x0019 | ||
181 | #define HostCmd_CMD_BBP_REG_ACCESS 0x001a | ||
182 | #define HostCmd_CMD_RF_REG_ACCESS 0x001b | ||
183 | #define HostCmd_CMD_PMIC_REG_ACCESS 0x00ad | ||
184 | #define HostCmd_CMD_802_11_RF_CHANNEL 0x001d | ||
185 | #define HostCmd_CMD_802_11_DEAUTHENTICATE 0x0024 | ||
186 | #define HostCmd_CMD_MAC_CONTROL 0x0028 | ||
187 | #define HostCmd_CMD_802_11_AD_HOC_START 0x002b | ||
188 | #define HostCmd_CMD_802_11_AD_HOC_JOIN 0x002c | ||
189 | #define HostCmd_CMD_802_11_AD_HOC_STOP 0x0040 | ||
190 | #define HostCmd_CMD_802_11_MAC_ADDRESS 0x004D | ||
191 | #define HostCmd_CMD_802_11D_DOMAIN_INFO 0x005b | ||
192 | #define HostCmd_CMD_802_11_KEY_MATERIAL 0x005e | ||
193 | #define HostCmd_CMD_802_11_BG_SCAN_QUERY 0x006c | ||
194 | #define HostCmd_CMD_WMM_GET_STATUS 0x0071 | ||
195 | #define HostCmd_CMD_802_11_TX_RATE_QUERY 0x007f | ||
196 | #define HostCmd_CMD_802_11_IBSS_COALESCING_STATUS 0x0083 | ||
197 | #define HostCmd_CMD_VERSION_EXT 0x0097 | ||
198 | #define HostCmd_CMD_RSSI_INFO 0x00a4 | ||
199 | #define HostCmd_CMD_FUNC_INIT 0x00a9 | ||
200 | #define HostCmd_CMD_FUNC_SHUTDOWN 0x00aa | ||
201 | #define HostCmd_CMD_11N_CFG 0x00cd | ||
202 | #define HostCmd_CMD_11N_ADDBA_REQ 0x00ce | ||
203 | #define HostCmd_CMD_11N_ADDBA_RSP 0x00cf | ||
204 | #define HostCmd_CMD_11N_DELBA 0x00d0 | ||
205 | #define HostCmd_CMD_RECONFIGURE_TX_BUFF 0x00d9 | ||
206 | #define HostCmd_CMD_AMSDU_AGGR_CTRL 0x00df | ||
207 | #define HostCmd_CMD_TXPWR_CFG 0x00d1 | ||
208 | #define HostCmd_CMD_TX_RATE_CFG 0x00d6 | ||
209 | #define HostCmd_CMD_802_11_PS_MODE_ENH 0x00e4 | ||
210 | #define HostCmd_CMD_802_11_HS_CFG_ENH 0x00e5 | ||
211 | #define HostCmd_CMD_CAU_REG_ACCESS 0x00ed | ||
212 | #define HostCmd_CMD_SET_BSS_MODE 0x00f7 | ||
213 | |||
214 | |||
215 | enum ENH_PS_MODES { | ||
216 | EN_PS = 1, | ||
217 | DIS_PS = 2, | ||
218 | EN_AUTO_DS = 3, | ||
219 | DIS_AUTO_DS = 4, | ||
220 | SLEEP_CONFIRM = 5, | ||
221 | GET_PS = 0, | ||
222 | EN_AUTO_PS = 0xff, | ||
223 | DIS_AUTO_PS = 0xfe, | ||
224 | }; | ||
225 | |||
226 | #define HostCmd_RET_BIT 0x8000 | ||
227 | #define HostCmd_ACT_GEN_GET 0x0000 | ||
228 | #define HostCmd_ACT_GEN_SET 0x0001 | ||
229 | #define HostCmd_RESULT_OK 0x0000 | ||
230 | |||
231 | #define HostCmd_ACT_MAC_RX_ON 0x0001 | ||
232 | #define HostCmd_ACT_MAC_TX_ON 0x0002 | ||
233 | #define HostCmd_ACT_MAC_WEP_ENABLE 0x0008 | ||
234 | #define HostCmd_ACT_MAC_ETHERNETII_ENABLE 0x0010 | ||
235 | #define HostCmd_ACT_MAC_PROMISCUOUS_ENABLE 0x0080 | ||
236 | #define HostCmd_ACT_MAC_ALL_MULTICAST_ENABLE 0x0100 | ||
237 | #define HostCmd_ACT_MAC_ADHOC_G_PROTECTION_ON 0x2000 | ||
238 | |||
239 | #define HostCmd_BSS_MODE_IBSS 0x0002 | ||
240 | #define HostCmd_BSS_MODE_ANY 0x0003 | ||
241 | |||
242 | #define HostCmd_SCAN_RADIO_TYPE_BG 0 | ||
243 | #define HostCmd_SCAN_RADIO_TYPE_A 1 | ||
244 | |||
245 | #define HOST_SLEEP_CFG_CANCEL 0xffffffff | ||
246 | #define HOST_SLEEP_CFG_COND_DEF 0x0000000f | ||
247 | #define HOST_SLEEP_CFG_GPIO_DEF 0xff | ||
248 | #define HOST_SLEEP_CFG_GAP_DEF 0 | ||
249 | |||
250 | #define CMD_F_HOSTCMD (1 << 0) | ||
251 | #define CMD_F_CANCELED (1 << 1) | ||
252 | |||
253 | #define HostCmd_CMD_ID_MASK 0x0fff | ||
254 | |||
255 | #define HostCmd_SEQ_NUM_MASK 0x00ff | ||
256 | |||
257 | #define HostCmd_BSS_NUM_MASK 0x0f00 | ||
258 | |||
259 | #define HostCmd_BSS_TYPE_MASK 0xf000 | ||
260 | |||
261 | #define HostCmd_SET_SEQ_NO_BSS_INFO(seq, num, type) { \ | ||
262 | (((seq) & 0x00ff) | \ | ||
263 | (((num) & 0x000f) << 8)) | \ | ||
264 | (((type) & 0x000f) << 12); } | ||
265 | |||
266 | #define HostCmd_GET_SEQ_NO(seq) \ | ||
267 | ((seq) & HostCmd_SEQ_NUM_MASK) | ||
268 | |||
269 | #define HostCmd_GET_BSS_NO(seq) \ | ||
270 | (((seq) & HostCmd_BSS_NUM_MASK) >> 8) | ||
271 | |||
272 | #define HostCmd_GET_BSS_TYPE(seq) \ | ||
273 | (((seq) & HostCmd_BSS_TYPE_MASK) >> 12) | ||
274 | |||
275 | #define EVENT_DUMMY_HOST_WAKEUP_SIGNAL 0x00000001 | ||
276 | #define EVENT_LINK_LOST 0x00000003 | ||
277 | #define EVENT_LINK_SENSED 0x00000004 | ||
278 | #define EVENT_MIB_CHANGED 0x00000006 | ||
279 | #define EVENT_INIT_DONE 0x00000007 | ||
280 | #define EVENT_DEAUTHENTICATED 0x00000008 | ||
281 | #define EVENT_DISASSOCIATED 0x00000009 | ||
282 | #define EVENT_PS_AWAKE 0x0000000a | ||
283 | #define EVENT_PS_SLEEP 0x0000000b | ||
284 | #define EVENT_MIC_ERR_MULTICAST 0x0000000d | ||
285 | #define EVENT_MIC_ERR_UNICAST 0x0000000e | ||
286 | #define EVENT_DEEP_SLEEP_AWAKE 0x00000010 | ||
287 | #define EVENT_ADHOC_BCN_LOST 0x00000011 | ||
288 | |||
289 | #define EVENT_WMM_STATUS_CHANGE 0x00000017 | ||
290 | #define EVENT_BG_SCAN_REPORT 0x00000018 | ||
291 | #define EVENT_RSSI_LOW 0x00000019 | ||
292 | #define EVENT_SNR_LOW 0x0000001a | ||
293 | #define EVENT_MAX_FAIL 0x0000001b | ||
294 | #define EVENT_RSSI_HIGH 0x0000001c | ||
295 | #define EVENT_SNR_HIGH 0x0000001d | ||
296 | #define EVENT_IBSS_COALESCED 0x0000001e | ||
297 | #define EVENT_DATA_RSSI_LOW 0x00000024 | ||
298 | #define EVENT_DATA_SNR_LOW 0x00000025 | ||
299 | #define EVENT_DATA_RSSI_HIGH 0x00000026 | ||
300 | #define EVENT_DATA_SNR_HIGH 0x00000027 | ||
301 | #define EVENT_LINK_QUALITY 0x00000028 | ||
302 | #define EVENT_PORT_RELEASE 0x0000002b | ||
303 | #define EVENT_PRE_BEACON_LOST 0x00000031 | ||
304 | #define EVENT_ADDBA 0x00000033 | ||
305 | #define EVENT_DELBA 0x00000034 | ||
306 | #define EVENT_BA_STREAM_TIEMOUT 0x00000037 | ||
307 | #define EVENT_AMSDU_AGGR_CTRL 0x00000042 | ||
308 | #define EVENT_WEP_ICV_ERR 0x00000046 | ||
309 | #define EVENT_HS_ACT_REQ 0x00000047 | ||
310 | #define EVENT_BW_CHANGE 0x00000048 | ||
311 | |||
312 | #define EVENT_HOSTWAKE_STAIE 0x0000004d | ||
313 | |||
314 | #define EVENT_ID_MASK 0xffff | ||
315 | #define BSS_NUM_MASK 0xf | ||
316 | |||
317 | #define EVENT_GET_BSS_NUM(event_cause) \ | ||
318 | (((event_cause) >> 16) & BSS_NUM_MASK) | ||
319 | |||
320 | #define EVENT_GET_BSS_TYPE(event_cause) \ | ||
321 | (((event_cause) >> 24) & 0x00ff) | ||
322 | |||
323 | struct mwifiex_ie_types_header { | ||
324 | __le16 type; | ||
325 | __le16 len; | ||
326 | } __packed; | ||
327 | |||
328 | struct mwifiex_ie_types_data { | ||
329 | struct mwifiex_ie_types_header header; | ||
330 | u8 data[1]; | ||
331 | } __packed; | ||
332 | |||
333 | #define MWIFIEX_TxPD_POWER_MGMT_NULL_PACKET 0x01 | ||
334 | #define MWIFIEX_TxPD_POWER_MGMT_LAST_PACKET 0x08 | ||
335 | |||
336 | struct txpd { | ||
337 | u8 bss_type; | ||
338 | u8 bss_num; | ||
339 | __le16 tx_pkt_length; | ||
340 | __le16 tx_pkt_offset; | ||
341 | __le16 tx_pkt_type; | ||
342 | __le32 tx_control; | ||
343 | u8 priority; | ||
344 | u8 flags; | ||
345 | u8 pkt_delay_2ms; | ||
346 | u8 reserved1; | ||
347 | } __packed; | ||
348 | |||
349 | struct rxpd { | ||
350 | u8 bss_type; | ||
351 | u8 bss_num; | ||
352 | u16 rx_pkt_length; | ||
353 | u16 rx_pkt_offset; | ||
354 | u16 rx_pkt_type; | ||
355 | u16 seq_num; | ||
356 | u8 priority; | ||
357 | u8 rx_rate; | ||
358 | s8 snr; | ||
359 | s8 nf; | ||
360 | /* Ht Info [Bit 0] RxRate format: LG=0, HT=1 | ||
361 | * [Bit 1] HT Bandwidth: BW20 = 0, BW40 = 1 | ||
362 | * [Bit 2] HT Guard Interval: LGI = 0, SGI = 1 */ | ||
363 | u8 ht_info; | ||
364 | u8 reserved; | ||
365 | } __packed; | ||
366 | |||
367 | enum mwifiex_chan_scan_mode_bitmasks { | ||
368 | MWIFIEX_PASSIVE_SCAN = BIT(0), | ||
369 | MWIFIEX_DISABLE_CHAN_FILT = BIT(1), | ||
370 | }; | ||
371 | |||
372 | #define SECOND_CHANNEL_BELOW 0x30 | ||
373 | #define SECOND_CHANNEL_ABOVE 0x10 | ||
374 | struct mwifiex_chan_scan_param_set { | ||
375 | u8 radio_type; | ||
376 | u8 chan_number; | ||
377 | u8 chan_scan_mode_bitmap; | ||
378 | __le16 min_scan_time; | ||
379 | __le16 max_scan_time; | ||
380 | } __packed; | ||
381 | |||
382 | struct mwifiex_ie_types_chan_list_param_set { | ||
383 | struct mwifiex_ie_types_header header; | ||
384 | struct mwifiex_chan_scan_param_set chan_scan_param[1]; | ||
385 | } __packed; | ||
386 | |||
387 | struct chan_band_param_set { | ||
388 | u8 radio_type; | ||
389 | u8 chan_number; | ||
390 | }; | ||
391 | |||
392 | struct mwifiex_ie_types_chan_band_list_param_set { | ||
393 | struct mwifiex_ie_types_header header; | ||
394 | struct chan_band_param_set chan_band_param[1]; | ||
395 | } __packed; | ||
396 | |||
397 | struct mwifiex_ie_types_rates_param_set { | ||
398 | struct mwifiex_ie_types_header header; | ||
399 | u8 rates[1]; | ||
400 | } __packed; | ||
401 | |||
402 | struct mwifiex_ie_types_ssid_param_set { | ||
403 | struct mwifiex_ie_types_header header; | ||
404 | u8 ssid[1]; | ||
405 | } __packed; | ||
406 | |||
407 | struct mwifiex_ie_types_num_probes { | ||
408 | struct mwifiex_ie_types_header header; | ||
409 | __le16 num_probes; | ||
410 | } __packed; | ||
411 | |||
412 | struct mwifiex_ie_types_wildcard_ssid_params { | ||
413 | struct mwifiex_ie_types_header header; | ||
414 | u8 max_ssid_length; | ||
415 | u8 ssid[1]; | ||
416 | } __packed; | ||
417 | |||
418 | #define TSF_DATA_SIZE 8 | ||
419 | struct mwifiex_ie_types_tsf_timestamp { | ||
420 | struct mwifiex_ie_types_header header; | ||
421 | u8 tsf_data[1]; | ||
422 | } __packed; | ||
423 | |||
424 | struct mwifiex_cf_param_set { | ||
425 | u8 cfp_cnt; | ||
426 | u8 cfp_period; | ||
427 | u16 cfp_max_duration; | ||
428 | u16 cfp_duration_remaining; | ||
429 | } __packed; | ||
430 | |||
431 | struct mwifiex_ibss_param_set { | ||
432 | u16 atim_window; | ||
433 | } __packed; | ||
434 | |||
435 | struct mwifiex_ie_types_ss_param_set { | ||
436 | struct mwifiex_ie_types_header header; | ||
437 | union { | ||
438 | struct mwifiex_cf_param_set cf_param_set[1]; | ||
439 | struct mwifiex_ibss_param_set ibss_param_set[1]; | ||
440 | } cf_ibss; | ||
441 | } __packed; | ||
442 | |||
443 | struct mwifiex_fh_param_set { | ||
444 | u16 dwell_time; | ||
445 | u8 hop_set; | ||
446 | u8 hop_pattern; | ||
447 | u8 hop_index; | ||
448 | } __packed; | ||
449 | |||
450 | struct mwifiex_ds_param_set { | ||
451 | u8 current_chan; | ||
452 | } __packed; | ||
453 | |||
454 | struct mwifiex_ie_types_phy_param_set { | ||
455 | struct mwifiex_ie_types_header header; | ||
456 | union { | ||
457 | struct mwifiex_fh_param_set fh_param_set[1]; | ||
458 | struct mwifiex_ds_param_set ds_param_set[1]; | ||
459 | } fh_ds; | ||
460 | } __packed; | ||
461 | |||
462 | struct mwifiex_ie_types_auth_type { | ||
463 | struct mwifiex_ie_types_header header; | ||
464 | __le16 auth_type; | ||
465 | } __packed; | ||
466 | |||
467 | struct mwifiex_ie_types_vendor_param_set { | ||
468 | struct mwifiex_ie_types_header header; | ||
469 | u8 ie[MWIFIEX_MAX_VSIE_LEN]; | ||
470 | }; | ||
471 | |||
472 | struct mwifiex_ie_types_rsn_param_set { | ||
473 | struct mwifiex_ie_types_header header; | ||
474 | u8 rsn_ie[1]; | ||
475 | } __packed; | ||
476 | |||
477 | #define KEYPARAMSET_FIXED_LEN 6 | ||
478 | |||
479 | struct mwifiex_ie_type_key_param_set { | ||
480 | __le16 type; | ||
481 | __le16 length; | ||
482 | __le16 key_type_id; | ||
483 | __le16 key_info; | ||
484 | __le16 key_len; | ||
485 | u8 key[50]; | ||
486 | } __packed; | ||
487 | |||
488 | struct host_cmd_ds_802_11_key_material { | ||
489 | __le16 action; | ||
490 | struct mwifiex_ie_type_key_param_set key_param_set; | ||
491 | } __packed; | ||
492 | |||
493 | struct host_cmd_ds_gen { | ||
494 | u16 command; | ||
495 | u16 size; | ||
496 | u16 seq_num; | ||
497 | u16 result; | ||
498 | }; | ||
499 | |||
500 | #define S_DS_GEN sizeof(struct host_cmd_ds_gen) | ||
501 | |||
502 | enum sleep_resp_ctrl { | ||
503 | RESP_NOT_NEEDED = 0, | ||
504 | RESP_NEEDED, | ||
505 | }; | ||
506 | |||
507 | struct mwifiex_ps_param { | ||
508 | __le16 null_pkt_interval; | ||
509 | __le16 multiple_dtims; | ||
510 | __le16 bcn_miss_timeout; | ||
511 | __le16 local_listen_interval; | ||
512 | __le16 adhoc_wake_period; | ||
513 | __le16 mode; | ||
514 | __le16 delay_to_ps; | ||
515 | }; | ||
516 | |||
517 | #define BITMAP_AUTO_DS 0x01 | ||
518 | #define BITMAP_STA_PS 0x10 | ||
519 | |||
520 | struct mwifiex_ie_types_auto_ds_param { | ||
521 | struct mwifiex_ie_types_header header; | ||
522 | __le16 deep_sleep_timeout; | ||
523 | } __packed; | ||
524 | |||
525 | struct mwifiex_ie_types_ps_param { | ||
526 | struct mwifiex_ie_types_header header; | ||
527 | struct mwifiex_ps_param param; | ||
528 | } __packed; | ||
529 | |||
530 | struct host_cmd_ds_802_11_ps_mode_enh { | ||
531 | __le16 action; | ||
532 | |||
533 | union { | ||
534 | struct mwifiex_ps_param opt_ps; | ||
535 | __le16 ps_bitmap; | ||
536 | } params; | ||
537 | } __packed; | ||
538 | |||
539 | struct host_cmd_ds_get_hw_spec { | ||
540 | __le16 hw_if_version; | ||
541 | __le16 version; | ||
542 | __le16 reserved; | ||
543 | __le16 num_of_mcast_adr; | ||
544 | u8 permanent_addr[ETH_ALEN]; | ||
545 | __le16 region_code; | ||
546 | __le16 number_of_antenna; | ||
547 | __le32 fw_release_number; | ||
548 | __le32 reserved_1; | ||
549 | __le32 reserved_2; | ||
550 | __le32 reserved_3; | ||
551 | __le32 fw_cap_info; | ||
552 | __le32 dot_11n_dev_cap; | ||
553 | u8 dev_mcs_support; | ||
554 | __le16 mp_end_port; /* SDIO only, reserved for other interfacces */ | ||
555 | __le16 reserved_4; | ||
556 | } __packed; | ||
557 | |||
558 | struct host_cmd_ds_802_11_rssi_info { | ||
559 | __le16 action; | ||
560 | __le16 ndata; | ||
561 | __le16 nbcn; | ||
562 | __le16 reserved[9]; | ||
563 | long long reserved_1; | ||
564 | }; | ||
565 | |||
566 | struct host_cmd_ds_802_11_rssi_info_rsp { | ||
567 | __le16 action; | ||
568 | __le16 ndata; | ||
569 | __le16 nbcn; | ||
570 | __le16 data_rssi_last; | ||
571 | __le16 data_nf_last; | ||
572 | __le16 data_rssi_avg; | ||
573 | __le16 data_nf_avg; | ||
574 | __le16 bcn_rssi_last; | ||
575 | __le16 bcn_nf_last; | ||
576 | __le16 bcn_rssi_avg; | ||
577 | __le16 bcn_nf_avg; | ||
578 | long long tsf_bcn; | ||
579 | }; | ||
580 | |||
581 | struct host_cmd_ds_802_11_mac_address { | ||
582 | __le16 action; | ||
583 | u8 mac_addr[ETH_ALEN]; | ||
584 | }; | ||
585 | |||
586 | struct host_cmd_ds_mac_control { | ||
587 | __le16 action; | ||
588 | __le16 reserved; | ||
589 | }; | ||
590 | |||
591 | struct host_cmd_ds_mac_multicast_adr { | ||
592 | __le16 action; | ||
593 | __le16 num_of_adrs; | ||
594 | u8 mac_list[MWIFIEX_MAX_MULTICAST_LIST_SIZE][ETH_ALEN]; | ||
595 | } __packed; | ||
596 | |||
597 | struct host_cmd_ds_802_11_deauthenticate { | ||
598 | u8 mac_addr[ETH_ALEN]; | ||
599 | __le16 reason_code; | ||
600 | } __packed; | ||
601 | |||
602 | struct host_cmd_ds_802_11_associate { | ||
603 | u8 peer_sta_addr[ETH_ALEN]; | ||
604 | __le16 cap_info_bitmap; | ||
605 | __le16 listen_interval; | ||
606 | __le16 beacon_period; | ||
607 | u8 dtim_period; | ||
608 | } __packed; | ||
609 | |||
610 | struct ieee_types_assoc_rsp { | ||
611 | __le16 cap_info_bitmap; | ||
612 | __le16 status_code; | ||
613 | __le16 a_id; | ||
614 | u8 ie_buffer[1]; | ||
615 | } __packed; | ||
616 | |||
617 | struct host_cmd_ds_802_11_associate_rsp { | ||
618 | struct ieee_types_assoc_rsp assoc_rsp; | ||
619 | } __packed; | ||
620 | |||
621 | struct ieee_types_cf_param_set { | ||
622 | u8 element_id; | ||
623 | u8 len; | ||
624 | u8 cfp_cnt; | ||
625 | u8 cfp_period; | ||
626 | u16 cfp_max_duration; | ||
627 | u16 cfp_duration_remaining; | ||
628 | } __packed; | ||
629 | |||
630 | struct ieee_types_ibss_param_set { | ||
631 | u8 element_id; | ||
632 | u8 len; | ||
633 | __le16 atim_window; | ||
634 | } __packed; | ||
635 | |||
636 | union ieee_types_ss_param_set { | ||
637 | struct ieee_types_cf_param_set cf_param_set; | ||
638 | struct ieee_types_ibss_param_set ibss_param_set; | ||
639 | } __packed; | ||
640 | |||
641 | struct ieee_types_fh_param_set { | ||
642 | u8 element_id; | ||
643 | u8 len; | ||
644 | __le16 dwell_time; | ||
645 | u8 hop_set; | ||
646 | u8 hop_pattern; | ||
647 | u8 hop_index; | ||
648 | } __packed; | ||
649 | |||
650 | struct ieee_types_ds_param_set { | ||
651 | u8 element_id; | ||
652 | u8 len; | ||
653 | u8 current_chan; | ||
654 | } __packed; | ||
655 | |||
656 | union ieee_types_phy_param_set { | ||
657 | struct ieee_types_fh_param_set fh_param_set; | ||
658 | struct ieee_types_ds_param_set ds_param_set; | ||
659 | } __packed; | ||
660 | |||
661 | struct host_cmd_ds_802_11_ad_hoc_start { | ||
662 | u8 ssid[IEEE80211_MAX_SSID_LEN]; | ||
663 | u8 bss_mode; | ||
664 | __le16 beacon_period; | ||
665 | u8 dtim_period; | ||
666 | union ieee_types_ss_param_set ss_param_set; | ||
667 | union ieee_types_phy_param_set phy_param_set; | ||
668 | u16 reserved1; | ||
669 | __le16 cap_info_bitmap; | ||
670 | u8 DataRate[HOSTCMD_SUPPORTED_RATES]; | ||
671 | } __packed; | ||
672 | |||
673 | struct host_cmd_ds_802_11_ad_hoc_result { | ||
674 | u8 pad[3]; | ||
675 | u8 bssid[ETH_ALEN]; | ||
676 | } __packed; | ||
677 | |||
678 | struct adhoc_bss_desc { | ||
679 | u8 bssid[ETH_ALEN]; | ||
680 | u8 ssid[IEEE80211_MAX_SSID_LEN]; | ||
681 | u8 bss_mode; | ||
682 | __le16 beacon_period; | ||
683 | u8 dtim_period; | ||
684 | u8 time_stamp[8]; | ||
685 | u8 local_time[8]; | ||
686 | union ieee_types_phy_param_set phy_param_set; | ||
687 | union ieee_types_ss_param_set ss_param_set; | ||
688 | __le16 cap_info_bitmap; | ||
689 | u8 data_rates[HOSTCMD_SUPPORTED_RATES]; | ||
690 | |||
691 | /* | ||
692 | * DO NOT ADD ANY FIELDS TO THIS STRUCTURE. | ||
693 | * It is used in the Adhoc join command and will cause a | ||
694 | * binary layout mismatch with the firmware | ||
695 | */ | ||
696 | } __packed; | ||
697 | |||
698 | struct host_cmd_ds_802_11_ad_hoc_join { | ||
699 | struct adhoc_bss_desc bss_descriptor; | ||
700 | u16 reserved1; | ||
701 | u16 reserved2; | ||
702 | } __packed; | ||
703 | |||
704 | struct host_cmd_ds_802_11_get_log { | ||
705 | __le32 mcast_tx_frame; | ||
706 | __le32 failed; | ||
707 | __le32 retry; | ||
708 | __le32 multi_retry; | ||
709 | __le32 frame_dup; | ||
710 | __le32 rts_success; | ||
711 | __le32 rts_failure; | ||
712 | __le32 ack_failure; | ||
713 | __le32 rx_frag; | ||
714 | __le32 mcast_rx_frame; | ||
715 | __le32 fcs_error; | ||
716 | __le32 tx_frame; | ||
717 | __le32 reserved; | ||
718 | __le32 wep_icv_err_cnt[4]; | ||
719 | }; | ||
720 | |||
721 | struct host_cmd_ds_tx_rate_query { | ||
722 | u8 tx_rate; | ||
723 | /* Ht Info [Bit 0] RxRate format: LG=0, HT=1 | ||
724 | * [Bit 1] HT Bandwidth: BW20 = 0, BW40 = 1 | ||
725 | * [Bit 2] HT Guard Interval: LGI = 0, SGI = 1 */ | ||
726 | u8 ht_info; | ||
727 | } __packed; | ||
728 | |||
729 | enum Host_Sleep_Action { | ||
730 | HS_CONFIGURE = 0x0001, | ||
731 | HS_ACTIVATE = 0x0002, | ||
732 | }; | ||
733 | |||
734 | struct mwifiex_hs_config_param { | ||
735 | __le32 conditions; | ||
736 | u8 gpio; | ||
737 | u8 gap; | ||
738 | } __packed; | ||
739 | |||
740 | struct hs_activate_param { | ||
741 | u16 resp_ctrl; | ||
742 | } __packed; | ||
743 | |||
744 | struct host_cmd_ds_802_11_hs_cfg_enh { | ||
745 | __le16 action; | ||
746 | |||
747 | union { | ||
748 | struct mwifiex_hs_config_param hs_config; | ||
749 | struct hs_activate_param hs_activate; | ||
750 | } params; | ||
751 | } __packed; | ||
752 | |||
753 | enum SNMP_MIB_INDEX { | ||
754 | OP_RATE_SET_I = 1, | ||
755 | DTIM_PERIOD_I = 3, | ||
756 | RTS_THRESH_I = 5, | ||
757 | SHORT_RETRY_LIM_I = 6, | ||
758 | LONG_RETRY_LIM_I = 7, | ||
759 | FRAG_THRESH_I = 8, | ||
760 | DOT11D_I = 9, | ||
761 | }; | ||
762 | |||
763 | #define MAX_SNMP_BUF_SIZE 128 | ||
764 | |||
765 | struct host_cmd_ds_802_11_snmp_mib { | ||
766 | __le16 query_type; | ||
767 | __le16 oid; | ||
768 | __le16 buf_size; | ||
769 | u8 value[1]; | ||
770 | } __packed; | ||
771 | |||
772 | struct mwifiex_rate_scope { | ||
773 | __le16 type; | ||
774 | __le16 length; | ||
775 | __le16 hr_dsss_rate_bitmap; | ||
776 | __le16 ofdm_rate_bitmap; | ||
777 | __le16 ht_mcs_rate_bitmap[8]; | ||
778 | } __packed; | ||
779 | |||
780 | struct mwifiex_rate_drop_pattern { | ||
781 | __le16 type; | ||
782 | __le16 length; | ||
783 | __le32 rate_drop_mode; | ||
784 | } __packed; | ||
785 | |||
786 | struct host_cmd_ds_tx_rate_cfg { | ||
787 | __le16 action; | ||
788 | __le16 cfg_index; | ||
789 | } __packed; | ||
790 | |||
791 | struct mwifiex_power_group { | ||
792 | u8 modulation_class; | ||
793 | u8 first_rate_code; | ||
794 | u8 last_rate_code; | ||
795 | s8 power_step; | ||
796 | s8 power_min; | ||
797 | s8 power_max; | ||
798 | u8 ht_bandwidth; | ||
799 | u8 reserved; | ||
800 | } __packed; | ||
801 | |||
802 | struct mwifiex_types_power_group { | ||
803 | u16 type; | ||
804 | u16 length; | ||
805 | } __packed; | ||
806 | |||
807 | struct host_cmd_ds_txpwr_cfg { | ||
808 | __le16 action; | ||
809 | __le16 cfg_index; | ||
810 | __le32 mode; | ||
811 | } __packed; | ||
812 | |||
813 | #define MWIFIEX_USER_SCAN_CHAN_MAX 50 | ||
814 | |||
815 | #define MWIFIEX_MAX_SSID_LIST_LENGTH 10 | ||
816 | |||
817 | struct mwifiex_scan_cmd_config { | ||
818 | /* | ||
819 | * BSS mode to be sent in the firmware command | ||
820 | */ | ||
821 | u8 bss_mode; | ||
822 | |||
823 | /* Specific BSSID used to filter scan results in the firmware */ | ||
824 | u8 specific_bssid[ETH_ALEN]; | ||
825 | |||
826 | /* Length of TLVs sent in command starting at tlvBuffer */ | ||
827 | u32 tlv_buf_len; | ||
828 | |||
829 | /* | ||
830 | * SSID TLV(s) and ChanList TLVs to be sent in the firmware command | ||
831 | * | ||
832 | * TLV_TYPE_CHANLIST, mwifiex_ie_types_chan_list_param_set | ||
833 | * WLAN_EID_SSID, mwifiex_ie_types_ssid_param_set | ||
834 | */ | ||
835 | u8 tlv_buf[1]; /* SSID TLV(s) and ChanList TLVs are stored | ||
836 | here */ | ||
837 | } __packed; | ||
838 | |||
839 | struct mwifiex_user_scan_chan { | ||
840 | u8 chan_number; | ||
841 | u8 radio_type; | ||
842 | u8 scan_type; | ||
843 | u8 reserved; | ||
844 | u32 scan_time; | ||
845 | } __packed; | ||
846 | |||
847 | struct mwifiex_user_scan_ssid { | ||
848 | u8 ssid[IEEE80211_MAX_SSID_LEN + 1]; | ||
849 | u8 max_len; | ||
850 | } __packed; | ||
851 | |||
852 | struct mwifiex_user_scan_cfg { | ||
853 | /* | ||
854 | * Flag set to keep the previous scan table intact | ||
855 | * | ||
856 | * If set, the scan results will accumulate, replacing any previous | ||
857 | * matched entries for a BSS with the new scan data | ||
858 | */ | ||
859 | u8 keep_previous_scan; | ||
860 | /* | ||
861 | * BSS mode to be sent in the firmware command | ||
862 | */ | ||
863 | u8 bss_mode; | ||
864 | /* Configure the number of probe requests for active chan scans */ | ||
865 | u8 num_probes; | ||
866 | u8 reserved; | ||
867 | /* BSSID filter sent in the firmware command to limit the results */ | ||
868 | u8 specific_bssid[ETH_ALEN]; | ||
869 | /* SSID filter list used in the to limit the scan results */ | ||
870 | struct mwifiex_user_scan_ssid ssid_list[MWIFIEX_MAX_SSID_LIST_LENGTH]; | ||
871 | /* Variable number (fixed maximum) of channels to scan up */ | ||
872 | struct mwifiex_user_scan_chan chan_list[MWIFIEX_USER_SCAN_CHAN_MAX]; | ||
873 | } __packed; | ||
874 | |||
875 | struct ie_body { | ||
876 | u8 grp_key_oui[4]; | ||
877 | u8 ptk_cnt[2]; | ||
878 | u8 ptk_body[4]; | ||
879 | } __packed; | ||
880 | |||
881 | struct host_cmd_ds_802_11_scan { | ||
882 | u8 bss_mode; | ||
883 | u8 bssid[ETH_ALEN]; | ||
884 | u8 tlv_buffer[1]; | ||
885 | } __packed; | ||
886 | |||
887 | struct host_cmd_ds_802_11_scan_rsp { | ||
888 | __le16 bss_descript_size; | ||
889 | u8 number_of_sets; | ||
890 | u8 bss_desc_and_tlv_buffer[1]; | ||
891 | } __packed; | ||
892 | |||
893 | struct host_cmd_ds_802_11_bg_scan_query { | ||
894 | u8 flush; | ||
895 | } __packed; | ||
896 | |||
897 | struct host_cmd_ds_802_11_bg_scan_query_rsp { | ||
898 | u32 report_condition; | ||
899 | struct host_cmd_ds_802_11_scan_rsp scan_resp; | ||
900 | } __packed; | ||
901 | |||
902 | struct mwifiex_ietypes_domain_param_set { | ||
903 | struct mwifiex_ie_types_header header; | ||
904 | u8 country_code[IEEE80211_COUNTRY_STRING_LEN]; | ||
905 | struct ieee80211_country_ie_triplet triplet[1]; | ||
906 | } __packed; | ||
907 | |||
908 | struct host_cmd_ds_802_11d_domain_info { | ||
909 | __le16 action; | ||
910 | struct mwifiex_ietypes_domain_param_set domain; | ||
911 | } __packed; | ||
912 | |||
913 | struct host_cmd_ds_802_11d_domain_info_rsp { | ||
914 | __le16 action; | ||
915 | struct mwifiex_ietypes_domain_param_set domain; | ||
916 | } __packed; | ||
917 | |||
918 | struct host_cmd_ds_11n_addba_req { | ||
919 | u8 add_req_result; | ||
920 | u8 peer_mac_addr[ETH_ALEN]; | ||
921 | u8 dialog_token; | ||
922 | __le16 block_ack_param_set; | ||
923 | __le16 block_ack_tmo; | ||
924 | __le16 ssn; | ||
925 | } __packed; | ||
926 | |||
927 | struct host_cmd_ds_11n_addba_rsp { | ||
928 | u8 add_rsp_result; | ||
929 | u8 peer_mac_addr[ETH_ALEN]; | ||
930 | u8 dialog_token; | ||
931 | __le16 status_code; | ||
932 | __le16 block_ack_param_set; | ||
933 | __le16 block_ack_tmo; | ||
934 | __le16 ssn; | ||
935 | } __packed; | ||
936 | |||
937 | struct host_cmd_ds_11n_delba { | ||
938 | u8 del_result; | ||
939 | u8 peer_mac_addr[ETH_ALEN]; | ||
940 | __le16 del_ba_param_set; | ||
941 | __le16 reason_code; | ||
942 | u8 reserved; | ||
943 | } __packed; | ||
944 | |||
945 | struct host_cmd_ds_11n_batimeout { | ||
946 | u8 tid; | ||
947 | u8 peer_mac_addr[ETH_ALEN]; | ||
948 | u8 origninator; | ||
949 | } __packed; | ||
950 | |||
951 | struct host_cmd_ds_11n_cfg { | ||
952 | __le16 action; | ||
953 | __le16 ht_tx_cap; | ||
954 | __le16 ht_tx_info; | ||
955 | } __packed; | ||
956 | |||
957 | struct host_cmd_ds_txbuf_cfg { | ||
958 | __le16 action; | ||
959 | __le16 buff_size; | ||
960 | __le16 mp_end_port; /* SDIO only, reserved for other interfacces */ | ||
961 | __le16 reserved3; | ||
962 | } __packed; | ||
963 | |||
964 | struct host_cmd_ds_amsdu_aggr_ctrl { | ||
965 | __le16 action; | ||
966 | __le16 enable; | ||
967 | __le16 curr_buf_size; | ||
968 | } __packed; | ||
969 | |||
970 | struct mwifiex_ie_types_wmm_param_set { | ||
971 | struct mwifiex_ie_types_header header; | ||
972 | u8 wmm_ie[1]; | ||
973 | }; | ||
974 | |||
975 | struct mwifiex_ie_types_wmm_queue_status { | ||
976 | struct mwifiex_ie_types_header header; | ||
977 | u8 queue_index; | ||
978 | u8 disabled; | ||
979 | u16 medium_time; | ||
980 | u8 flow_required; | ||
981 | u8 flow_created; | ||
982 | u32 reserved; | ||
983 | }; | ||
984 | |||
985 | struct ieee_types_vendor_header { | ||
986 | u8 element_id; | ||
987 | u8 len; | ||
988 | u8 oui[3]; | ||
989 | u8 oui_type; | ||
990 | u8 oui_subtype; | ||
991 | u8 version; | ||
992 | } __packed; | ||
993 | |||
994 | struct ieee_types_wmm_ac_parameters { | ||
995 | u8 aci_aifsn_bitmap; | ||
996 | u8 ecw_bitmap; | ||
997 | __le16 tx_op_limit; | ||
998 | } __packed; | ||
999 | |||
1000 | struct ieee_types_wmm_parameter { | ||
1001 | /* | ||
1002 | * WMM Parameter IE - Vendor Specific Header: | ||
1003 | * element_id [221/0xdd] | ||
1004 | * Len [24] | ||
1005 | * Oui [00:50:f2] | ||
1006 | * OuiType [2] | ||
1007 | * OuiSubType [1] | ||
1008 | * Version [1] | ||
1009 | */ | ||
1010 | struct ieee_types_vendor_header vend_hdr; | ||
1011 | u8 qos_info_bitmap; | ||
1012 | u8 reserved; | ||
1013 | struct ieee_types_wmm_ac_parameters ac_params[IEEE80211_MAX_QUEUES]; | ||
1014 | } __packed; | ||
1015 | |||
1016 | struct ieee_types_wmm_info { | ||
1017 | |||
1018 | /* | ||
1019 | * WMM Info IE - Vendor Specific Header: | ||
1020 | * element_id [221/0xdd] | ||
1021 | * Len [7] | ||
1022 | * Oui [00:50:f2] | ||
1023 | * OuiType [2] | ||
1024 | * OuiSubType [0] | ||
1025 | * Version [1] | ||
1026 | */ | ||
1027 | struct ieee_types_vendor_header vend_hdr; | ||
1028 | |||
1029 | u8 qos_info_bitmap; | ||
1030 | } __packed; | ||
1031 | |||
1032 | struct host_cmd_ds_wmm_get_status { | ||
1033 | u8 queue_status_tlv[sizeof(struct mwifiex_ie_types_wmm_queue_status) * | ||
1034 | IEEE80211_MAX_QUEUES]; | ||
1035 | u8 wmm_param_tlv[sizeof(struct ieee_types_wmm_parameter) + 2]; | ||
1036 | } __packed; | ||
1037 | |||
1038 | struct mwifiex_wmm_ac_status { | ||
1039 | u8 disabled; | ||
1040 | u8 flow_required; | ||
1041 | u8 flow_created; | ||
1042 | }; | ||
1043 | |||
1044 | struct mwifiex_ie_types_htcap { | ||
1045 | struct mwifiex_ie_types_header header; | ||
1046 | struct ieee80211_ht_cap ht_cap; | ||
1047 | } __packed; | ||
1048 | |||
1049 | struct mwifiex_ie_types_htinfo { | ||
1050 | struct mwifiex_ie_types_header header; | ||
1051 | struct ieee80211_ht_info ht_info; | ||
1052 | } __packed; | ||
1053 | |||
1054 | struct mwifiex_ie_types_2040bssco { | ||
1055 | struct mwifiex_ie_types_header header; | ||
1056 | u8 bss_co_2040; | ||
1057 | } __packed; | ||
1058 | |||
1059 | struct mwifiex_ie_types_extcap { | ||
1060 | struct mwifiex_ie_types_header header; | ||
1061 | u8 ext_cap; | ||
1062 | } __packed; | ||
1063 | |||
1064 | struct host_cmd_ds_mac_reg_access { | ||
1065 | __le16 action; | ||
1066 | __le16 offset; | ||
1067 | __le32 value; | ||
1068 | } __packed; | ||
1069 | |||
1070 | struct host_cmd_ds_bbp_reg_access { | ||
1071 | __le16 action; | ||
1072 | __le16 offset; | ||
1073 | u8 value; | ||
1074 | u8 reserved[3]; | ||
1075 | } __packed; | ||
1076 | |||
1077 | struct host_cmd_ds_rf_reg_access { | ||
1078 | __le16 action; | ||
1079 | __le16 offset; | ||
1080 | u8 value; | ||
1081 | u8 reserved[3]; | ||
1082 | } __packed; | ||
1083 | |||
1084 | struct host_cmd_ds_pmic_reg_access { | ||
1085 | __le16 action; | ||
1086 | __le16 offset; | ||
1087 | u8 value; | ||
1088 | u8 reserved[3]; | ||
1089 | } __packed; | ||
1090 | |||
1091 | struct host_cmd_ds_802_11_eeprom_access { | ||
1092 | __le16 action; | ||
1093 | |||
1094 | __le16 offset; | ||
1095 | __le16 byte_count; | ||
1096 | u8 value; | ||
1097 | } __packed; | ||
1098 | |||
1099 | struct host_cmd_ds_802_11_rf_channel { | ||
1100 | __le16 action; | ||
1101 | __le16 current_channel; | ||
1102 | __le16 rf_type; | ||
1103 | __le16 reserved; | ||
1104 | u8 reserved_1[32]; | ||
1105 | } __packed; | ||
1106 | |||
1107 | struct host_cmd_ds_version_ext { | ||
1108 | u8 version_str_sel; | ||
1109 | char version_str[128]; | ||
1110 | } __packed; | ||
1111 | |||
1112 | struct host_cmd_ds_802_11_ibss_status { | ||
1113 | __le16 action; | ||
1114 | __le16 enable; | ||
1115 | u8 bssid[ETH_ALEN]; | ||
1116 | __le16 beacon_interval; | ||
1117 | __le16 atim_window; | ||
1118 | __le16 use_g_rate_protect; | ||
1119 | } __packed; | ||
1120 | |||
1121 | #define CONNECTION_TYPE_INFRA 0 | ||
1122 | #define CONNECTION_TYPE_ADHOC 1 | ||
1123 | |||
1124 | struct host_cmd_ds_set_bss_mode { | ||
1125 | u8 con_type; | ||
1126 | } __packed; | ||
1127 | |||
1128 | struct host_cmd_ds_command { | ||
1129 | __le16 command; | ||
1130 | __le16 size; | ||
1131 | __le16 seq_num; | ||
1132 | __le16 result; | ||
1133 | union { | ||
1134 | struct host_cmd_ds_get_hw_spec hw_spec; | ||
1135 | struct host_cmd_ds_mac_control mac_ctrl; | ||
1136 | struct host_cmd_ds_802_11_mac_address mac_addr; | ||
1137 | struct host_cmd_ds_mac_multicast_adr mc_addr; | ||
1138 | struct host_cmd_ds_802_11_get_log get_log; | ||
1139 | struct host_cmd_ds_802_11_rssi_info rssi_info; | ||
1140 | struct host_cmd_ds_802_11_rssi_info_rsp rssi_info_rsp; | ||
1141 | struct host_cmd_ds_802_11_snmp_mib smib; | ||
1142 | struct host_cmd_ds_802_11_rf_channel rf_channel; | ||
1143 | struct host_cmd_ds_tx_rate_query tx_rate; | ||
1144 | struct host_cmd_ds_tx_rate_cfg tx_rate_cfg; | ||
1145 | struct host_cmd_ds_txpwr_cfg txp_cfg; | ||
1146 | struct host_cmd_ds_802_11_ps_mode_enh psmode_enh; | ||
1147 | struct host_cmd_ds_802_11_hs_cfg_enh opt_hs_cfg; | ||
1148 | struct host_cmd_ds_802_11_scan scan; | ||
1149 | struct host_cmd_ds_802_11_scan_rsp scan_resp; | ||
1150 | struct host_cmd_ds_802_11_bg_scan_query bg_scan_query; | ||
1151 | struct host_cmd_ds_802_11_bg_scan_query_rsp bg_scan_query_resp; | ||
1152 | struct host_cmd_ds_802_11_associate associate; | ||
1153 | struct host_cmd_ds_802_11_associate_rsp associate_rsp; | ||
1154 | struct host_cmd_ds_802_11_deauthenticate deauth; | ||
1155 | struct host_cmd_ds_802_11_ad_hoc_start adhoc_start; | ||
1156 | struct host_cmd_ds_802_11_ad_hoc_result adhoc_result; | ||
1157 | struct host_cmd_ds_802_11_ad_hoc_join adhoc_join; | ||
1158 | struct host_cmd_ds_802_11d_domain_info domain_info; | ||
1159 | struct host_cmd_ds_802_11d_domain_info_rsp domain_info_resp; | ||
1160 | struct host_cmd_ds_11n_addba_req add_ba_req; | ||
1161 | struct host_cmd_ds_11n_addba_rsp add_ba_rsp; | ||
1162 | struct host_cmd_ds_11n_delba del_ba; | ||
1163 | struct host_cmd_ds_txbuf_cfg tx_buf; | ||
1164 | struct host_cmd_ds_amsdu_aggr_ctrl amsdu_aggr_ctrl; | ||
1165 | struct host_cmd_ds_11n_cfg htcfg; | ||
1166 | struct host_cmd_ds_wmm_get_status get_wmm_status; | ||
1167 | struct host_cmd_ds_802_11_key_material key_material; | ||
1168 | struct host_cmd_ds_version_ext verext; | ||
1169 | struct host_cmd_ds_802_11_ibss_status ibss_coalescing; | ||
1170 | struct host_cmd_ds_mac_reg_access mac_reg; | ||
1171 | struct host_cmd_ds_bbp_reg_access bbp_reg; | ||
1172 | struct host_cmd_ds_rf_reg_access rf_reg; | ||
1173 | struct host_cmd_ds_pmic_reg_access pmic_reg; | ||
1174 | struct host_cmd_ds_set_bss_mode bss_mode; | ||
1175 | struct host_cmd_ds_802_11_eeprom_access eeprom; | ||
1176 | } params; | ||
1177 | } __packed; | ||
1178 | |||
1179 | struct mwifiex_opt_sleep_confirm { | ||
1180 | __le16 command; | ||
1181 | __le16 size; | ||
1182 | __le16 seq_num; | ||
1183 | __le16 result; | ||
1184 | __le16 action; | ||
1185 | __le16 resp_ctrl; | ||
1186 | } __packed; | ||
1187 | #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..3f1559e61320 --- /dev/null +++ b/drivers/net/wireless/mwifiex/init.c | |||
@@ -0,0 +1,645 @@ | |||
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 | unsigned long flags; | ||
39 | |||
40 | bss_prio = kzalloc(sizeof(struct mwifiex_bss_prio_node), GFP_KERNEL); | ||
41 | if (!bss_prio) { | ||
42 | dev_err(adapter->dev, "%s: failed to alloc bss_prio\n", | ||
43 | __func__); | ||
44 | return -ENOMEM; | ||
45 | } | ||
46 | |||
47 | bss_prio->priv = priv; | ||
48 | INIT_LIST_HEAD(&bss_prio->list); | ||
49 | if (!adapter->bss_prio_tbl[priv->bss_priority].bss_prio_cur) | ||
50 | adapter->bss_prio_tbl[priv->bss_priority].bss_prio_cur = | ||
51 | bss_prio; | ||
52 | |||
53 | spin_lock_irqsave(&adapter->bss_prio_tbl[priv->bss_priority] | ||
54 | .bss_prio_lock, flags); | ||
55 | list_add_tail(&bss_prio->list, | ||
56 | &adapter->bss_prio_tbl[priv->bss_priority] | ||
57 | .bss_prio_head); | ||
58 | spin_unlock_irqrestore(&adapter->bss_prio_tbl[priv->bss_priority] | ||
59 | .bss_prio_lock, flags); | ||
60 | |||
61 | return 0; | ||
62 | } | ||
63 | |||
64 | /* | ||
65 | * This function initializes the private structure and sets default | ||
66 | * values to the members. | ||
67 | * | ||
68 | * Additionally, it also initializes all the locks and sets up all the | ||
69 | * lists. | ||
70 | */ | ||
71 | static int mwifiex_init_priv(struct mwifiex_private *priv) | ||
72 | { | ||
73 | u32 i; | ||
74 | |||
75 | priv->media_connected = false; | ||
76 | memset(priv->curr_addr, 0xff, ETH_ALEN); | ||
77 | |||
78 | priv->pkt_tx_ctrl = 0; | ||
79 | priv->bss_mode = NL80211_IFTYPE_STATION; | ||
80 | priv->data_rate = 0; /* Initially indicate the rate as auto */ | ||
81 | priv->is_data_rate_auto = true; | ||
82 | priv->bcn_avg_factor = DEFAULT_BCN_AVG_FACTOR; | ||
83 | priv->data_avg_factor = DEFAULT_DATA_AVG_FACTOR; | ||
84 | |||
85 | priv->sec_info.wep_status = MWIFIEX_802_11_WEP_DISABLED; | ||
86 | priv->sec_info.authentication_mode = NL80211_AUTHTYPE_OPEN_SYSTEM; | ||
87 | priv->sec_info.encryption_mode = 0; | ||
88 | for (i = 0; i < ARRAY_SIZE(priv->wep_key); i++) | ||
89 | memset(&priv->wep_key[i], 0, sizeof(struct mwifiex_wep_key)); | ||
90 | priv->wep_key_curr_index = 0; | ||
91 | priv->curr_pkt_filter = HostCmd_ACT_MAC_RX_ON | HostCmd_ACT_MAC_TX_ON | | ||
92 | HostCmd_ACT_MAC_ETHERNETII_ENABLE; | ||
93 | |||
94 | priv->beacon_period = 100; /* beacon interval */ ; | ||
95 | priv->attempted_bss_desc = NULL; | ||
96 | memset(&priv->curr_bss_params, 0, sizeof(priv->curr_bss_params)); | ||
97 | priv->listen_interval = MWIFIEX_DEFAULT_LISTEN_INTERVAL; | ||
98 | |||
99 | memset(&priv->prev_ssid, 0, sizeof(priv->prev_ssid)); | ||
100 | memset(&priv->prev_bssid, 0, sizeof(priv->prev_bssid)); | ||
101 | memset(&priv->assoc_rsp_buf, 0, sizeof(priv->assoc_rsp_buf)); | ||
102 | priv->assoc_rsp_size = 0; | ||
103 | priv->adhoc_channel = DEFAULT_AD_HOC_CHANNEL; | ||
104 | priv->atim_window = 0; | ||
105 | priv->adhoc_state = ADHOC_IDLE; | ||
106 | priv->tx_power_level = 0; | ||
107 | priv->max_tx_power_level = 0; | ||
108 | priv->min_tx_power_level = 0; | ||
109 | priv->tx_rate = 0; | ||
110 | priv->rxpd_htinfo = 0; | ||
111 | priv->rxpd_rate = 0; | ||
112 | priv->rate_bitmap = 0; | ||
113 | priv->data_rssi_last = 0; | ||
114 | priv->data_rssi_avg = 0; | ||
115 | priv->data_nf_avg = 0; | ||
116 | priv->data_nf_last = 0; | ||
117 | priv->bcn_rssi_last = 0; | ||
118 | priv->bcn_rssi_avg = 0; | ||
119 | priv->bcn_nf_avg = 0; | ||
120 | priv->bcn_nf_last = 0; | ||
121 | memset(&priv->wpa_ie, 0, sizeof(priv->wpa_ie)); | ||
122 | memset(&priv->aes_key, 0, sizeof(priv->aes_key)); | ||
123 | priv->wpa_ie_len = 0; | ||
124 | priv->wpa_is_gtk_set = false; | ||
125 | |||
126 | memset(&priv->assoc_tlv_buf, 0, sizeof(priv->assoc_tlv_buf)); | ||
127 | priv->assoc_tlv_buf_len = 0; | ||
128 | memset(&priv->wps, 0, sizeof(priv->wps)); | ||
129 | memset(&priv->gen_ie_buf, 0, sizeof(priv->gen_ie_buf)); | ||
130 | priv->gen_ie_buf_len = 0; | ||
131 | memset(priv->vs_ie, 0, sizeof(priv->vs_ie)); | ||
132 | |||
133 | priv->wmm_required = true; | ||
134 | priv->wmm_enabled = false; | ||
135 | priv->wmm_qosinfo = 0; | ||
136 | priv->curr_bcn_buf = NULL; | ||
137 | priv->curr_bcn_size = 0; | ||
138 | |||
139 | priv->scan_block = false; | ||
140 | |||
141 | return mwifiex_add_bss_prio_tbl(priv); | ||
142 | } | ||
143 | |||
144 | /* | ||
145 | * This function allocates buffers for members of the adapter | ||
146 | * structure. | ||
147 | * | ||
148 | * The memory allocated includes scan table, command buffers, and | ||
149 | * sleep confirm command buffer. In addition, the queues are | ||
150 | * also initialized. | ||
151 | */ | ||
152 | static int mwifiex_allocate_adapter(struct mwifiex_adapter *adapter) | ||
153 | { | ||
154 | int ret; | ||
155 | u32 buf_size; | ||
156 | struct mwifiex_bssdescriptor *temp_scan_table; | ||
157 | |||
158 | /* Allocate buffer to store the BSSID list */ | ||
159 | buf_size = sizeof(struct mwifiex_bssdescriptor) * IW_MAX_AP; | ||
160 | temp_scan_table = kzalloc(buf_size, GFP_KERNEL); | ||
161 | if (!temp_scan_table) { | ||
162 | dev_err(adapter->dev, "%s: failed to alloc temp_scan_table\n", | ||
163 | __func__); | ||
164 | return -ENOMEM; | ||
165 | } | ||
166 | |||
167 | adapter->scan_table = temp_scan_table; | ||
168 | |||
169 | /* Allocate command buffer */ | ||
170 | ret = mwifiex_alloc_cmd_buffer(adapter); | ||
171 | if (ret) { | ||
172 | dev_err(adapter->dev, "%s: failed to alloc cmd buffer\n", | ||
173 | __func__); | ||
174 | return -1; | ||
175 | } | ||
176 | |||
177 | adapter->sleep_cfm = | ||
178 | dev_alloc_skb(sizeof(struct mwifiex_opt_sleep_confirm) | ||
179 | + INTF_HEADER_LEN); | ||
180 | |||
181 | if (!adapter->sleep_cfm) { | ||
182 | dev_err(adapter->dev, "%s: failed to alloc sleep cfm" | ||
183 | " cmd buffer\n", __func__); | ||
184 | return -1; | ||
185 | } | ||
186 | skb_reserve(adapter->sleep_cfm, INTF_HEADER_LEN); | ||
187 | |||
188 | return 0; | ||
189 | } | ||
190 | |||
191 | /* | ||
192 | * This function initializes the adapter structure and sets default | ||
193 | * values to the members of adapter. | ||
194 | * | ||
195 | * This also initializes the WMM related parameters in the driver private | ||
196 | * structures. | ||
197 | */ | ||
198 | static void mwifiex_init_adapter(struct mwifiex_adapter *adapter) | ||
199 | { | ||
200 | struct mwifiex_opt_sleep_confirm *sleep_cfm_buf = NULL; | ||
201 | |||
202 | skb_put(adapter->sleep_cfm, sizeof(struct mwifiex_opt_sleep_confirm)); | ||
203 | sleep_cfm_buf = (struct mwifiex_opt_sleep_confirm *) | ||
204 | (adapter->sleep_cfm->data); | ||
205 | |||
206 | adapter->cmd_sent = false; | ||
207 | adapter->data_sent = true; | ||
208 | adapter->cmd_resp_received = false; | ||
209 | adapter->event_received = false; | ||
210 | adapter->data_received = false; | ||
211 | |||
212 | adapter->surprise_removed = false; | ||
213 | |||
214 | adapter->hw_status = MWIFIEX_HW_STATUS_INITIALIZING; | ||
215 | |||
216 | adapter->ps_mode = MWIFIEX_802_11_POWER_MODE_CAM; | ||
217 | adapter->ps_state = PS_STATE_AWAKE; | ||
218 | adapter->need_to_wakeup = false; | ||
219 | |||
220 | adapter->scan_mode = HostCmd_BSS_MODE_ANY; | ||
221 | adapter->specific_scan_time = MWIFIEX_SPECIFIC_SCAN_CHAN_TIME; | ||
222 | adapter->active_scan_time = MWIFIEX_ACTIVE_SCAN_CHAN_TIME; | ||
223 | adapter->passive_scan_time = MWIFIEX_PASSIVE_SCAN_CHAN_TIME; | ||
224 | |||
225 | adapter->num_in_scan_table = 0; | ||
226 | memset(adapter->scan_table, 0, | ||
227 | (sizeof(struct mwifiex_bssdescriptor) * IW_MAX_AP)); | ||
228 | adapter->scan_probes = 1; | ||
229 | |||
230 | memset(adapter->bcn_buf, 0, sizeof(adapter->bcn_buf)); | ||
231 | adapter->bcn_buf_end = adapter->bcn_buf; | ||
232 | |||
233 | adapter->multiple_dtim = 1; | ||
234 | |||
235 | adapter->local_listen_interval = 0; /* default value in firmware | ||
236 | will be used */ | ||
237 | |||
238 | adapter->is_deep_sleep = false; | ||
239 | |||
240 | adapter->delay_null_pkt = false; | ||
241 | adapter->delay_to_ps = 1000; | ||
242 | adapter->enhanced_ps_mode = PS_MODE_AUTO; | ||
243 | |||
244 | adapter->gen_null_pkt = false; /* Disable NULL Pkg generation by | ||
245 | default */ | ||
246 | adapter->pps_uapsd_mode = false; /* Disable pps/uapsd mode by | ||
247 | default */ | ||
248 | adapter->pm_wakeup_card_req = false; | ||
249 | |||
250 | adapter->pm_wakeup_fw_try = false; | ||
251 | |||
252 | adapter->max_tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_2K; | ||
253 | adapter->tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_2K; | ||
254 | adapter->curr_tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_2K; | ||
255 | |||
256 | adapter->is_hs_configured = false; | ||
257 | adapter->hs_cfg.conditions = cpu_to_le32(HOST_SLEEP_CFG_COND_DEF); | ||
258 | adapter->hs_cfg.gpio = HOST_SLEEP_CFG_GPIO_DEF; | ||
259 | adapter->hs_cfg.gap = HOST_SLEEP_CFG_GAP_DEF; | ||
260 | adapter->hs_activated = false; | ||
261 | |||
262 | memset(adapter->event_body, 0, sizeof(adapter->event_body)); | ||
263 | adapter->hw_dot_11n_dev_cap = 0; | ||
264 | adapter->hw_dev_mcs_support = 0; | ||
265 | adapter->chan_offset = 0; | ||
266 | adapter->adhoc_11n_enabled = false; | ||
267 | |||
268 | mwifiex_wmm_init(adapter); | ||
269 | |||
270 | if (adapter->sleep_cfm) { | ||
271 | memset(sleep_cfm_buf, 0, adapter->sleep_cfm->len); | ||
272 | sleep_cfm_buf->command = | ||
273 | cpu_to_le16(HostCmd_CMD_802_11_PS_MODE_ENH); | ||
274 | sleep_cfm_buf->size = | ||
275 | cpu_to_le16(adapter->sleep_cfm->len); | ||
276 | sleep_cfm_buf->result = 0; | ||
277 | sleep_cfm_buf->action = cpu_to_le16(SLEEP_CONFIRM); | ||
278 | sleep_cfm_buf->resp_ctrl = cpu_to_le16(RESP_NEEDED); | ||
279 | } | ||
280 | memset(&adapter->sleep_params, 0, sizeof(adapter->sleep_params)); | ||
281 | memset(&adapter->sleep_period, 0, sizeof(adapter->sleep_period)); | ||
282 | adapter->tx_lock_flag = false; | ||
283 | adapter->null_pkt_interval = 0; | ||
284 | adapter->fw_bands = 0; | ||
285 | adapter->config_bands = 0; | ||
286 | adapter->adhoc_start_band = 0; | ||
287 | adapter->scan_channels = NULL; | ||
288 | adapter->fw_release_number = 0; | ||
289 | adapter->fw_cap_info = 0; | ||
290 | memset(&adapter->upld_buf, 0, sizeof(adapter->upld_buf)); | ||
291 | adapter->event_cause = 0; | ||
292 | adapter->region_code = 0; | ||
293 | adapter->bcn_miss_time_out = DEFAULT_BCN_MISS_TIMEOUT; | ||
294 | adapter->adhoc_awake_period = 0; | ||
295 | memset(&adapter->arp_filter, 0, sizeof(adapter->arp_filter)); | ||
296 | adapter->arp_filter_size = 0; | ||
297 | } | ||
298 | |||
299 | /* | ||
300 | * This function frees the adapter structure. | ||
301 | * | ||
302 | * The freeing operation is done recursively, by canceling all | ||
303 | * pending commands, freeing the member buffers previously | ||
304 | * allocated (command buffers, scan table buffer, sleep confirm | ||
305 | * command buffer), stopping the timers and calling the cleanup | ||
306 | * routines for every interface, before the actual adapter | ||
307 | * structure is freed. | ||
308 | */ | ||
309 | static void | ||
310 | mwifiex_free_adapter(struct mwifiex_adapter *adapter) | ||
311 | { | ||
312 | if (!adapter) { | ||
313 | pr_err("%s: adapter is NULL\n", __func__); | ||
314 | return; | ||
315 | } | ||
316 | |||
317 | mwifiex_cancel_all_pending_cmd(adapter); | ||
318 | |||
319 | /* Free lock variables */ | ||
320 | mwifiex_free_lock_list(adapter); | ||
321 | |||
322 | /* Free command buffer */ | ||
323 | dev_dbg(adapter->dev, "info: free cmd buffer\n"); | ||
324 | mwifiex_free_cmd_buffer(adapter); | ||
325 | |||
326 | del_timer(&adapter->cmd_timer); | ||
327 | |||
328 | dev_dbg(adapter->dev, "info: free scan table\n"); | ||
329 | kfree(adapter->scan_table); | ||
330 | adapter->scan_table = NULL; | ||
331 | |||
332 | adapter->if_ops.cleanup_if(adapter); | ||
333 | |||
334 | dev_kfree_skb_any(adapter->sleep_cfm); | ||
335 | } | ||
336 | |||
337 | /* | ||
338 | * This function intializes the lock variables and | ||
339 | * the list heads. | ||
340 | */ | ||
341 | int mwifiex_init_lock_list(struct mwifiex_adapter *adapter) | ||
342 | { | ||
343 | struct mwifiex_private *priv; | ||
344 | s32 i, j; | ||
345 | |||
346 | spin_lock_init(&adapter->mwifiex_lock); | ||
347 | spin_lock_init(&adapter->int_lock); | ||
348 | spin_lock_init(&adapter->main_proc_lock); | ||
349 | spin_lock_init(&adapter->mwifiex_cmd_lock); | ||
350 | for (i = 0; i < adapter->priv_num; i++) { | ||
351 | if (adapter->priv[i]) { | ||
352 | priv = adapter->priv[i]; | ||
353 | spin_lock_init(&priv->rx_pkt_lock); | ||
354 | spin_lock_init(&priv->wmm.ra_list_spinlock); | ||
355 | spin_lock_init(&priv->curr_bcn_buf_lock); | ||
356 | } | ||
357 | } | ||
358 | |||
359 | /* Initialize cmd_free_q */ | ||
360 | INIT_LIST_HEAD(&adapter->cmd_free_q); | ||
361 | /* Initialize cmd_pending_q */ | ||
362 | INIT_LIST_HEAD(&adapter->cmd_pending_q); | ||
363 | /* Initialize scan_pending_q */ | ||
364 | INIT_LIST_HEAD(&adapter->scan_pending_q); | ||
365 | |||
366 | spin_lock_init(&adapter->cmd_free_q_lock); | ||
367 | spin_lock_init(&adapter->cmd_pending_q_lock); | ||
368 | spin_lock_init(&adapter->scan_pending_q_lock); | ||
369 | |||
370 | for (i = 0; i < adapter->priv_num; ++i) { | ||
371 | INIT_LIST_HEAD(&adapter->bss_prio_tbl[i].bss_prio_head); | ||
372 | adapter->bss_prio_tbl[i].bss_prio_cur = NULL; | ||
373 | spin_lock_init(&adapter->bss_prio_tbl[i].bss_prio_lock); | ||
374 | } | ||
375 | |||
376 | for (i = 0; i < adapter->priv_num; i++) { | ||
377 | if (!adapter->priv[i]) | ||
378 | continue; | ||
379 | priv = adapter->priv[i]; | ||
380 | for (j = 0; j < MAX_NUM_TID; ++j) { | ||
381 | INIT_LIST_HEAD(&priv->wmm.tid_tbl_ptr[j].ra_list); | ||
382 | spin_lock_init(&priv->wmm.tid_tbl_ptr[j].tid_tbl_lock); | ||
383 | } | ||
384 | INIT_LIST_HEAD(&priv->tx_ba_stream_tbl_ptr); | ||
385 | INIT_LIST_HEAD(&priv->rx_reorder_tbl_ptr); | ||
386 | |||
387 | spin_lock_init(&priv->tx_ba_stream_tbl_lock); | ||
388 | spin_lock_init(&priv->rx_reorder_tbl_lock); | ||
389 | } | ||
390 | |||
391 | return 0; | ||
392 | } | ||
393 | |||
394 | /* | ||
395 | * This function releases the lock variables and frees the locks and | ||
396 | * associated locks. | ||
397 | */ | ||
398 | void mwifiex_free_lock_list(struct mwifiex_adapter *adapter) | ||
399 | { | ||
400 | struct mwifiex_private *priv; | ||
401 | s32 i, j; | ||
402 | |||
403 | /* Free lists */ | ||
404 | list_del(&adapter->cmd_free_q); | ||
405 | list_del(&adapter->cmd_pending_q); | ||
406 | list_del(&adapter->scan_pending_q); | ||
407 | |||
408 | for (i = 0; i < adapter->priv_num; i++) | ||
409 | list_del(&adapter->bss_prio_tbl[i].bss_prio_head); | ||
410 | |||
411 | for (i = 0; i < adapter->priv_num; i++) { | ||
412 | if (adapter->priv[i]) { | ||
413 | priv = adapter->priv[i]; | ||
414 | for (j = 0; j < MAX_NUM_TID; ++j) | ||
415 | list_del(&priv->wmm.tid_tbl_ptr[j].ra_list); | ||
416 | list_del(&priv->tx_ba_stream_tbl_ptr); | ||
417 | list_del(&priv->rx_reorder_tbl_ptr); | ||
418 | } | ||
419 | } | ||
420 | } | ||
421 | |||
422 | /* | ||
423 | * This function initializes the firmware. | ||
424 | * | ||
425 | * The following operations are performed sequentially - | ||
426 | * - Allocate adapter structure | ||
427 | * - Initialize the adapter structure | ||
428 | * - Initialize the private structure | ||
429 | * - Add BSS priority tables to the adapter structure | ||
430 | * - For each interface, send the init commands to firmware | ||
431 | * - Send the first command in command pending queue, if available | ||
432 | */ | ||
433 | int mwifiex_init_fw(struct mwifiex_adapter *adapter) | ||
434 | { | ||
435 | int ret; | ||
436 | struct mwifiex_private *priv; | ||
437 | u8 i, first_sta = true; | ||
438 | int is_cmd_pend_q_empty; | ||
439 | unsigned long flags; | ||
440 | |||
441 | adapter->hw_status = MWIFIEX_HW_STATUS_INITIALIZING; | ||
442 | |||
443 | /* Allocate memory for member of adapter structure */ | ||
444 | ret = mwifiex_allocate_adapter(adapter); | ||
445 | if (ret) | ||
446 | return -1; | ||
447 | |||
448 | /* Initialize adapter structure */ | ||
449 | mwifiex_init_adapter(adapter); | ||
450 | |||
451 | for (i = 0; i < adapter->priv_num; i++) { | ||
452 | if (adapter->priv[i]) { | ||
453 | priv = adapter->priv[i]; | ||
454 | |||
455 | /* Initialize private structure */ | ||
456 | ret = mwifiex_init_priv(priv); | ||
457 | if (ret) | ||
458 | return -1; | ||
459 | } | ||
460 | } | ||
461 | for (i = 0; i < adapter->priv_num; i++) { | ||
462 | if (adapter->priv[i]) { | ||
463 | ret = mwifiex_sta_init_cmd(adapter->priv[i], first_sta); | ||
464 | if (ret == -1) | ||
465 | return -1; | ||
466 | |||
467 | first_sta = false; | ||
468 | } | ||
469 | } | ||
470 | |||
471 | spin_lock_irqsave(&adapter->cmd_pending_q_lock, flags); | ||
472 | is_cmd_pend_q_empty = list_empty(&adapter->cmd_pending_q); | ||
473 | spin_unlock_irqrestore(&adapter->cmd_pending_q_lock, flags); | ||
474 | if (!is_cmd_pend_q_empty) { | ||
475 | /* Send the first command in queue and return */ | ||
476 | if (mwifiex_main_process(adapter) != -1) | ||
477 | ret = -EINPROGRESS; | ||
478 | } else { | ||
479 | adapter->hw_status = MWIFIEX_HW_STATUS_READY; | ||
480 | } | ||
481 | |||
482 | return ret; | ||
483 | } | ||
484 | |||
485 | /* | ||
486 | * This function deletes the BSS priority tables. | ||
487 | * | ||
488 | * The function traverses through all the allocated BSS priority nodes | ||
489 | * in every BSS priority table and frees them. | ||
490 | */ | ||
491 | static void mwifiex_delete_bss_prio_tbl(struct mwifiex_private *priv) | ||
492 | { | ||
493 | int i; | ||
494 | struct mwifiex_adapter *adapter = priv->adapter; | ||
495 | struct mwifiex_bss_prio_node *bssprio_node, *tmp_node, **cur; | ||
496 | struct list_head *head; | ||
497 | spinlock_t *lock; | ||
498 | unsigned long flags; | ||
499 | |||
500 | for (i = 0; i < adapter->priv_num; ++i) { | ||
501 | head = &adapter->bss_prio_tbl[i].bss_prio_head; | ||
502 | cur = &adapter->bss_prio_tbl[i].bss_prio_cur; | ||
503 | lock = &adapter->bss_prio_tbl[i].bss_prio_lock; | ||
504 | dev_dbg(adapter->dev, "info: delete BSS priority table," | ||
505 | " index = %d, i = %d, head = %p, cur = %p\n", | ||
506 | priv->bss_index, i, head, *cur); | ||
507 | if (*cur) { | ||
508 | spin_lock_irqsave(lock, flags); | ||
509 | if (list_empty(head)) { | ||
510 | spin_unlock_irqrestore(lock, flags); | ||
511 | continue; | ||
512 | } | ||
513 | bssprio_node = list_first_entry(head, | ||
514 | struct mwifiex_bss_prio_node, list); | ||
515 | spin_unlock_irqrestore(lock, flags); | ||
516 | |||
517 | list_for_each_entry_safe(bssprio_node, tmp_node, head, | ||
518 | list) { | ||
519 | if (bssprio_node->priv == priv) { | ||
520 | dev_dbg(adapter->dev, "info: Delete " | ||
521 | "node %p, next = %p\n", | ||
522 | bssprio_node, tmp_node); | ||
523 | spin_lock_irqsave(lock, flags); | ||
524 | list_del(&bssprio_node->list); | ||
525 | spin_unlock_irqrestore(lock, flags); | ||
526 | kfree(bssprio_node); | ||
527 | } | ||
528 | } | ||
529 | *cur = (struct mwifiex_bss_prio_node *)head; | ||
530 | } | ||
531 | } | ||
532 | } | ||
533 | |||
534 | /* | ||
535 | * This function is used to shutdown the driver. | ||
536 | * | ||
537 | * The following operations are performed sequentially - | ||
538 | * - Check if already shut down | ||
539 | * - Make sure the main process has stopped | ||
540 | * - Clean up the Tx and Rx queues | ||
541 | * - Delete BSS priority tables | ||
542 | * - Free the adapter | ||
543 | * - Notify completion | ||
544 | */ | ||
545 | int | ||
546 | mwifiex_shutdown_drv(struct mwifiex_adapter *adapter) | ||
547 | { | ||
548 | int ret = -EINPROGRESS; | ||
549 | struct mwifiex_private *priv; | ||
550 | s32 i; | ||
551 | unsigned long flags; | ||
552 | |||
553 | /* mwifiex already shutdown */ | ||
554 | if (adapter->hw_status == MWIFIEX_HW_STATUS_NOT_READY) | ||
555 | return 0; | ||
556 | |||
557 | adapter->hw_status = MWIFIEX_HW_STATUS_CLOSING; | ||
558 | /* wait for mwifiex_process to complete */ | ||
559 | if (adapter->mwifiex_processing) { | ||
560 | dev_warn(adapter->dev, "main process is still running\n"); | ||
561 | return ret; | ||
562 | } | ||
563 | |||
564 | /* shut down mwifiex */ | ||
565 | dev_dbg(adapter->dev, "info: shutdown mwifiex...\n"); | ||
566 | |||
567 | /* Clean up Tx/Rx queues and delete BSS priority table */ | ||
568 | for (i = 0; i < adapter->priv_num; i++) { | ||
569 | if (adapter->priv[i]) { | ||
570 | priv = adapter->priv[i]; | ||
571 | |||
572 | mwifiex_clean_txrx(priv); | ||
573 | mwifiex_delete_bss_prio_tbl(priv); | ||
574 | } | ||
575 | } | ||
576 | |||
577 | spin_lock_irqsave(&adapter->mwifiex_lock, flags); | ||
578 | |||
579 | /* Free adapter structure */ | ||
580 | mwifiex_free_adapter(adapter); | ||
581 | |||
582 | spin_unlock_irqrestore(&adapter->mwifiex_lock, flags); | ||
583 | |||
584 | /* Notify completion */ | ||
585 | ret = mwifiex_shutdown_fw_complete(adapter); | ||
586 | |||
587 | return ret; | ||
588 | } | ||
589 | |||
590 | /* | ||
591 | * This function downloads the firmware to the card. | ||
592 | * | ||
593 | * The actual download is preceded by two sanity checks - | ||
594 | * - Check if firmware is already running | ||
595 | * - Check if the interface is the winner to download the firmware | ||
596 | * | ||
597 | * ...and followed by another - | ||
598 | * - Check if the firmware is downloaded successfully | ||
599 | * | ||
600 | * After download is successfully completed, the host interrupts are enabled. | ||
601 | */ | ||
602 | int mwifiex_dnld_fw(struct mwifiex_adapter *adapter, | ||
603 | struct mwifiex_fw_image *pmfw) | ||
604 | { | ||
605 | int ret, winner; | ||
606 | u32 poll_num = 1; | ||
607 | |||
608 | /* Check if firmware is already running */ | ||
609 | ret = adapter->if_ops.check_fw_status(adapter, poll_num, &winner); | ||
610 | if (!ret) { | ||
611 | dev_notice(adapter->dev, | ||
612 | "WLAN FW already running! Skip FW download\n"); | ||
613 | goto done; | ||
614 | } | ||
615 | poll_num = MAX_FIRMWARE_POLL_TRIES; | ||
616 | |||
617 | /* Check if we are the winner for downloading FW */ | ||
618 | if (!winner) { | ||
619 | dev_notice(adapter->dev, | ||
620 | "Other interface already running!" | ||
621 | " Skip FW download\n"); | ||
622 | poll_num = MAX_MULTI_INTERFACE_POLL_TRIES; | ||
623 | goto poll_fw; | ||
624 | } | ||
625 | if (pmfw) { | ||
626 | /* Download firmware with helper */ | ||
627 | ret = adapter->if_ops.prog_fw(adapter, pmfw); | ||
628 | if (ret) { | ||
629 | dev_err(adapter->dev, "prog_fw failed ret=%#x\n", ret); | ||
630 | return ret; | ||
631 | } | ||
632 | } | ||
633 | |||
634 | poll_fw: | ||
635 | /* Check if the firmware is downloaded successfully or not */ | ||
636 | ret = adapter->if_ops.check_fw_status(adapter, poll_num, NULL); | ||
637 | if (ret) { | ||
638 | dev_err(adapter->dev, "FW failed to be active in time\n"); | ||
639 | return -1; | ||
640 | } | ||
641 | done: | ||
642 | /* re-enable host interrupt for mwifiex after fw dnld is successful */ | ||
643 | adapter->if_ops.enable_int(adapter); | ||
644 | return ret; | ||
645 | } | ||
diff --git a/drivers/net/wireless/mwifiex/ioctl.h b/drivers/net/wireless/mwifiex/ioctl.h new file mode 100644 index 000000000000..7c1c5ee40eb9 --- /dev/null +++ b/drivers/net/wireless/mwifiex/ioctl.h | |||
@@ -0,0 +1,331 @@ | |||
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_TYPE_UNCHANGED = 0, | ||
27 | MWIFIEX_SCAN_TYPE_ACTIVE, | ||
28 | MWIFIEX_SCAN_TYPE_PASSIVE | ||
29 | }; | ||
30 | |||
31 | struct mwifiex_user_scan { | ||
32 | u32 scan_cfg_len; | ||
33 | u8 scan_cfg_buf[1]; | ||
34 | }; | ||
35 | |||
36 | #define MWIFIEX_PROMISC_MODE 1 | ||
37 | #define MWIFIEX_MULTICAST_MODE 2 | ||
38 | #define MWIFIEX_ALL_MULTI_MODE 4 | ||
39 | #define MWIFIEX_MAX_MULTICAST_LIST_SIZE 32 | ||
40 | |||
41 | struct mwifiex_multicast_list { | ||
42 | u32 mode; | ||
43 | u32 num_multicast_addr; | ||
44 | u8 mac_list[MWIFIEX_MAX_MULTICAST_LIST_SIZE][ETH_ALEN]; | ||
45 | }; | ||
46 | |||
47 | struct mwifiex_chan_freq { | ||
48 | u32 channel; | ||
49 | u32 freq; | ||
50 | }; | ||
51 | |||
52 | struct mwifiex_ssid_bssid { | ||
53 | struct mwifiex_802_11_ssid ssid; | ||
54 | u8 bssid[ETH_ALEN]; | ||
55 | }; | ||
56 | |||
57 | enum { | ||
58 | BAND_B = 1, | ||
59 | BAND_G = 2, | ||
60 | BAND_A = 4, | ||
61 | BAND_GN = 8, | ||
62 | BAND_AN = 16, | ||
63 | }; | ||
64 | |||
65 | #define NO_SEC_CHANNEL 0 | ||
66 | #define SEC_CHANNEL_ABOVE 1 | ||
67 | #define SEC_CHANNEL_BELOW 3 | ||
68 | |||
69 | struct mwifiex_ds_band_cfg { | ||
70 | u32 config_bands; | ||
71 | u32 adhoc_start_band; | ||
72 | u32 adhoc_channel; | ||
73 | u32 sec_chan_offset; | ||
74 | }; | ||
75 | |||
76 | enum { | ||
77 | ADHOC_IDLE, | ||
78 | ADHOC_STARTED, | ||
79 | ADHOC_JOINED, | ||
80 | ADHOC_COALESCED | ||
81 | }; | ||
82 | |||
83 | struct mwifiex_ds_get_stats { | ||
84 | u32 mcast_tx_frame; | ||
85 | u32 failed; | ||
86 | u32 retry; | ||
87 | u32 multi_retry; | ||
88 | u32 frame_dup; | ||
89 | u32 rts_success; | ||
90 | u32 rts_failure; | ||
91 | u32 ack_failure; | ||
92 | u32 rx_frag; | ||
93 | u32 mcast_rx_frame; | ||
94 | u32 fcs_error; | ||
95 | u32 tx_frame; | ||
96 | u32 wep_icv_error[4]; | ||
97 | }; | ||
98 | |||
99 | #define BCN_RSSI_AVG_MASK 0x00000002 | ||
100 | #define BCN_NF_AVG_MASK 0x00000200 | ||
101 | #define ALL_RSSI_INFO_MASK 0x00000fff | ||
102 | |||
103 | struct mwifiex_ds_get_signal { | ||
104 | /* | ||
105 | * Bit0: Last Beacon RSSI, Bit1: Average Beacon RSSI, | ||
106 | * Bit2: Last Data RSSI, Bit3: Average Data RSSI, | ||
107 | * Bit4: Last Beacon SNR, Bit5: Average Beacon SNR, | ||
108 | * Bit6: Last Data SNR, Bit7: Average Data SNR, | ||
109 | * Bit8: Last Beacon NF, Bit9: Average Beacon NF, | ||
110 | * Bit10: Last Data NF, Bit11: Average Data NF | ||
111 | */ | ||
112 | u16 selector; | ||
113 | s16 bcn_rssi_last; | ||
114 | s16 bcn_rssi_avg; | ||
115 | s16 data_rssi_last; | ||
116 | s16 data_rssi_avg; | ||
117 | s16 bcn_snr_last; | ||
118 | s16 bcn_snr_avg; | ||
119 | s16 data_snr_last; | ||
120 | s16 data_snr_avg; | ||
121 | s16 bcn_nf_last; | ||
122 | s16 bcn_nf_avg; | ||
123 | s16 data_nf_last; | ||
124 | s16 data_nf_avg; | ||
125 | }; | ||
126 | |||
127 | #define MWIFIEX_MAX_VER_STR_LEN 128 | ||
128 | |||
129 | struct mwifiex_ver_ext { | ||
130 | u32 version_str_sel; | ||
131 | char version_str[MWIFIEX_MAX_VER_STR_LEN]; | ||
132 | }; | ||
133 | |||
134 | struct mwifiex_bss_info { | ||
135 | u32 bss_mode; | ||
136 | struct mwifiex_802_11_ssid ssid; | ||
137 | u32 scan_table_idx; | ||
138 | u32 bss_chan; | ||
139 | u32 region_code; | ||
140 | u32 media_connected; | ||
141 | u32 max_power_level; | ||
142 | u32 min_power_level; | ||
143 | u32 adhoc_state; | ||
144 | signed int bcn_nf_last; | ||
145 | u32 wep_status; | ||
146 | u32 is_hs_configured; | ||
147 | u32 is_deep_sleep; | ||
148 | u8 bssid[ETH_ALEN]; | ||
149 | }; | ||
150 | |||
151 | #define MAX_NUM_TID 8 | ||
152 | |||
153 | #define MAX_RX_WINSIZE 64 | ||
154 | |||
155 | struct mwifiex_ds_rx_reorder_tbl { | ||
156 | u16 tid; | ||
157 | u8 ta[ETH_ALEN]; | ||
158 | u32 start_win; | ||
159 | u32 win_size; | ||
160 | u32 buffer[MAX_RX_WINSIZE]; | ||
161 | }; | ||
162 | |||
163 | struct mwifiex_ds_tx_ba_stream_tbl { | ||
164 | u16 tid; | ||
165 | u8 ra[ETH_ALEN]; | ||
166 | }; | ||
167 | |||
168 | #define DBG_CMD_NUM 5 | ||
169 | |||
170 | struct mwifiex_debug_info { | ||
171 | u32 int_counter; | ||
172 | u32 packets_out[MAX_NUM_TID]; | ||
173 | u32 max_tx_buf_size; | ||
174 | u32 tx_buf_size; | ||
175 | u32 curr_tx_buf_size; | ||
176 | u32 tx_tbl_num; | ||
177 | struct mwifiex_ds_tx_ba_stream_tbl | ||
178 | tx_tbl[MWIFIEX_MAX_TX_BASTREAM_SUPPORTED]; | ||
179 | u32 rx_tbl_num; | ||
180 | struct mwifiex_ds_rx_reorder_tbl rx_tbl | ||
181 | [MWIFIEX_MAX_RX_BASTREAM_SUPPORTED]; | ||
182 | u16 ps_mode; | ||
183 | u32 ps_state; | ||
184 | u8 is_deep_sleep; | ||
185 | u8 pm_wakeup_card_req; | ||
186 | u32 pm_wakeup_fw_try; | ||
187 | u8 is_hs_configured; | ||
188 | u8 hs_activated; | ||
189 | u32 num_cmd_host_to_card_failure; | ||
190 | u32 num_cmd_sleep_cfm_host_to_card_failure; | ||
191 | u32 num_tx_host_to_card_failure; | ||
192 | u32 num_event_deauth; | ||
193 | u32 num_event_disassoc; | ||
194 | u32 num_event_link_lost; | ||
195 | u32 num_cmd_deauth; | ||
196 | u32 num_cmd_assoc_success; | ||
197 | u32 num_cmd_assoc_failure; | ||
198 | u32 num_tx_timeout; | ||
199 | u32 num_cmd_timeout; | ||
200 | u16 timeout_cmd_id; | ||
201 | u16 timeout_cmd_act; | ||
202 | u16 last_cmd_id[DBG_CMD_NUM]; | ||
203 | u16 last_cmd_act[DBG_CMD_NUM]; | ||
204 | u16 last_cmd_index; | ||
205 | u16 last_cmd_resp_id[DBG_CMD_NUM]; | ||
206 | u16 last_cmd_resp_index; | ||
207 | u16 last_event[DBG_CMD_NUM]; | ||
208 | u16 last_event_index; | ||
209 | u8 data_sent; | ||
210 | u8 cmd_sent; | ||
211 | u8 cmd_resp_received; | ||
212 | u8 event_received; | ||
213 | }; | ||
214 | |||
215 | #define MWIFIEX_KEY_INDEX_UNICAST 0x40000000 | ||
216 | #define WAPI_RXPN_LEN 16 | ||
217 | |||
218 | struct mwifiex_ds_encrypt_key { | ||
219 | u32 key_disable; | ||
220 | u32 key_index; | ||
221 | u32 key_len; | ||
222 | u8 key_material[WLAN_MAX_KEY_LEN]; | ||
223 | u8 mac_addr[ETH_ALEN]; | ||
224 | u32 is_wapi_key; | ||
225 | u8 wapi_rxpn[WAPI_RXPN_LEN]; | ||
226 | }; | ||
227 | |||
228 | struct mwifiex_rate_cfg { | ||
229 | u32 action; | ||
230 | u32 is_rate_auto; | ||
231 | u32 rate; | ||
232 | }; | ||
233 | |||
234 | struct mwifiex_power_cfg { | ||
235 | u32 is_power_auto; | ||
236 | u32 power_level; | ||
237 | }; | ||
238 | |||
239 | struct mwifiex_ds_hs_cfg { | ||
240 | u32 is_invoke_hostcmd; | ||
241 | /* Bit0: non-unicast data | ||
242 | * Bit1: unicast data | ||
243 | * Bit2: mac events | ||
244 | * Bit3: magic packet | ||
245 | */ | ||
246 | u32 conditions; | ||
247 | u32 gpio; | ||
248 | u32 gap; | ||
249 | }; | ||
250 | |||
251 | #define DEEP_SLEEP_ON 1 | ||
252 | #define DEEP_SLEEP_IDLE_TIME 100 | ||
253 | #define PS_MODE_AUTO 1 | ||
254 | |||
255 | struct mwifiex_ds_auto_ds { | ||
256 | u16 auto_ds; | ||
257 | u16 idle_time; | ||
258 | }; | ||
259 | |||
260 | struct mwifiex_ds_pm_cfg { | ||
261 | union { | ||
262 | u32 ps_mode; | ||
263 | struct mwifiex_ds_hs_cfg hs_cfg; | ||
264 | struct mwifiex_ds_auto_ds auto_deep_sleep; | ||
265 | u32 sleep_period; | ||
266 | } param; | ||
267 | }; | ||
268 | |||
269 | struct mwifiex_ds_11n_tx_cfg { | ||
270 | u16 tx_htcap; | ||
271 | u16 tx_htinfo; | ||
272 | }; | ||
273 | |||
274 | struct mwifiex_ds_11n_amsdu_aggr_ctrl { | ||
275 | u16 enable; | ||
276 | u16 curr_buf_size; | ||
277 | }; | ||
278 | |||
279 | #define MWIFIEX_NUM_OF_CMD_BUFFER 20 | ||
280 | #define MWIFIEX_SIZE_OF_CMD_BUFFER 2048 | ||
281 | |||
282 | enum { | ||
283 | MWIFIEX_IE_TYPE_GEN_IE = 0, | ||
284 | MWIFIEX_IE_TYPE_ARP_FILTER, | ||
285 | }; | ||
286 | |||
287 | enum { | ||
288 | MWIFIEX_REG_MAC = 1, | ||
289 | MWIFIEX_REG_BBP, | ||
290 | MWIFIEX_REG_RF, | ||
291 | MWIFIEX_REG_PMIC, | ||
292 | MWIFIEX_REG_CAU, | ||
293 | }; | ||
294 | |||
295 | struct mwifiex_ds_reg_rw { | ||
296 | __le32 type; | ||
297 | __le32 offset; | ||
298 | __le32 value; | ||
299 | }; | ||
300 | |||
301 | #define MAX_EEPROM_DATA 256 | ||
302 | |||
303 | struct mwifiex_ds_read_eeprom { | ||
304 | __le16 offset; | ||
305 | __le16 byte_count; | ||
306 | u8 value[MAX_EEPROM_DATA]; | ||
307 | }; | ||
308 | |||
309 | struct mwifiex_ds_misc_gen_ie { | ||
310 | u32 type; | ||
311 | u32 len; | ||
312 | u8 ie_data[IW_CUSTOM_MAX]; | ||
313 | }; | ||
314 | |||
315 | struct mwifiex_ds_misc_cmd { | ||
316 | u32 len; | ||
317 | u8 cmd[MWIFIEX_SIZE_OF_CMD_BUFFER]; | ||
318 | }; | ||
319 | |||
320 | #define MWIFIEX_MAX_VSIE_LEN (256) | ||
321 | #define MWIFIEX_MAX_VSIE_NUM (8) | ||
322 | #define MWIFIEX_VSIE_MASK_SCAN 0x01 | ||
323 | #define MWIFIEX_VSIE_MASK_ASSOC 0x02 | ||
324 | #define MWIFIEX_VSIE_MASK_ADHOC 0x04 | ||
325 | |||
326 | enum { | ||
327 | MWIFIEX_FUNC_INIT = 1, | ||
328 | MWIFIEX_FUNC_SHUTDOWN, | ||
329 | }; | ||
330 | |||
331 | #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..5eab3dc29b1c --- /dev/null +++ b/drivers/net/wireless/mwifiex/join.c | |||
@@ -0,0 +1,1423 @@ | |||
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 | __le64 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 | /* TSF at the time when beacon/probe_response was received */ | ||
120 | tsf_val = cpu_to_le64(bss_desc->network_tsf); | ||
121 | memcpy(*buffer, &tsf_val, sizeof(tsf_val)); | ||
122 | *buffer += sizeof(tsf_val); | ||
123 | |||
124 | memcpy(&tsf_val, bss_desc->time_stamp, sizeof(tsf_val)); | ||
125 | |||
126 | dev_dbg(priv->adapter->dev, "info: %s: TSF offset calc: %016llx - " | ||
127 | "%016llx\n", __func__, tsf_val, bss_desc->network_tsf); | ||
128 | |||
129 | memcpy(*buffer, &tsf_val, sizeof(tsf_val)); | ||
130 | *buffer += sizeof(tsf_val); | ||
131 | |||
132 | return sizeof(tsf_tlv.header) + (2 * sizeof(tsf_val)); | ||
133 | } | ||
134 | |||
135 | /* | ||
136 | * This function finds out the common rates between rate1 and rate2. | ||
137 | * | ||
138 | * It will fill common rates in rate1 as output if found. | ||
139 | * | ||
140 | * NOTE: Setting the MSB of the basic rates needs to be taken | ||
141 | * care of, either before or after calling this function. | ||
142 | */ | ||
143 | static int mwifiex_get_common_rates(struct mwifiex_private *priv, u8 *rate1, | ||
144 | u32 rate1_size, u8 *rate2, u32 rate2_size) | ||
145 | { | ||
146 | int ret; | ||
147 | u8 *ptr = rate1, *tmp; | ||
148 | u32 i, j; | ||
149 | |||
150 | tmp = kmalloc(rate1_size, GFP_KERNEL); | ||
151 | if (!tmp) { | ||
152 | dev_err(priv->adapter->dev, "failed to alloc tmp buf\n"); | ||
153 | return -ENOMEM; | ||
154 | } | ||
155 | |||
156 | memcpy(tmp, rate1, rate1_size); | ||
157 | memset(rate1, 0, rate1_size); | ||
158 | |||
159 | for (i = 0; rate2[i] && i < rate2_size; i++) { | ||
160 | for (j = 0; tmp[j] && j < rate1_size; j++) { | ||
161 | /* Check common rate, excluding the bit for | ||
162 | basic rate */ | ||
163 | if ((rate2[i] & 0x7F) == (tmp[j] & 0x7F)) { | ||
164 | *rate1++ = tmp[j]; | ||
165 | break; | ||
166 | } | ||
167 | } | ||
168 | } | ||
169 | |||
170 | dev_dbg(priv->adapter->dev, "info: Tx data rate set to %#x\n", | ||
171 | priv->data_rate); | ||
172 | |||
173 | if (!priv->is_data_rate_auto) { | ||
174 | while (*ptr) { | ||
175 | if ((*ptr & 0x7f) == priv->data_rate) { | ||
176 | ret = 0; | ||
177 | goto done; | ||
178 | } | ||
179 | ptr++; | ||
180 | } | ||
181 | dev_err(priv->adapter->dev, "previously set fixed data rate %#x" | ||
182 | " is not compatible with the network\n", | ||
183 | priv->data_rate); | ||
184 | |||
185 | ret = -1; | ||
186 | goto done; | ||
187 | } | ||
188 | |||
189 | ret = 0; | ||
190 | done: | ||
191 | kfree(tmp); | ||
192 | return ret; | ||
193 | } | ||
194 | |||
195 | /* | ||
196 | * This function creates the intersection of the rates supported by a | ||
197 | * target BSS and our adapter settings for use in an assoc/join command. | ||
198 | */ | ||
199 | static int | ||
200 | mwifiex_setup_rates_from_bssdesc(struct mwifiex_private *priv, | ||
201 | struct mwifiex_bssdescriptor *bss_desc, | ||
202 | u8 *out_rates, u32 *out_rates_size) | ||
203 | { | ||
204 | u8 card_rates[MWIFIEX_SUPPORTED_RATES]; | ||
205 | u32 card_rates_size; | ||
206 | |||
207 | /* Copy AP supported rates */ | ||
208 | memcpy(out_rates, bss_desc->supported_rates, MWIFIEX_SUPPORTED_RATES); | ||
209 | /* Get the STA supported rates */ | ||
210 | card_rates_size = mwifiex_get_active_data_rates(priv, card_rates); | ||
211 | /* Get the common rates between AP and STA supported rates */ | ||
212 | if (mwifiex_get_common_rates(priv, out_rates, MWIFIEX_SUPPORTED_RATES, | ||
213 | card_rates, card_rates_size)) { | ||
214 | *out_rates_size = 0; | ||
215 | dev_err(priv->adapter->dev, "%s: cannot get common rates\n", | ||
216 | __func__); | ||
217 | return -1; | ||
218 | } | ||
219 | |||
220 | *out_rates_size = | ||
221 | min_t(size_t, strlen(out_rates), MWIFIEX_SUPPORTED_RATES); | ||
222 | |||
223 | return 0; | ||
224 | } | ||
225 | |||
226 | /* | ||
227 | * This function updates the scan entry TSF timestamps to reflect | ||
228 | * a new association. | ||
229 | */ | ||
230 | static void | ||
231 | mwifiex_update_tsf_timestamps(struct mwifiex_private *priv, | ||
232 | struct mwifiex_bssdescriptor *new_bss_desc) | ||
233 | { | ||
234 | struct mwifiex_adapter *adapter = priv->adapter; | ||
235 | u32 table_idx; | ||
236 | long long new_tsf_base; | ||
237 | signed long long tsf_delta; | ||
238 | |||
239 | memcpy(&new_tsf_base, new_bss_desc->time_stamp, sizeof(new_tsf_base)); | ||
240 | |||
241 | tsf_delta = new_tsf_base - new_bss_desc->network_tsf; | ||
242 | |||
243 | dev_dbg(adapter->dev, "info: TSF: update TSF timestamps, " | ||
244 | "0x%016llx -> 0x%016llx\n", | ||
245 | new_bss_desc->network_tsf, new_tsf_base); | ||
246 | |||
247 | for (table_idx = 0; table_idx < adapter->num_in_scan_table; | ||
248 | table_idx++) | ||
249 | adapter->scan_table[table_idx].network_tsf += tsf_delta; | ||
250 | } | ||
251 | |||
252 | /* | ||
253 | * This function appends a WAPI IE. | ||
254 | * | ||
255 | * This function is called from the network join command preparation routine. | ||
256 | * | ||
257 | * If the IE buffer has been setup by the application, this routine appends | ||
258 | * the buffer as a WAPI TLV type to the request. | ||
259 | */ | ||
260 | static int | ||
261 | mwifiex_cmd_append_wapi_ie(struct mwifiex_private *priv, u8 **buffer) | ||
262 | { | ||
263 | int retLen = 0; | ||
264 | struct mwifiex_ie_types_header ie_header; | ||
265 | |||
266 | /* Null Checks */ | ||
267 | if (buffer == NULL) | ||
268 | return 0; | ||
269 | if (*buffer == NULL) | ||
270 | return 0; | ||
271 | |||
272 | /* | ||
273 | * If there is a wapi ie buffer setup, append it to the return | ||
274 | * parameter buffer pointer. | ||
275 | */ | ||
276 | if (priv->wapi_ie_len) { | ||
277 | dev_dbg(priv->adapter->dev, "cmd: append wapi ie %d to %p\n", | ||
278 | priv->wapi_ie_len, *buffer); | ||
279 | |||
280 | /* Wrap the generic IE buffer with a pass through TLV type */ | ||
281 | ie_header.type = cpu_to_le16(TLV_TYPE_WAPI_IE); | ||
282 | ie_header.len = cpu_to_le16(priv->wapi_ie_len); | ||
283 | memcpy(*buffer, &ie_header, sizeof(ie_header)); | ||
284 | |||
285 | /* Increment the return size and the return buffer pointer | ||
286 | param */ | ||
287 | *buffer += sizeof(ie_header); | ||
288 | retLen += sizeof(ie_header); | ||
289 | |||
290 | /* Copy the wapi IE buffer to the output buffer, advance | ||
291 | pointer */ | ||
292 | memcpy(*buffer, priv->wapi_ie, priv->wapi_ie_len); | ||
293 | |||
294 | /* Increment the return size and the return buffer pointer | ||
295 | param */ | ||
296 | *buffer += priv->wapi_ie_len; | ||
297 | retLen += priv->wapi_ie_len; | ||
298 | |||
299 | } | ||
300 | /* return the length appended to the buffer */ | ||
301 | return retLen; | ||
302 | } | ||
303 | |||
304 | /* | ||
305 | * This function appends rsn ie tlv for wpa/wpa2 security modes. | ||
306 | * It is called from the network join command preparation routine. | ||
307 | */ | ||
308 | static int mwifiex_append_rsn_ie_wpa_wpa2(struct mwifiex_private *priv, | ||
309 | u8 **buffer) | ||
310 | { | ||
311 | struct mwifiex_ie_types_rsn_param_set *rsn_ie_tlv; | ||
312 | int rsn_ie_len; | ||
313 | |||
314 | if (!buffer || !(*buffer)) | ||
315 | return 0; | ||
316 | |||
317 | rsn_ie_tlv = (struct mwifiex_ie_types_rsn_param_set *) (*buffer); | ||
318 | rsn_ie_tlv->header.type = cpu_to_le16((u16) priv->wpa_ie[0]); | ||
319 | rsn_ie_tlv->header.type = cpu_to_le16( | ||
320 | le16_to_cpu(rsn_ie_tlv->header.type) & 0x00FF); | ||
321 | rsn_ie_tlv->header.len = cpu_to_le16((u16) priv->wpa_ie[1]); | ||
322 | rsn_ie_tlv->header.len = cpu_to_le16(le16_to_cpu(rsn_ie_tlv->header.len) | ||
323 | & 0x00FF); | ||
324 | if (le16_to_cpu(rsn_ie_tlv->header.len) <= (sizeof(priv->wpa_ie) - 2)) | ||
325 | memcpy(rsn_ie_tlv->rsn_ie, &priv->wpa_ie[2], | ||
326 | le16_to_cpu(rsn_ie_tlv->header.len)); | ||
327 | else | ||
328 | return -1; | ||
329 | |||
330 | rsn_ie_len = sizeof(rsn_ie_tlv->header) + | ||
331 | le16_to_cpu(rsn_ie_tlv->header.len); | ||
332 | *buffer += rsn_ie_len; | ||
333 | |||
334 | return rsn_ie_len; | ||
335 | } | ||
336 | |||
337 | /* | ||
338 | * This function prepares command for association. | ||
339 | * | ||
340 | * This sets the following parameters - | ||
341 | * - Peer MAC address | ||
342 | * - Listen interval | ||
343 | * - Beacon interval | ||
344 | * - Capability information | ||
345 | * | ||
346 | * ...and the following TLVs, as required - | ||
347 | * - SSID TLV | ||
348 | * - PHY TLV | ||
349 | * - SS TLV | ||
350 | * - Rates TLV | ||
351 | * - Authentication TLV | ||
352 | * - Channel TLV | ||
353 | * - WPA/WPA2 IE | ||
354 | * - 11n TLV | ||
355 | * - Vendor specific TLV | ||
356 | * - WMM TLV | ||
357 | * - WAPI IE | ||
358 | * - Generic IE | ||
359 | * - TSF TLV | ||
360 | * | ||
361 | * Preparation also includes - | ||
362 | * - Setting command ID and proper size | ||
363 | * - Ensuring correct endian-ness | ||
364 | */ | ||
365 | int mwifiex_cmd_802_11_associate(struct mwifiex_private *priv, | ||
366 | struct host_cmd_ds_command *cmd, | ||
367 | void *data_buf) | ||
368 | { | ||
369 | struct host_cmd_ds_802_11_associate *assoc = &cmd->params.associate; | ||
370 | struct mwifiex_bssdescriptor *bss_desc; | ||
371 | struct mwifiex_ie_types_ssid_param_set *ssid_tlv; | ||
372 | struct mwifiex_ie_types_phy_param_set *phy_tlv; | ||
373 | struct mwifiex_ie_types_ss_param_set *ss_tlv; | ||
374 | struct mwifiex_ie_types_rates_param_set *rates_tlv; | ||
375 | struct mwifiex_ie_types_auth_type *auth_tlv; | ||
376 | struct mwifiex_ie_types_chan_list_param_set *chan_tlv; | ||
377 | u8 rates[MWIFIEX_SUPPORTED_RATES]; | ||
378 | u32 rates_size; | ||
379 | u16 tmp_cap; | ||
380 | u8 *pos; | ||
381 | int rsn_ie_len = 0; | ||
382 | |||
383 | bss_desc = (struct mwifiex_bssdescriptor *) data_buf; | ||
384 | pos = (u8 *) assoc; | ||
385 | |||
386 | mwifiex_cfg_tx_buf(priv, bss_desc); | ||
387 | |||
388 | cmd->command = cpu_to_le16(HostCmd_CMD_802_11_ASSOCIATE); | ||
389 | |||
390 | /* Save so we know which BSS Desc to use in the response handler */ | ||
391 | priv->attempted_bss_desc = bss_desc; | ||
392 | |||
393 | memcpy(assoc->peer_sta_addr, | ||
394 | bss_desc->mac_address, sizeof(assoc->peer_sta_addr)); | ||
395 | pos += sizeof(assoc->peer_sta_addr); | ||
396 | |||
397 | /* Set the listen interval */ | ||
398 | assoc->listen_interval = cpu_to_le16(priv->listen_interval); | ||
399 | /* Set the beacon period */ | ||
400 | assoc->beacon_period = cpu_to_le16(bss_desc->beacon_period); | ||
401 | |||
402 | pos += sizeof(assoc->cap_info_bitmap); | ||
403 | pos += sizeof(assoc->listen_interval); | ||
404 | pos += sizeof(assoc->beacon_period); | ||
405 | pos += sizeof(assoc->dtim_period); | ||
406 | |||
407 | ssid_tlv = (struct mwifiex_ie_types_ssid_param_set *) pos; | ||
408 | ssid_tlv->header.type = cpu_to_le16(WLAN_EID_SSID); | ||
409 | ssid_tlv->header.len = cpu_to_le16((u16) bss_desc->ssid.ssid_len); | ||
410 | memcpy(ssid_tlv->ssid, bss_desc->ssid.ssid, | ||
411 | le16_to_cpu(ssid_tlv->header.len)); | ||
412 | pos += sizeof(ssid_tlv->header) + le16_to_cpu(ssid_tlv->header.len); | ||
413 | |||
414 | phy_tlv = (struct mwifiex_ie_types_phy_param_set *) pos; | ||
415 | phy_tlv->header.type = cpu_to_le16(WLAN_EID_DS_PARAMS); | ||
416 | phy_tlv->header.len = cpu_to_le16(sizeof(phy_tlv->fh_ds.ds_param_set)); | ||
417 | memcpy(&phy_tlv->fh_ds.ds_param_set, | ||
418 | &bss_desc->phy_param_set.ds_param_set.current_chan, | ||
419 | sizeof(phy_tlv->fh_ds.ds_param_set)); | ||
420 | pos += sizeof(phy_tlv->header) + le16_to_cpu(phy_tlv->header.len); | ||
421 | |||
422 | ss_tlv = (struct mwifiex_ie_types_ss_param_set *) pos; | ||
423 | ss_tlv->header.type = cpu_to_le16(WLAN_EID_CF_PARAMS); | ||
424 | ss_tlv->header.len = cpu_to_le16(sizeof(ss_tlv->cf_ibss.cf_param_set)); | ||
425 | pos += sizeof(ss_tlv->header) + le16_to_cpu(ss_tlv->header.len); | ||
426 | |||
427 | /* Get the common rates supported between the driver and the BSS Desc */ | ||
428 | if (mwifiex_setup_rates_from_bssdesc | ||
429 | (priv, bss_desc, rates, &rates_size)) | ||
430 | return -1; | ||
431 | |||
432 | /* Save the data rates into Current BSS state structure */ | ||
433 | priv->curr_bss_params.num_of_rates = rates_size; | ||
434 | memcpy(&priv->curr_bss_params.data_rates, rates, rates_size); | ||
435 | |||
436 | /* Setup the Rates TLV in the association command */ | ||
437 | rates_tlv = (struct mwifiex_ie_types_rates_param_set *) pos; | ||
438 | rates_tlv->header.type = cpu_to_le16(WLAN_EID_SUPP_RATES); | ||
439 | rates_tlv->header.len = cpu_to_le16((u16) rates_size); | ||
440 | memcpy(rates_tlv->rates, rates, rates_size); | ||
441 | pos += sizeof(rates_tlv->header) + rates_size; | ||
442 | dev_dbg(priv->adapter->dev, "info: ASSOC_CMD: rates size = %d\n", | ||
443 | rates_size); | ||
444 | |||
445 | /* Add the Authentication type to be used for Auth frames */ | ||
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( | ||
451 | (u16) priv->sec_info.authentication_mode); | ||
452 | else | ||
453 | auth_tlv->auth_type = cpu_to_le16(NL80211_AUTHTYPE_OPEN_SYSTEM); | ||
454 | |||
455 | pos += sizeof(auth_tlv->header) + le16_to_cpu(auth_tlv->header.len); | ||
456 | |||
457 | if (IS_SUPPORT_MULTI_BANDS(priv->adapter) | ||
458 | && !(ISSUPP_11NENABLED(priv->adapter->fw_cap_info) | ||
459 | && (!bss_desc->disable_11n) | ||
460 | && (priv->adapter->config_bands & BAND_GN | ||
461 | || priv->adapter->config_bands & BAND_AN) | ||
462 | && (bss_desc->bcn_ht_cap) | ||
463 | ) | ||
464 | ) { | ||
465 | /* Append a channel TLV for the channel the attempted AP was | ||
466 | found on */ | ||
467 | chan_tlv = (struct mwifiex_ie_types_chan_list_param_set *) pos; | ||
468 | chan_tlv->header.type = cpu_to_le16(TLV_TYPE_CHANLIST); | ||
469 | chan_tlv->header.len = | ||
470 | cpu_to_le16(sizeof(struct mwifiex_chan_scan_param_set)); | ||
471 | |||
472 | memset(chan_tlv->chan_scan_param, 0x00, | ||
473 | sizeof(struct mwifiex_chan_scan_param_set)); | ||
474 | chan_tlv->chan_scan_param[0].chan_number = | ||
475 | (bss_desc->phy_param_set.ds_param_set.current_chan); | ||
476 | dev_dbg(priv->adapter->dev, "info: Assoc: TLV Chan = %d\n", | ||
477 | chan_tlv->chan_scan_param[0].chan_number); | ||
478 | |||
479 | chan_tlv->chan_scan_param[0].radio_type = | ||
480 | mwifiex_band_to_radio_type((u8) bss_desc->bss_band); | ||
481 | |||
482 | dev_dbg(priv->adapter->dev, "info: Assoc: TLV Band = %d\n", | ||
483 | chan_tlv->chan_scan_param[0].radio_type); | ||
484 | pos += sizeof(chan_tlv->header) + | ||
485 | sizeof(struct mwifiex_chan_scan_param_set); | ||
486 | } | ||
487 | |||
488 | if (!priv->wps.session_enable) { | ||
489 | if (priv->sec_info.wpa_enabled || priv->sec_info.wpa2_enabled) | ||
490 | rsn_ie_len = mwifiex_append_rsn_ie_wpa_wpa2(priv, &pos); | ||
491 | |||
492 | if (rsn_ie_len == -1) | ||
493 | return -1; | ||
494 | } | ||
495 | |||
496 | if (ISSUPP_11NENABLED(priv->adapter->fw_cap_info) | ||
497 | && (!bss_desc->disable_11n) | ||
498 | && (priv->adapter->config_bands & BAND_GN | ||
499 | || priv->adapter->config_bands & BAND_AN)) | ||
500 | mwifiex_cmd_append_11n_tlv(priv, bss_desc, &pos); | ||
501 | |||
502 | /* Append vendor specific IE TLV */ | ||
503 | mwifiex_cmd_append_vsie_tlv(priv, MWIFIEX_VSIE_MASK_ASSOC, &pos); | ||
504 | |||
505 | mwifiex_wmm_process_association_req(priv, &pos, &bss_desc->wmm_ie, | ||
506 | bss_desc->bcn_ht_cap); | ||
507 | if (priv->sec_info.wapi_enabled && priv->wapi_ie_len) | ||
508 | mwifiex_cmd_append_wapi_ie(priv, &pos); | ||
509 | |||
510 | |||
511 | mwifiex_cmd_append_generic_ie(priv, &pos); | ||
512 | |||
513 | mwifiex_cmd_append_tsf_tlv(priv, &pos, bss_desc); | ||
514 | |||
515 | cmd->size = cpu_to_le16((u16) (pos - (u8 *) assoc) + S_DS_GEN); | ||
516 | |||
517 | /* Set the Capability info at last */ | ||
518 | tmp_cap = bss_desc->cap_info_bitmap; | ||
519 | |||
520 | if (priv->adapter->config_bands == BAND_B) | ||
521 | tmp_cap &= ~WLAN_CAPABILITY_SHORT_SLOT_TIME; | ||
522 | |||
523 | tmp_cap &= CAPINFO_MASK; | ||
524 | dev_dbg(priv->adapter->dev, "info: ASSOC_CMD: tmp_cap=%4X CAPINFO_MASK=%4lX\n", | ||
525 | tmp_cap, CAPINFO_MASK); | ||
526 | assoc->cap_info_bitmap = cpu_to_le16(tmp_cap); | ||
527 | |||
528 | return 0; | ||
529 | } | ||
530 | |||
531 | /* | ||
532 | * Association firmware command response handler | ||
533 | * | ||
534 | * The response buffer for the association command has the following | ||
535 | * memory layout. | ||
536 | * | ||
537 | * For cases where an association response was not received (indicated | ||
538 | * by the CapInfo and AId field): | ||
539 | * | ||
540 | * .------------------------------------------------------------. | ||
541 | * | Header(4 * sizeof(t_u16)): Standard command response hdr | | ||
542 | * .------------------------------------------------------------. | ||
543 | * | cap_info/Error Return(t_u16): | | ||
544 | * | 0xFFFF(-1): Internal error | | ||
545 | * | 0xFFFE(-2): Authentication unhandled message | | ||
546 | * | 0xFFFD(-3): Authentication refused | | ||
547 | * | 0xFFFC(-4): Timeout waiting for AP response | | ||
548 | * .------------------------------------------------------------. | ||
549 | * | status_code(t_u16): | | ||
550 | * | If cap_info is -1: | | ||
551 | * | An internal firmware failure prevented the | | ||
552 | * | command from being processed. The status_code | | ||
553 | * | will be set to 1. | | ||
554 | * | | | ||
555 | * | If cap_info is -2: | | ||
556 | * | An authentication frame was received but was | | ||
557 | * | not handled by the firmware. IEEE Status | | ||
558 | * | code for the failure is returned. | | ||
559 | * | | | ||
560 | * | If cap_info is -3: | | ||
561 | * | An authentication frame was received and the | | ||
562 | * | status_code is the IEEE Status reported in the | | ||
563 | * | response. | | ||
564 | * | | | ||
565 | * | If cap_info is -4: | | ||
566 | * | (1) Association response timeout | | ||
567 | * | (2) Authentication response timeout | | ||
568 | * .------------------------------------------------------------. | ||
569 | * | a_id(t_u16): 0xFFFF | | ||
570 | * .------------------------------------------------------------. | ||
571 | * | ||
572 | * | ||
573 | * For cases where an association response was received, the IEEE | ||
574 | * standard association response frame is returned: | ||
575 | * | ||
576 | * .------------------------------------------------------------. | ||
577 | * | Header(4 * sizeof(t_u16)): Standard command response hdr | | ||
578 | * .------------------------------------------------------------. | ||
579 | * | cap_info(t_u16): IEEE Capability | | ||
580 | * .------------------------------------------------------------. | ||
581 | * | status_code(t_u16): IEEE Status Code | | ||
582 | * .------------------------------------------------------------. | ||
583 | * | a_id(t_u16): IEEE Association ID | | ||
584 | * .------------------------------------------------------------. | ||
585 | * | IEEE IEs(variable): Any received IEs comprising the | | ||
586 | * | remaining portion of a received | | ||
587 | * | association response frame. | | ||
588 | * .------------------------------------------------------------. | ||
589 | * | ||
590 | * For simplistic handling, the status_code field can be used to determine | ||
591 | * an association success (0) or failure (non-zero). | ||
592 | */ | ||
593 | int mwifiex_ret_802_11_associate(struct mwifiex_private *priv, | ||
594 | struct host_cmd_ds_command *resp) | ||
595 | { | ||
596 | struct mwifiex_adapter *adapter = priv->adapter; | ||
597 | int ret = 0; | ||
598 | struct ieee_types_assoc_rsp *assoc_rsp; | ||
599 | struct mwifiex_bssdescriptor *bss_desc; | ||
600 | u8 enable_data = true; | ||
601 | |||
602 | assoc_rsp = (struct ieee_types_assoc_rsp *) &resp->params; | ||
603 | |||
604 | priv->assoc_rsp_size = min(le16_to_cpu(resp->size) - S_DS_GEN, | ||
605 | sizeof(priv->assoc_rsp_buf)); | ||
606 | |||
607 | memcpy(priv->assoc_rsp_buf, &resp->params, priv->assoc_rsp_size); | ||
608 | |||
609 | if (le16_to_cpu(assoc_rsp->status_code)) { | ||
610 | priv->adapter->dbg.num_cmd_assoc_failure++; | ||
611 | dev_err(priv->adapter->dev, "ASSOC_RESP: association failed, " | ||
612 | "status code = %d, error = 0x%x, a_id = 0x%x\n", | ||
613 | le16_to_cpu(assoc_rsp->status_code), | ||
614 | le16_to_cpu(assoc_rsp->cap_info_bitmap), | ||
615 | le16_to_cpu(assoc_rsp->a_id)); | ||
616 | |||
617 | ret = -1; | ||
618 | goto done; | ||
619 | } | ||
620 | |||
621 | /* Send a Media Connected event, according to the Spec */ | ||
622 | priv->media_connected = true; | ||
623 | |||
624 | priv->adapter->ps_state = PS_STATE_AWAKE; | ||
625 | priv->adapter->pps_uapsd_mode = false; | ||
626 | priv->adapter->tx_lock_flag = false; | ||
627 | |||
628 | /* Set the attempted BSSID Index to current */ | ||
629 | bss_desc = priv->attempted_bss_desc; | ||
630 | |||
631 | dev_dbg(priv->adapter->dev, "info: ASSOC_RESP: %s\n", | ||
632 | bss_desc->ssid.ssid); | ||
633 | |||
634 | /* Make a copy of current BSSID descriptor */ | ||
635 | memcpy(&priv->curr_bss_params.bss_descriptor, | ||
636 | bss_desc, sizeof(struct mwifiex_bssdescriptor)); | ||
637 | |||
638 | /* Update curr_bss_params */ | ||
639 | priv->curr_bss_params.bss_descriptor.channel | ||
640 | = bss_desc->phy_param_set.ds_param_set.current_chan; | ||
641 | |||
642 | priv->curr_bss_params.band = (u8) bss_desc->bss_band; | ||
643 | |||
644 | /* | ||
645 | * Adjust the timestamps in the scan table to be relative to the newly | ||
646 | * associated AP's TSF | ||
647 | */ | ||
648 | mwifiex_update_tsf_timestamps(priv, bss_desc); | ||
649 | |||
650 | if (bss_desc->wmm_ie.vend_hdr.element_id == WLAN_EID_VENDOR_SPECIFIC) | ||
651 | priv->curr_bss_params.wmm_enabled = true; | ||
652 | else | ||
653 | priv->curr_bss_params.wmm_enabled = false; | ||
654 | |||
655 | if ((priv->wmm_required || bss_desc->bcn_ht_cap) | ||
656 | && priv->curr_bss_params.wmm_enabled) | ||
657 | priv->wmm_enabled = true; | ||
658 | else | ||
659 | priv->wmm_enabled = false; | ||
660 | |||
661 | priv->curr_bss_params.wmm_uapsd_enabled = false; | ||
662 | |||
663 | if (priv->wmm_enabled) | ||
664 | priv->curr_bss_params.wmm_uapsd_enabled | ||
665 | = ((bss_desc->wmm_ie.qos_info_bitmap & | ||
666 | IEEE80211_WMM_IE_AP_QOSINFO_UAPSD) ? 1 : 0); | ||
667 | |||
668 | dev_dbg(priv->adapter->dev, "info: ASSOC_RESP: curr_pkt_filter is %#x\n", | ||
669 | priv->curr_pkt_filter); | ||
670 | if (priv->sec_info.wpa_enabled || priv->sec_info.wpa2_enabled) | ||
671 | priv->wpa_is_gtk_set = false; | ||
672 | |||
673 | if (priv->wmm_enabled) { | ||
674 | /* Don't re-enable carrier until we get the WMM_GET_STATUS | ||
675 | event */ | ||
676 | enable_data = false; | ||
677 | } else { | ||
678 | /* Since WMM is not enabled, setup the queues with the | ||
679 | defaults */ | ||
680 | mwifiex_wmm_setup_queue_priorities(priv, NULL); | ||
681 | mwifiex_wmm_setup_ac_downgrade(priv); | ||
682 | } | ||
683 | |||
684 | if (enable_data) | ||
685 | dev_dbg(priv->adapter->dev, | ||
686 | "info: post association, re-enabling data flow\n"); | ||
687 | |||
688 | /* Reset SNR/NF/RSSI values */ | ||
689 | priv->data_rssi_last = 0; | ||
690 | priv->data_nf_last = 0; | ||
691 | priv->data_rssi_avg = 0; | ||
692 | priv->data_nf_avg = 0; | ||
693 | priv->bcn_rssi_last = 0; | ||
694 | priv->bcn_nf_last = 0; | ||
695 | priv->bcn_rssi_avg = 0; | ||
696 | priv->bcn_nf_avg = 0; | ||
697 | priv->rxpd_rate = 0; | ||
698 | priv->rxpd_htinfo = 0; | ||
699 | |||
700 | mwifiex_save_curr_bcn(priv); | ||
701 | |||
702 | priv->adapter->dbg.num_cmd_assoc_success++; | ||
703 | |||
704 | dev_dbg(priv->adapter->dev, "info: ASSOC_RESP: associated\n"); | ||
705 | |||
706 | /* Add the ra_list here for infra mode as there will be only 1 ra | ||
707 | always */ | ||
708 | mwifiex_ralist_add(priv, | ||
709 | priv->curr_bss_params.bss_descriptor.mac_address); | ||
710 | |||
711 | if (!netif_carrier_ok(priv->netdev)) | ||
712 | netif_carrier_on(priv->netdev); | ||
713 | if (netif_queue_stopped(priv->netdev)) | ||
714 | netif_wake_queue(priv->netdev); | ||
715 | |||
716 | if (priv->sec_info.wpa_enabled || priv->sec_info.wpa2_enabled) | ||
717 | priv->scan_block = true; | ||
718 | |||
719 | done: | ||
720 | /* Need to indicate IOCTL complete */ | ||
721 | if (adapter->curr_cmd->wait_q_enabled) { | ||
722 | if (ret) | ||
723 | adapter->cmd_wait_q.status = -1; | ||
724 | else | ||
725 | adapter->cmd_wait_q.status = 0; | ||
726 | } | ||
727 | |||
728 | return ret; | ||
729 | } | ||
730 | |||
731 | /* | ||
732 | * This function prepares command for ad-hoc start. | ||
733 | * | ||
734 | * Driver will fill up SSID, BSS mode, IBSS parameters, physical | ||
735 | * parameters, probe delay, and capability information. Firmware | ||
736 | * will fill up beacon period, basic rates and operational rates. | ||
737 | * | ||
738 | * In addition, the following TLVs are added - | ||
739 | * - Channel TLV | ||
740 | * - Vendor specific IE | ||
741 | * - WPA/WPA2 IE | ||
742 | * - HT Capabilities IE | ||
743 | * - HT Information IE | ||
744 | * | ||
745 | * Preparation also includes - | ||
746 | * - Setting command ID and proper size | ||
747 | * - Ensuring correct endian-ness | ||
748 | */ | ||
749 | int | ||
750 | mwifiex_cmd_802_11_ad_hoc_start(struct mwifiex_private *priv, | ||
751 | struct host_cmd_ds_command *cmd, void *data_buf) | ||
752 | { | ||
753 | int rsn_ie_len = 0; | ||
754 | struct mwifiex_adapter *adapter = priv->adapter; | ||
755 | struct host_cmd_ds_802_11_ad_hoc_start *adhoc_start = | ||
756 | &cmd->params.adhoc_start; | ||
757 | struct mwifiex_bssdescriptor *bss_desc; | ||
758 | u32 cmd_append_size = 0; | ||
759 | u32 i; | ||
760 | u16 tmp_cap; | ||
761 | uint16_t ht_cap_info; | ||
762 | struct mwifiex_ie_types_chan_list_param_set *chan_tlv; | ||
763 | |||
764 | struct mwifiex_ie_types_htcap *ht_cap; | ||
765 | struct mwifiex_ie_types_htinfo *ht_info; | ||
766 | u8 *pos = (u8 *) adhoc_start + | ||
767 | sizeof(struct host_cmd_ds_802_11_ad_hoc_start); | ||
768 | |||
769 | if (!adapter) | ||
770 | return -1; | ||
771 | |||
772 | cmd->command = cpu_to_le16(HostCmd_CMD_802_11_AD_HOC_START); | ||
773 | |||
774 | bss_desc = &priv->curr_bss_params.bss_descriptor; | ||
775 | priv->attempted_bss_desc = bss_desc; | ||
776 | |||
777 | /* | ||
778 | * Fill in the parameters for 2 data structures: | ||
779 | * 1. struct host_cmd_ds_802_11_ad_hoc_start command | ||
780 | * 2. bss_desc | ||
781 | * Driver will fill up SSID, bss_mode,IBSS param, Physical Param, | ||
782 | * probe delay, and Cap info. | ||
783 | * Firmware will fill up beacon period, Basic rates | ||
784 | * and operational rates. | ||
785 | */ | ||
786 | |||
787 | memset(adhoc_start->ssid, 0, IEEE80211_MAX_SSID_LEN); | ||
788 | |||
789 | memcpy(adhoc_start->ssid, | ||
790 | ((struct mwifiex_802_11_ssid *) data_buf)->ssid, | ||
791 | ((struct mwifiex_802_11_ssid *) data_buf)->ssid_len); | ||
792 | |||
793 | dev_dbg(adapter->dev, "info: ADHOC_S_CMD: SSID = %s\n", | ||
794 | adhoc_start->ssid); | ||
795 | |||
796 | memset(bss_desc->ssid.ssid, 0, IEEE80211_MAX_SSID_LEN); | ||
797 | memcpy(bss_desc->ssid.ssid, | ||
798 | ((struct mwifiex_802_11_ssid *) data_buf)->ssid, | ||
799 | ((struct mwifiex_802_11_ssid *) data_buf)->ssid_len); | ||
800 | |||
801 | bss_desc->ssid.ssid_len = | ||
802 | ((struct mwifiex_802_11_ssid *) data_buf)->ssid_len; | ||
803 | |||
804 | /* Set the BSS mode */ | ||
805 | adhoc_start->bss_mode = HostCmd_BSS_MODE_IBSS; | ||
806 | bss_desc->bss_mode = NL80211_IFTYPE_ADHOC; | ||
807 | adhoc_start->beacon_period = cpu_to_le16(priv->beacon_period); | ||
808 | bss_desc->beacon_period = priv->beacon_period; | ||
809 | |||
810 | /* Set Physical param set */ | ||
811 | /* Parameter IE Id */ | ||
812 | #define DS_PARA_IE_ID 3 | ||
813 | /* Parameter IE length */ | ||
814 | #define DS_PARA_IE_LEN 1 | ||
815 | |||
816 | adhoc_start->phy_param_set.ds_param_set.element_id = DS_PARA_IE_ID; | ||
817 | adhoc_start->phy_param_set.ds_param_set.len = DS_PARA_IE_LEN; | ||
818 | |||
819 | if (!mwifiex_get_cfp_by_band_and_channel_from_cfg80211 | ||
820 | (priv, adapter->adhoc_start_band, (u16) | ||
821 | priv->adhoc_channel)) { | ||
822 | struct mwifiex_chan_freq_power *cfp; | ||
823 | cfp = mwifiex_get_cfp_by_band_and_channel_from_cfg80211(priv, | ||
824 | adapter->adhoc_start_band, FIRST_VALID_CHANNEL); | ||
825 | if (cfp) | ||
826 | priv->adhoc_channel = (u8) cfp->channel; | ||
827 | } | ||
828 | |||
829 | if (!priv->adhoc_channel) { | ||
830 | dev_err(adapter->dev, "ADHOC_S_CMD: adhoc_channel cannot be 0\n"); | ||
831 | return -1; | ||
832 | } | ||
833 | |||
834 | dev_dbg(adapter->dev, "info: ADHOC_S_CMD: creating ADHOC on channel %d\n", | ||
835 | priv->adhoc_channel); | ||
836 | |||
837 | priv->curr_bss_params.bss_descriptor.channel = priv->adhoc_channel; | ||
838 | priv->curr_bss_params.band = adapter->adhoc_start_band; | ||
839 | |||
840 | bss_desc->channel = priv->adhoc_channel; | ||
841 | adhoc_start->phy_param_set.ds_param_set.current_chan = | ||
842 | priv->adhoc_channel; | ||
843 | |||
844 | memcpy(&bss_desc->phy_param_set, &adhoc_start->phy_param_set, | ||
845 | sizeof(union ieee_types_phy_param_set)); | ||
846 | |||
847 | /* Set IBSS param set */ | ||
848 | /* IBSS parameter IE Id */ | ||
849 | #define IBSS_PARA_IE_ID 6 | ||
850 | /* IBSS parameter IE length */ | ||
851 | #define IBSS_PARA_IE_LEN 2 | ||
852 | |||
853 | adhoc_start->ss_param_set.ibss_param_set.element_id = IBSS_PARA_IE_ID; | ||
854 | adhoc_start->ss_param_set.ibss_param_set.len = IBSS_PARA_IE_LEN; | ||
855 | adhoc_start->ss_param_set.ibss_param_set.atim_window | ||
856 | = cpu_to_le16(priv->atim_window); | ||
857 | memcpy(&bss_desc->ss_param_set, &adhoc_start->ss_param_set, | ||
858 | sizeof(union ieee_types_ss_param_set)); | ||
859 | |||
860 | /* Set Capability info */ | ||
861 | bss_desc->cap_info_bitmap |= WLAN_CAPABILITY_IBSS; | ||
862 | tmp_cap = le16_to_cpu(adhoc_start->cap_info_bitmap); | ||
863 | tmp_cap &= ~WLAN_CAPABILITY_ESS; | ||
864 | tmp_cap |= WLAN_CAPABILITY_IBSS; | ||
865 | |||
866 | /* Set up privacy in bss_desc */ | ||
867 | if (priv->sec_info.encryption_mode) { | ||
868 | /* Ad-Hoc capability privacy on */ | ||
869 | dev_dbg(adapter->dev, | ||
870 | "info: ADHOC_S_CMD: wep_status set privacy to WEP\n"); | ||
871 | bss_desc->privacy = MWIFIEX_802_11_PRIV_FILTER_8021X_WEP; | ||
872 | tmp_cap |= WLAN_CAPABILITY_PRIVACY; | ||
873 | } else { | ||
874 | dev_dbg(adapter->dev, "info: ADHOC_S_CMD: wep_status NOT set," | ||
875 | " setting privacy to ACCEPT ALL\n"); | ||
876 | bss_desc->privacy = MWIFIEX_802_11_PRIV_FILTER_ACCEPT_ALL; | ||
877 | } | ||
878 | |||
879 | memset(adhoc_start->DataRate, 0, sizeof(adhoc_start->DataRate)); | ||
880 | mwifiex_get_active_data_rates(priv, adhoc_start->DataRate); | ||
881 | if ((adapter->adhoc_start_band & BAND_G) && | ||
882 | (priv->curr_pkt_filter & HostCmd_ACT_MAC_ADHOC_G_PROTECTION_ON)) { | ||
883 | if (mwifiex_send_cmd_async(priv, HostCmd_CMD_MAC_CONTROL, | ||
884 | HostCmd_ACT_GEN_SET, 0, | ||
885 | &priv->curr_pkt_filter)) { | ||
886 | dev_err(adapter->dev, | ||
887 | "ADHOC_S_CMD: G Protection config failed\n"); | ||
888 | return -1; | ||
889 | } | ||
890 | } | ||
891 | /* Find the last non zero */ | ||
892 | for (i = 0; i < sizeof(adhoc_start->DataRate) && | ||
893 | adhoc_start->DataRate[i]; | ||
894 | i++) | ||
895 | ; | ||
896 | |||
897 | priv->curr_bss_params.num_of_rates = i; | ||
898 | |||
899 | /* Copy the ad-hoc creating rates into Current BSS rate structure */ | ||
900 | memcpy(&priv->curr_bss_params.data_rates, | ||
901 | &adhoc_start->DataRate, priv->curr_bss_params.num_of_rates); | ||
902 | |||
903 | dev_dbg(adapter->dev, "info: ADHOC_S_CMD: rates=%02x %02x %02x %02x\n", | ||
904 | adhoc_start->DataRate[0], adhoc_start->DataRate[1], | ||
905 | adhoc_start->DataRate[2], adhoc_start->DataRate[3]); | ||
906 | |||
907 | dev_dbg(adapter->dev, "info: ADHOC_S_CMD: AD-HOC Start command is ready\n"); | ||
908 | |||
909 | if (IS_SUPPORT_MULTI_BANDS(adapter)) { | ||
910 | /* Append a channel TLV */ | ||
911 | chan_tlv = (struct mwifiex_ie_types_chan_list_param_set *) pos; | ||
912 | chan_tlv->header.type = cpu_to_le16(TLV_TYPE_CHANLIST); | ||
913 | chan_tlv->header.len = | ||
914 | cpu_to_le16(sizeof(struct mwifiex_chan_scan_param_set)); | ||
915 | |||
916 | memset(chan_tlv->chan_scan_param, 0x00, | ||
917 | sizeof(struct mwifiex_chan_scan_param_set)); | ||
918 | chan_tlv->chan_scan_param[0].chan_number = | ||
919 | (u8) priv->curr_bss_params.bss_descriptor.channel; | ||
920 | |||
921 | dev_dbg(adapter->dev, "info: ADHOC_S_CMD: TLV Chan = %d\n", | ||
922 | chan_tlv->chan_scan_param[0].chan_number); | ||
923 | |||
924 | chan_tlv->chan_scan_param[0].radio_type | ||
925 | = mwifiex_band_to_radio_type(priv->curr_bss_params.band); | ||
926 | if (adapter->adhoc_start_band & BAND_GN | ||
927 | || adapter->adhoc_start_band & BAND_AN) { | ||
928 | if (adapter->chan_offset == SEC_CHANNEL_ABOVE) | ||
929 | chan_tlv->chan_scan_param[0].radio_type |= | ||
930 | SECOND_CHANNEL_ABOVE; | ||
931 | else if (adapter->chan_offset == SEC_CHANNEL_BELOW) | ||
932 | chan_tlv->chan_scan_param[0].radio_type |= | ||
933 | SECOND_CHANNEL_BELOW; | ||
934 | } | ||
935 | dev_dbg(adapter->dev, "info: ADHOC_S_CMD: TLV Band = %d\n", | ||
936 | chan_tlv->chan_scan_param[0].radio_type); | ||
937 | pos += sizeof(chan_tlv->header) + | ||
938 | sizeof(struct mwifiex_chan_scan_param_set); | ||
939 | cmd_append_size += | ||
940 | sizeof(chan_tlv->header) + | ||
941 | sizeof(struct mwifiex_chan_scan_param_set); | ||
942 | } | ||
943 | |||
944 | /* Append vendor specific IE TLV */ | ||
945 | cmd_append_size += mwifiex_cmd_append_vsie_tlv(priv, | ||
946 | MWIFIEX_VSIE_MASK_ADHOC, &pos); | ||
947 | |||
948 | if (priv->sec_info.wpa_enabled) { | ||
949 | rsn_ie_len = mwifiex_append_rsn_ie_wpa_wpa2(priv, &pos); | ||
950 | if (rsn_ie_len == -1) | ||
951 | return -1; | ||
952 | cmd_append_size += rsn_ie_len; | ||
953 | } | ||
954 | |||
955 | if (adapter->adhoc_11n_enabled) { | ||
956 | { | ||
957 | ht_cap = (struct mwifiex_ie_types_htcap *) pos; | ||
958 | memset(ht_cap, 0, | ||
959 | sizeof(struct mwifiex_ie_types_htcap)); | ||
960 | ht_cap->header.type = | ||
961 | cpu_to_le16(WLAN_EID_HT_CAPABILITY); | ||
962 | ht_cap->header.len = | ||
963 | cpu_to_le16(sizeof(struct ieee80211_ht_cap)); | ||
964 | ht_cap_info = le16_to_cpu(ht_cap->ht_cap.cap_info); | ||
965 | |||
966 | ht_cap_info |= IEEE80211_HT_CAP_SGI_20; | ||
967 | if (adapter->chan_offset) { | ||
968 | ht_cap_info |= IEEE80211_HT_CAP_SGI_40; | ||
969 | ht_cap_info |= IEEE80211_HT_CAP_DSSSCCK40; | ||
970 | ht_cap_info |= IEEE80211_HT_CAP_SUP_WIDTH_20_40; | ||
971 | SETHT_MCS32(ht_cap->ht_cap.mcs.rx_mask); | ||
972 | } | ||
973 | |||
974 | ht_cap->ht_cap.ampdu_params_info | ||
975 | = IEEE80211_HT_MAX_AMPDU_64K; | ||
976 | ht_cap->ht_cap.mcs.rx_mask[0] = 0xff; | ||
977 | pos += sizeof(struct mwifiex_ie_types_htcap); | ||
978 | cmd_append_size += | ||
979 | sizeof(struct mwifiex_ie_types_htcap); | ||
980 | } | ||
981 | { | ||
982 | ht_info = (struct mwifiex_ie_types_htinfo *) pos; | ||
983 | memset(ht_info, 0, | ||
984 | sizeof(struct mwifiex_ie_types_htinfo)); | ||
985 | ht_info->header.type = | ||
986 | cpu_to_le16(WLAN_EID_HT_INFORMATION); | ||
987 | ht_info->header.len = | ||
988 | cpu_to_le16(sizeof(struct ieee80211_ht_info)); | ||
989 | ht_info->ht_info.control_chan = | ||
990 | (u8) priv->curr_bss_params.bss_descriptor. | ||
991 | channel; | ||
992 | if (adapter->chan_offset) { | ||
993 | ht_info->ht_info.ht_param = | ||
994 | adapter->chan_offset; | ||
995 | ht_info->ht_info.ht_param |= | ||
996 | IEEE80211_HT_PARAM_CHAN_WIDTH_ANY; | ||
997 | } | ||
998 | ht_info->ht_info.operation_mode = | ||
999 | cpu_to_le16(IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT); | ||
1000 | ht_info->ht_info.basic_set[0] = 0xff; | ||
1001 | pos += sizeof(struct mwifiex_ie_types_htinfo); | ||
1002 | cmd_append_size += | ||
1003 | sizeof(struct mwifiex_ie_types_htinfo); | ||
1004 | } | ||
1005 | } | ||
1006 | |||
1007 | cmd->size = cpu_to_le16((u16) | ||
1008 | (sizeof(struct host_cmd_ds_802_11_ad_hoc_start) | ||
1009 | + S_DS_GEN + cmd_append_size)); | ||
1010 | |||
1011 | if (adapter->adhoc_start_band == BAND_B) | ||
1012 | tmp_cap &= ~WLAN_CAPABILITY_SHORT_SLOT_TIME; | ||
1013 | else | ||
1014 | tmp_cap |= WLAN_CAPABILITY_SHORT_SLOT_TIME; | ||
1015 | |||
1016 | adhoc_start->cap_info_bitmap = cpu_to_le16(tmp_cap); | ||
1017 | |||
1018 | return 0; | ||
1019 | } | ||
1020 | |||
1021 | /* | ||
1022 | * This function prepares command for ad-hoc join. | ||
1023 | * | ||
1024 | * Most of the parameters are set up by copying from the target BSS descriptor | ||
1025 | * from the scan response. | ||
1026 | * | ||
1027 | * In addition, the following TLVs are added - | ||
1028 | * - Channel TLV | ||
1029 | * - Vendor specific IE | ||
1030 | * - WPA/WPA2 IE | ||
1031 | * - 11n IE | ||
1032 | * | ||
1033 | * Preparation also includes - | ||
1034 | * - Setting command ID and proper size | ||
1035 | * - Ensuring correct endian-ness | ||
1036 | */ | ||
1037 | int | ||
1038 | mwifiex_cmd_802_11_ad_hoc_join(struct mwifiex_private *priv, | ||
1039 | struct host_cmd_ds_command *cmd, void *data_buf) | ||
1040 | { | ||
1041 | int rsn_ie_len = 0; | ||
1042 | struct host_cmd_ds_802_11_ad_hoc_join *adhoc_join = | ||
1043 | &cmd->params.adhoc_join; | ||
1044 | struct mwifiex_bssdescriptor *bss_desc = | ||
1045 | (struct mwifiex_bssdescriptor *) data_buf; | ||
1046 | struct mwifiex_ie_types_chan_list_param_set *chan_tlv; | ||
1047 | u32 cmd_append_size = 0; | ||
1048 | u16 tmp_cap; | ||
1049 | u32 i, rates_size = 0; | ||
1050 | u16 curr_pkt_filter; | ||
1051 | u8 *pos = | ||
1052 | (u8 *) adhoc_join + | ||
1053 | sizeof(struct host_cmd_ds_802_11_ad_hoc_join); | ||
1054 | |||
1055 | /* Use G protection */ | ||
1056 | #define USE_G_PROTECTION 0x02 | ||
1057 | if (bss_desc->erp_flags & USE_G_PROTECTION) { | ||
1058 | curr_pkt_filter = | ||
1059 | priv-> | ||
1060 | curr_pkt_filter | HostCmd_ACT_MAC_ADHOC_G_PROTECTION_ON; | ||
1061 | |||
1062 | if (mwifiex_send_cmd_async(priv, HostCmd_CMD_MAC_CONTROL, | ||
1063 | HostCmd_ACT_GEN_SET, 0, | ||
1064 | &curr_pkt_filter)) { | ||
1065 | dev_err(priv->adapter->dev, | ||
1066 | "ADHOC_J_CMD: G Protection config failed\n"); | ||
1067 | return -1; | ||
1068 | } | ||
1069 | } | ||
1070 | |||
1071 | priv->attempted_bss_desc = bss_desc; | ||
1072 | |||
1073 | cmd->command = cpu_to_le16(HostCmd_CMD_802_11_AD_HOC_JOIN); | ||
1074 | |||
1075 | adhoc_join->bss_descriptor.bss_mode = HostCmd_BSS_MODE_IBSS; | ||
1076 | |||
1077 | adhoc_join->bss_descriptor.beacon_period | ||
1078 | = cpu_to_le16(bss_desc->beacon_period); | ||
1079 | |||
1080 | memcpy(&adhoc_join->bss_descriptor.bssid, | ||
1081 | &bss_desc->mac_address, ETH_ALEN); | ||
1082 | |||
1083 | memcpy(&adhoc_join->bss_descriptor.ssid, | ||
1084 | &bss_desc->ssid.ssid, bss_desc->ssid.ssid_len); | ||
1085 | |||
1086 | memcpy(&adhoc_join->bss_descriptor.phy_param_set, | ||
1087 | &bss_desc->phy_param_set, | ||
1088 | sizeof(union ieee_types_phy_param_set)); | ||
1089 | |||
1090 | memcpy(&adhoc_join->bss_descriptor.ss_param_set, | ||
1091 | &bss_desc->ss_param_set, sizeof(union ieee_types_ss_param_set)); | ||
1092 | |||
1093 | tmp_cap = bss_desc->cap_info_bitmap; | ||
1094 | |||
1095 | tmp_cap &= CAPINFO_MASK; | ||
1096 | |||
1097 | dev_dbg(priv->adapter->dev, "info: ADHOC_J_CMD: tmp_cap=%4X" | ||
1098 | " CAPINFO_MASK=%4lX\n", tmp_cap, CAPINFO_MASK); | ||
1099 | |||
1100 | /* Information on BSSID descriptor passed to FW */ | ||
1101 | dev_dbg(priv->adapter->dev, "info: ADHOC_J_CMD: BSSID = %pM, SSID = %s\n", | ||
1102 | adhoc_join->bss_descriptor.bssid, | ||
1103 | adhoc_join->bss_descriptor.ssid); | ||
1104 | |||
1105 | for (i = 0; bss_desc->supported_rates[i] && | ||
1106 | i < MWIFIEX_SUPPORTED_RATES; | ||
1107 | i++) | ||
1108 | ; | ||
1109 | rates_size = i; | ||
1110 | |||
1111 | /* Copy Data Rates from the Rates recorded in scan response */ | ||
1112 | memset(adhoc_join->bss_descriptor.data_rates, 0, | ||
1113 | sizeof(adhoc_join->bss_descriptor.data_rates)); | ||
1114 | memcpy(adhoc_join->bss_descriptor.data_rates, | ||
1115 | bss_desc->supported_rates, rates_size); | ||
1116 | |||
1117 | /* Copy the adhoc join rates into Current BSS state structure */ | ||
1118 | priv->curr_bss_params.num_of_rates = rates_size; | ||
1119 | memcpy(&priv->curr_bss_params.data_rates, bss_desc->supported_rates, | ||
1120 | rates_size); | ||
1121 | |||
1122 | /* Copy the channel information */ | ||
1123 | priv->curr_bss_params.bss_descriptor.channel = bss_desc->channel; | ||
1124 | priv->curr_bss_params.band = (u8) bss_desc->bss_band; | ||
1125 | |||
1126 | if (priv->sec_info.wep_status == MWIFIEX_802_11_WEP_ENABLED | ||
1127 | || priv->sec_info.wpa_enabled) | ||
1128 | tmp_cap |= WLAN_CAPABILITY_PRIVACY; | ||
1129 | |||
1130 | if (IS_SUPPORT_MULTI_BANDS(priv->adapter)) { | ||
1131 | /* Append a channel TLV */ | ||
1132 | chan_tlv = (struct mwifiex_ie_types_chan_list_param_set *) pos; | ||
1133 | chan_tlv->header.type = cpu_to_le16(TLV_TYPE_CHANLIST); | ||
1134 | chan_tlv->header.len = | ||
1135 | cpu_to_le16(sizeof(struct mwifiex_chan_scan_param_set)); | ||
1136 | |||
1137 | memset(chan_tlv->chan_scan_param, 0x00, | ||
1138 | sizeof(struct mwifiex_chan_scan_param_set)); | ||
1139 | chan_tlv->chan_scan_param[0].chan_number = | ||
1140 | (bss_desc->phy_param_set.ds_param_set.current_chan); | ||
1141 | dev_dbg(priv->adapter->dev, "info: ADHOC_J_CMD: TLV Chan = %d\n", | ||
1142 | chan_tlv->chan_scan_param[0].chan_number); | ||
1143 | |||
1144 | chan_tlv->chan_scan_param[0].radio_type = | ||
1145 | mwifiex_band_to_radio_type((u8) bss_desc->bss_band); | ||
1146 | |||
1147 | dev_dbg(priv->adapter->dev, "info: ADHOC_J_CMD: TLV Band = %d\n", | ||
1148 | chan_tlv->chan_scan_param[0].radio_type); | ||
1149 | pos += sizeof(chan_tlv->header) + | ||
1150 | sizeof(struct mwifiex_chan_scan_param_set); | ||
1151 | cmd_append_size += sizeof(chan_tlv->header) + | ||
1152 | sizeof(struct mwifiex_chan_scan_param_set); | ||
1153 | } | ||
1154 | |||
1155 | if (priv->sec_info.wpa_enabled) | ||
1156 | rsn_ie_len = mwifiex_append_rsn_ie_wpa_wpa2(priv, &pos); | ||
1157 | if (rsn_ie_len == -1) | ||
1158 | return -1; | ||
1159 | cmd_append_size += rsn_ie_len; | ||
1160 | |||
1161 | if (ISSUPP_11NENABLED(priv->adapter->fw_cap_info)) | ||
1162 | cmd_append_size += mwifiex_cmd_append_11n_tlv(priv, | ||
1163 | bss_desc, &pos); | ||
1164 | |||
1165 | /* Append vendor specific IE TLV */ | ||
1166 | cmd_append_size += mwifiex_cmd_append_vsie_tlv(priv, | ||
1167 | MWIFIEX_VSIE_MASK_ADHOC, &pos); | ||
1168 | |||
1169 | cmd->size = cpu_to_le16((u16) | ||
1170 | (sizeof(struct host_cmd_ds_802_11_ad_hoc_join) | ||
1171 | + S_DS_GEN + cmd_append_size)); | ||
1172 | |||
1173 | adhoc_join->bss_descriptor.cap_info_bitmap = cpu_to_le16(tmp_cap); | ||
1174 | |||
1175 | return 0; | ||
1176 | } | ||
1177 | |||
1178 | /* | ||
1179 | * This function handles the command response of ad-hoc start and | ||
1180 | * ad-hoc join. | ||
1181 | * | ||
1182 | * The function generates a device-connected event to notify | ||
1183 | * the applications, in case of successful ad-hoc start/join, and | ||
1184 | * saves the beacon buffer. | ||
1185 | */ | ||
1186 | int mwifiex_ret_802_11_ad_hoc(struct mwifiex_private *priv, | ||
1187 | struct host_cmd_ds_command *resp) | ||
1188 | { | ||
1189 | int ret = 0; | ||
1190 | struct mwifiex_adapter *adapter = priv->adapter; | ||
1191 | struct host_cmd_ds_802_11_ad_hoc_result *adhoc_result; | ||
1192 | struct mwifiex_bssdescriptor *bss_desc; | ||
1193 | |||
1194 | adhoc_result = &resp->params.adhoc_result; | ||
1195 | |||
1196 | bss_desc = priv->attempted_bss_desc; | ||
1197 | |||
1198 | /* Join result code 0 --> SUCCESS */ | ||
1199 | if (le16_to_cpu(resp->result)) { | ||
1200 | dev_err(priv->adapter->dev, "ADHOC_RESP: failed\n"); | ||
1201 | if (priv->media_connected) | ||
1202 | mwifiex_reset_connect_state(priv); | ||
1203 | |||
1204 | memset(&priv->curr_bss_params.bss_descriptor, | ||
1205 | 0x00, sizeof(struct mwifiex_bssdescriptor)); | ||
1206 | |||
1207 | ret = -1; | ||
1208 | goto done; | ||
1209 | } | ||
1210 | |||
1211 | /* Send a Media Connected event, according to the Spec */ | ||
1212 | priv->media_connected = true; | ||
1213 | |||
1214 | if (le16_to_cpu(resp->command) == HostCmd_CMD_802_11_AD_HOC_START) { | ||
1215 | dev_dbg(priv->adapter->dev, "info: ADHOC_S_RESP %s\n", | ||
1216 | bss_desc->ssid.ssid); | ||
1217 | |||
1218 | /* Update the created network descriptor with the new BSSID */ | ||
1219 | memcpy(bss_desc->mac_address, | ||
1220 | adhoc_result->bssid, ETH_ALEN); | ||
1221 | |||
1222 | priv->adhoc_state = ADHOC_STARTED; | ||
1223 | } else { | ||
1224 | /* | ||
1225 | * Now the join cmd should be successful. | ||
1226 | * If BSSID has changed use SSID to compare instead of BSSID | ||
1227 | */ | ||
1228 | dev_dbg(priv->adapter->dev, "info: ADHOC_J_RESP %s\n", | ||
1229 | bss_desc->ssid.ssid); | ||
1230 | |||
1231 | /* | ||
1232 | * Make a copy of current BSSID descriptor, only needed for | ||
1233 | * join since the current descriptor is already being used | ||
1234 | * for adhoc start | ||
1235 | */ | ||
1236 | memcpy(&priv->curr_bss_params.bss_descriptor, | ||
1237 | bss_desc, sizeof(struct mwifiex_bssdescriptor)); | ||
1238 | |||
1239 | priv->adhoc_state = ADHOC_JOINED; | ||
1240 | } | ||
1241 | |||
1242 | dev_dbg(priv->adapter->dev, "info: ADHOC_RESP: channel = %d\n", | ||
1243 | priv->adhoc_channel); | ||
1244 | dev_dbg(priv->adapter->dev, "info: ADHOC_RESP: BSSID = %pM\n", | ||
1245 | priv->curr_bss_params.bss_descriptor.mac_address); | ||
1246 | |||
1247 | if (!netif_carrier_ok(priv->netdev)) | ||
1248 | netif_carrier_on(priv->netdev); | ||
1249 | if (netif_queue_stopped(priv->netdev)) | ||
1250 | netif_wake_queue(priv->netdev); | ||
1251 | |||
1252 | mwifiex_save_curr_bcn(priv); | ||
1253 | |||
1254 | done: | ||
1255 | /* Need to indicate IOCTL complete */ | ||
1256 | if (adapter->curr_cmd->wait_q_enabled) { | ||
1257 | if (ret) | ||
1258 | adapter->cmd_wait_q.status = -1; | ||
1259 | else | ||
1260 | adapter->cmd_wait_q.status = 0; | ||
1261 | |||
1262 | } | ||
1263 | |||
1264 | return ret; | ||
1265 | } | ||
1266 | |||
1267 | /* | ||
1268 | * This function associates to a specific BSS discovered in a scan. | ||
1269 | * | ||
1270 | * It clears any past association response stored for application | ||
1271 | * retrieval and calls the command preparation routine to send the | ||
1272 | * command to firmware. | ||
1273 | */ | ||
1274 | int mwifiex_associate(struct mwifiex_private *priv, | ||
1275 | struct mwifiex_bssdescriptor *bss_desc) | ||
1276 | { | ||
1277 | u8 current_bssid[ETH_ALEN]; | ||
1278 | |||
1279 | /* Return error if the adapter or table entry is not marked as infra */ | ||
1280 | if ((priv->bss_mode != NL80211_IFTYPE_STATION) || | ||
1281 | (bss_desc->bss_mode != NL80211_IFTYPE_STATION)) | ||
1282 | return -1; | ||
1283 | |||
1284 | memcpy(¤t_bssid, | ||
1285 | &priv->curr_bss_params.bss_descriptor.mac_address, | ||
1286 | sizeof(current_bssid)); | ||
1287 | |||
1288 | /* Clear any past association response stored for application | ||
1289 | retrieval */ | ||
1290 | priv->assoc_rsp_size = 0; | ||
1291 | |||
1292 | return mwifiex_send_cmd_sync(priv, HostCmd_CMD_802_11_ASSOCIATE, | ||
1293 | HostCmd_ACT_GEN_SET, 0, bss_desc); | ||
1294 | } | ||
1295 | |||
1296 | /* | ||
1297 | * This function starts an ad-hoc network. | ||
1298 | * | ||
1299 | * It calls the command preparation routine to send the command to firmware. | ||
1300 | */ | ||
1301 | int | ||
1302 | mwifiex_adhoc_start(struct mwifiex_private *priv, | ||
1303 | struct mwifiex_802_11_ssid *adhoc_ssid) | ||
1304 | { | ||
1305 | dev_dbg(priv->adapter->dev, "info: Adhoc Channel = %d\n", | ||
1306 | priv->adhoc_channel); | ||
1307 | dev_dbg(priv->adapter->dev, "info: curr_bss_params.channel = %d\n", | ||
1308 | priv->curr_bss_params.bss_descriptor.channel); | ||
1309 | dev_dbg(priv->adapter->dev, "info: curr_bss_params.band = %d\n", | ||
1310 | priv->curr_bss_params.band); | ||
1311 | |||
1312 | return mwifiex_send_cmd_sync(priv, HostCmd_CMD_802_11_AD_HOC_START, | ||
1313 | HostCmd_ACT_GEN_SET, 0, adhoc_ssid); | ||
1314 | } | ||
1315 | |||
1316 | /* | ||
1317 | * This function joins an ad-hoc network found in a previous scan. | ||
1318 | * | ||
1319 | * It calls the command preparation routine to send the command to firmware, | ||
1320 | * if already not connected to the requested SSID. | ||
1321 | */ | ||
1322 | int mwifiex_adhoc_join(struct mwifiex_private *priv, | ||
1323 | struct mwifiex_bssdescriptor *bss_desc) | ||
1324 | { | ||
1325 | dev_dbg(priv->adapter->dev, "info: adhoc join: curr_bss ssid =%s\n", | ||
1326 | priv->curr_bss_params.bss_descriptor.ssid.ssid); | ||
1327 | dev_dbg(priv->adapter->dev, "info: adhoc join: curr_bss ssid_len =%u\n", | ||
1328 | priv->curr_bss_params.bss_descriptor.ssid.ssid_len); | ||
1329 | dev_dbg(priv->adapter->dev, "info: adhoc join: ssid =%s\n", | ||
1330 | bss_desc->ssid.ssid); | ||
1331 | dev_dbg(priv->adapter->dev, "info: adhoc join: ssid_len =%u\n", | ||
1332 | bss_desc->ssid.ssid_len); | ||
1333 | |||
1334 | /* Check if the requested SSID is already joined */ | ||
1335 | if (priv->curr_bss_params.bss_descriptor.ssid.ssid_len && | ||
1336 | !mwifiex_ssid_cmp(&bss_desc->ssid, | ||
1337 | &priv->curr_bss_params.bss_descriptor.ssid) && | ||
1338 | (priv->curr_bss_params.bss_descriptor.bss_mode == | ||
1339 | NL80211_IFTYPE_ADHOC)) { | ||
1340 | dev_dbg(priv->adapter->dev, "info: ADHOC_J_CMD: new ad-hoc SSID" | ||
1341 | " is the same as current; not attempting to re-join\n"); | ||
1342 | return -1; | ||
1343 | } | ||
1344 | |||
1345 | dev_dbg(priv->adapter->dev, "info: curr_bss_params.channel = %d\n", | ||
1346 | priv->curr_bss_params.bss_descriptor.channel); | ||
1347 | dev_dbg(priv->adapter->dev, "info: curr_bss_params.band = %c\n", | ||
1348 | priv->curr_bss_params.band); | ||
1349 | |||
1350 | return mwifiex_send_cmd_sync(priv, HostCmd_CMD_802_11_AD_HOC_JOIN, | ||
1351 | HostCmd_ACT_GEN_SET, 0, bss_desc); | ||
1352 | } | ||
1353 | |||
1354 | /* | ||
1355 | * This function deauthenticates/disconnects from infra network by sending | ||
1356 | * deauthentication request. | ||
1357 | */ | ||
1358 | static int mwifiex_deauthenticate_infra(struct mwifiex_private *priv, u8 *mac) | ||
1359 | { | ||
1360 | u8 mac_address[ETH_ALEN]; | ||
1361 | int ret; | ||
1362 | u8 zero_mac[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 }; | ||
1363 | |||
1364 | if (mac) { | ||
1365 | if (!memcmp(mac, zero_mac, sizeof(zero_mac))) | ||
1366 | memcpy((u8 *) &mac_address, | ||
1367 | (u8 *) &priv->curr_bss_params.bss_descriptor. | ||
1368 | mac_address, ETH_ALEN); | ||
1369 | else | ||
1370 | memcpy((u8 *) &mac_address, (u8 *) mac, ETH_ALEN); | ||
1371 | } else { | ||
1372 | memcpy((u8 *) &mac_address, (u8 *) &priv->curr_bss_params. | ||
1373 | bss_descriptor.mac_address, ETH_ALEN); | ||
1374 | } | ||
1375 | |||
1376 | ret = mwifiex_send_cmd_sync(priv, HostCmd_CMD_802_11_DEAUTHENTICATE, | ||
1377 | HostCmd_ACT_GEN_SET, 0, &mac_address); | ||
1378 | |||
1379 | return ret; | ||
1380 | } | ||
1381 | |||
1382 | /* | ||
1383 | * This function deauthenticates/disconnects from a BSS. | ||
1384 | * | ||
1385 | * In case of infra made, it sends deauthentication request, and | ||
1386 | * in case of ad-hoc mode, a stop network request is sent to the firmware. | ||
1387 | */ | ||
1388 | int mwifiex_deauthenticate(struct mwifiex_private *priv, u8 *mac) | ||
1389 | { | ||
1390 | int ret = 0; | ||
1391 | |||
1392 | if (priv->media_connected) { | ||
1393 | if (priv->bss_mode == NL80211_IFTYPE_STATION) { | ||
1394 | ret = mwifiex_deauthenticate_infra(priv, mac); | ||
1395 | } else if (priv->bss_mode == NL80211_IFTYPE_ADHOC) { | ||
1396 | ret = mwifiex_send_cmd_sync(priv, | ||
1397 | HostCmd_CMD_802_11_AD_HOC_STOP, | ||
1398 | HostCmd_ACT_GEN_SET, 0, NULL); | ||
1399 | } | ||
1400 | } | ||
1401 | |||
1402 | return ret; | ||
1403 | } | ||
1404 | EXPORT_SYMBOL_GPL(mwifiex_deauthenticate); | ||
1405 | |||
1406 | /* | ||
1407 | * This function converts band to radio type used in channel TLV. | ||
1408 | */ | ||
1409 | u8 | ||
1410 | mwifiex_band_to_radio_type(u8 band) | ||
1411 | { | ||
1412 | switch (band) { | ||
1413 | case BAND_A: | ||
1414 | case BAND_AN: | ||
1415 | case BAND_A | BAND_AN: | ||
1416 | return HostCmd_SCAN_RADIO_TYPE_A; | ||
1417 | case BAND_B: | ||
1418 | case BAND_G: | ||
1419 | case BAND_B | BAND_G: | ||
1420 | default: | ||
1421 | return HostCmd_SCAN_RADIO_TYPE_BG; | ||
1422 | } | ||
1423 | } | ||
diff --git a/drivers/net/wireless/mwifiex/main.c b/drivers/net/wireless/mwifiex/main.c new file mode 100644 index 000000000000..f0582259c935 --- /dev/null +++ b/drivers/net/wireless/mwifiex/main.c | |||
@@ -0,0 +1,1055 @@ | |||
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 = DRV_MODE_STA, | ||
44 | .intf_num = ARRAY_SIZE(mwifiex_bss_sta), | ||
45 | .bss_attr = mwifiex_bss_sta, | ||
46 | }, | ||
47 | }; | ||
48 | |||
49 | /* | ||
50 | * This function registers the device and performs all the necessary | ||
51 | * initializations. | ||
52 | * | ||
53 | * The following initialization operations are performed - | ||
54 | * - Allocate adapter structure | ||
55 | * - Save interface specific operations table in adapter | ||
56 | * - Call interface specific initialization routine | ||
57 | * - Allocate private structures | ||
58 | * - Set default adapter structure parameters | ||
59 | * - Initialize locks | ||
60 | * | ||
61 | * In case of any errors during inittialization, this function also ensures | ||
62 | * proper cleanup before exiting. | ||
63 | */ | ||
64 | static int mwifiex_register(void *card, struct mwifiex_if_ops *if_ops, | ||
65 | struct mwifiex_drv_mode *drv_mode_ptr) | ||
66 | { | ||
67 | struct mwifiex_adapter *adapter; | ||
68 | int i; | ||
69 | |||
70 | adapter = kzalloc(sizeof(struct mwifiex_adapter), GFP_KERNEL); | ||
71 | if (!adapter) | ||
72 | return -ENOMEM; | ||
73 | |||
74 | g_adapter = adapter; | ||
75 | adapter->card = card; | ||
76 | |||
77 | /* Save interface specific operations in adapter */ | ||
78 | memmove(&adapter->if_ops, if_ops, sizeof(struct mwifiex_if_ops)); | ||
79 | |||
80 | /* card specific initialization has been deferred until now .. */ | ||
81 | if (adapter->if_ops.init_if(adapter)) | ||
82 | goto error; | ||
83 | |||
84 | adapter->priv_num = 0; | ||
85 | for (i = 0; i < drv_mode_ptr->intf_num; i++) { | ||
86 | adapter->priv[i] = NULL; | ||
87 | |||
88 | if (!drv_mode_ptr->bss_attr[i].active) | ||
89 | continue; | ||
90 | |||
91 | /* Allocate memory for private structure */ | ||
92 | adapter->priv[i] = kzalloc(sizeof(struct mwifiex_private), | ||
93 | GFP_KERNEL); | ||
94 | if (!adapter->priv[i]) { | ||
95 | dev_err(adapter->dev, "%s: failed to alloc priv[%d]\n", | ||
96 | __func__, i); | ||
97 | goto error; | ||
98 | } | ||
99 | |||
100 | adapter->priv_num++; | ||
101 | adapter->priv[i]->adapter = adapter; | ||
102 | /* Save bss_type, frame_type & bss_priority */ | ||
103 | adapter->priv[i]->bss_type = drv_mode_ptr->bss_attr[i].bss_type; | ||
104 | adapter->priv[i]->frame_type = | ||
105 | drv_mode_ptr->bss_attr[i].frame_type; | ||
106 | adapter->priv[i]->bss_priority = | ||
107 | drv_mode_ptr->bss_attr[i].bss_priority; | ||
108 | |||
109 | if (drv_mode_ptr->bss_attr[i].bss_type == MWIFIEX_BSS_TYPE_STA) | ||
110 | adapter->priv[i]->bss_role = MWIFIEX_BSS_ROLE_STA; | ||
111 | else if (drv_mode_ptr->bss_attr[i].bss_type == | ||
112 | MWIFIEX_BSS_TYPE_UAP) | ||
113 | adapter->priv[i]->bss_role = MWIFIEX_BSS_ROLE_UAP; | ||
114 | |||
115 | /* Save bss_index & bss_num */ | ||
116 | adapter->priv[i]->bss_index = i; | ||
117 | adapter->priv[i]->bss_num = drv_mode_ptr->bss_attr[i].bss_num; | ||
118 | } | ||
119 | adapter->drv_mode = drv_mode_ptr; | ||
120 | |||
121 | if (mwifiex_init_lock_list(adapter)) | ||
122 | goto error; | ||
123 | |||
124 | init_timer(&adapter->cmd_timer); | ||
125 | adapter->cmd_timer.function = mwifiex_cmd_timeout_func; | ||
126 | adapter->cmd_timer.data = (unsigned long) adapter; | ||
127 | |||
128 | return 0; | ||
129 | |||
130 | error: | ||
131 | dev_dbg(adapter->dev, "info: leave mwifiex_register with error\n"); | ||
132 | |||
133 | mwifiex_free_lock_list(adapter); | ||
134 | for (i = 0; i < drv_mode_ptr->intf_num; i++) | ||
135 | kfree(adapter->priv[i]); | ||
136 | kfree(adapter); | ||
137 | |||
138 | return -1; | ||
139 | } | ||
140 | |||
141 | /* | ||
142 | * This function unregisters the device and performs all the necessary | ||
143 | * cleanups. | ||
144 | * | ||
145 | * The following cleanup operations are performed - | ||
146 | * - Free the timers | ||
147 | * - Free beacon buffers | ||
148 | * - Free private structures | ||
149 | * - Free adapter structure | ||
150 | */ | ||
151 | static int mwifiex_unregister(struct mwifiex_adapter *adapter) | ||
152 | { | ||
153 | s32 i; | ||
154 | |||
155 | del_timer(&adapter->cmd_timer); | ||
156 | |||
157 | /* Free private structures */ | ||
158 | for (i = 0; i < adapter->priv_num; i++) { | ||
159 | if (adapter->priv[i]) { | ||
160 | mwifiex_free_curr_bcn(adapter->priv[i]); | ||
161 | kfree(adapter->priv[i]); | ||
162 | } | ||
163 | } | ||
164 | |||
165 | kfree(adapter); | ||
166 | return 0; | ||
167 | } | ||
168 | |||
169 | /* | ||
170 | * The main process. | ||
171 | * | ||
172 | * This function is the main procedure of the driver and handles various driver | ||
173 | * operations. It runs in a loop and provides the core functionalities. | ||
174 | * | ||
175 | * The main responsibilities of this function are - | ||
176 | * - Ensure concurrency control | ||
177 | * - Handle pending interrupts and call interrupt handlers | ||
178 | * - Wake up the card if required | ||
179 | * - Handle command responses and call response handlers | ||
180 | * - Handle events and call event handlers | ||
181 | * - Execute pending commands | ||
182 | * - Transmit pending data packets | ||
183 | */ | ||
184 | int mwifiex_main_process(struct mwifiex_adapter *adapter) | ||
185 | { | ||
186 | int ret = 0; | ||
187 | unsigned long flags; | ||
188 | |||
189 | spin_lock_irqsave(&adapter->main_proc_lock, flags); | ||
190 | |||
191 | /* Check if already processing */ | ||
192 | if (adapter->mwifiex_processing) { | ||
193 | spin_unlock_irqrestore(&adapter->main_proc_lock, flags); | ||
194 | goto exit_main_proc; | ||
195 | } else { | ||
196 | adapter->mwifiex_processing = true; | ||
197 | spin_unlock_irqrestore(&adapter->main_proc_lock, flags); | ||
198 | } | ||
199 | process_start: | ||
200 | do { | ||
201 | if ((adapter->hw_status == MWIFIEX_HW_STATUS_CLOSING) || | ||
202 | (adapter->hw_status == MWIFIEX_HW_STATUS_NOT_READY)) | ||
203 | break; | ||
204 | |||
205 | /* Handle pending interrupt if any */ | ||
206 | if (adapter->int_status) { | ||
207 | if (adapter->hs_activated) | ||
208 | mwifiex_process_hs_config(adapter); | ||
209 | adapter->if_ops.process_int_status(adapter); | ||
210 | } | ||
211 | |||
212 | /* Need to wake up the card ? */ | ||
213 | if ((adapter->ps_state == PS_STATE_SLEEP) && | ||
214 | (adapter->pm_wakeup_card_req && | ||
215 | !adapter->pm_wakeup_fw_try) && | ||
216 | (is_command_pending(adapter) | ||
217 | || !mwifiex_wmm_lists_empty(adapter))) { | ||
218 | adapter->pm_wakeup_fw_try = true; | ||
219 | adapter->if_ops.wakeup(adapter); | ||
220 | continue; | ||
221 | } | ||
222 | if (IS_CARD_RX_RCVD(adapter)) { | ||
223 | adapter->pm_wakeup_fw_try = false; | ||
224 | if (adapter->ps_state == PS_STATE_SLEEP) | ||
225 | adapter->ps_state = PS_STATE_AWAKE; | ||
226 | } else { | ||
227 | /* We have tried to wakeup the card already */ | ||
228 | if (adapter->pm_wakeup_fw_try) | ||
229 | break; | ||
230 | if (adapter->ps_state != PS_STATE_AWAKE || | ||
231 | adapter->tx_lock_flag) | ||
232 | break; | ||
233 | |||
234 | if (adapter->scan_processing || adapter->data_sent | ||
235 | || mwifiex_wmm_lists_empty(adapter)) { | ||
236 | if (adapter->cmd_sent || adapter->curr_cmd | ||
237 | || (!is_command_pending(adapter))) | ||
238 | break; | ||
239 | } | ||
240 | } | ||
241 | |||
242 | /* Check for Cmd Resp */ | ||
243 | if (adapter->cmd_resp_received) { | ||
244 | adapter->cmd_resp_received = false; | ||
245 | mwifiex_process_cmdresp(adapter); | ||
246 | |||
247 | /* call mwifiex back when init_fw is done */ | ||
248 | if (adapter->hw_status == MWIFIEX_HW_STATUS_INIT_DONE) { | ||
249 | adapter->hw_status = MWIFIEX_HW_STATUS_READY; | ||
250 | mwifiex_init_fw_complete(adapter); | ||
251 | } | ||
252 | } | ||
253 | |||
254 | /* Check for event */ | ||
255 | if (adapter->event_received) { | ||
256 | adapter->event_received = false; | ||
257 | mwifiex_process_event(adapter); | ||
258 | } | ||
259 | |||
260 | /* Check if we need to confirm Sleep Request | ||
261 | received previously */ | ||
262 | if (adapter->ps_state == PS_STATE_PRE_SLEEP) { | ||
263 | if (!adapter->cmd_sent && !adapter->curr_cmd) | ||
264 | mwifiex_check_ps_cond(adapter); | ||
265 | } | ||
266 | |||
267 | /* * The ps_state may have been changed during processing of | ||
268 | * Sleep Request event. | ||
269 | */ | ||
270 | if ((adapter->ps_state == PS_STATE_SLEEP) | ||
271 | || (adapter->ps_state == PS_STATE_PRE_SLEEP) | ||
272 | || (adapter->ps_state == PS_STATE_SLEEP_CFM) | ||
273 | || adapter->tx_lock_flag) | ||
274 | continue; | ||
275 | |||
276 | if (!adapter->cmd_sent && !adapter->curr_cmd) { | ||
277 | if (mwifiex_exec_next_cmd(adapter) == -1) { | ||
278 | ret = -1; | ||
279 | break; | ||
280 | } | ||
281 | } | ||
282 | |||
283 | if (!adapter->scan_processing && !adapter->data_sent && | ||
284 | !mwifiex_wmm_lists_empty(adapter)) { | ||
285 | mwifiex_wmm_process_tx(adapter); | ||
286 | if (adapter->hs_activated) { | ||
287 | adapter->is_hs_configured = false; | ||
288 | mwifiex_hs_activated_event | ||
289 | (mwifiex_get_priv | ||
290 | (adapter, MWIFIEX_BSS_ROLE_ANY), | ||
291 | false); | ||
292 | } | ||
293 | } | ||
294 | |||
295 | if (adapter->delay_null_pkt && !adapter->cmd_sent && | ||
296 | !adapter->curr_cmd && !is_command_pending(adapter) | ||
297 | && mwifiex_wmm_lists_empty(adapter)) { | ||
298 | if (!mwifiex_send_null_packet | ||
299 | (mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_STA), | ||
300 | MWIFIEX_TxPD_POWER_MGMT_NULL_PACKET | | ||
301 | MWIFIEX_TxPD_POWER_MGMT_LAST_PACKET)) { | ||
302 | adapter->delay_null_pkt = false; | ||
303 | adapter->ps_state = PS_STATE_SLEEP; | ||
304 | } | ||
305 | break; | ||
306 | } | ||
307 | } while (true); | ||
308 | |||
309 | if ((adapter->int_status) || IS_CARD_RX_RCVD(adapter)) | ||
310 | goto process_start; | ||
311 | |||
312 | spin_lock_irqsave(&adapter->main_proc_lock, flags); | ||
313 | adapter->mwifiex_processing = false; | ||
314 | spin_unlock_irqrestore(&adapter->main_proc_lock, flags); | ||
315 | |||
316 | exit_main_proc: | ||
317 | if (adapter->hw_status == MWIFIEX_HW_STATUS_CLOSING) | ||
318 | mwifiex_shutdown_drv(adapter); | ||
319 | return ret; | ||
320 | } | ||
321 | |||
322 | /* | ||
323 | * This function initializes the software. | ||
324 | * | ||
325 | * The main work includes allocating and initializing the adapter structure | ||
326 | * and initializing the private structures. | ||
327 | */ | ||
328 | static int | ||
329 | mwifiex_init_sw(void *card, struct mwifiex_if_ops *if_ops) | ||
330 | { | ||
331 | int i; | ||
332 | struct mwifiex_drv_mode *drv_mode_ptr; | ||
333 | |||
334 | /* find mwifiex_drv_mode entry from mwifiex_drv_mode_tbl */ | ||
335 | drv_mode_ptr = NULL; | ||
336 | for (i = 0; i < ARRAY_SIZE(mwifiex_drv_mode_tbl); i++) { | ||
337 | if (mwifiex_drv_mode_tbl[i].drv_mode == drv_mode) { | ||
338 | drv_mode_ptr = &mwifiex_drv_mode_tbl[i]; | ||
339 | break; | ||
340 | } | ||
341 | } | ||
342 | |||
343 | if (!drv_mode_ptr) { | ||
344 | pr_err("invalid drv_mode=%d\n", drv_mode); | ||
345 | return -1; | ||
346 | } | ||
347 | |||
348 | if (mwifiex_register(card, if_ops, drv_mode_ptr)) | ||
349 | return -1; | ||
350 | |||
351 | return 0; | ||
352 | } | ||
353 | |||
354 | /* | ||
355 | * This function frees the adapter structure. | ||
356 | * | ||
357 | * Additionally, this closes the netlink socket, frees the timers | ||
358 | * and private structures. | ||
359 | */ | ||
360 | static void mwifiex_free_adapter(struct mwifiex_adapter *adapter) | ||
361 | { | ||
362 | if (!adapter) { | ||
363 | pr_err("%s: adapter is NULL\n", __func__); | ||
364 | return; | ||
365 | } | ||
366 | |||
367 | mwifiex_unregister(adapter); | ||
368 | pr_debug("info: %s: free adapter\n", __func__); | ||
369 | } | ||
370 | |||
371 | /* | ||
372 | * This function initializes the hardware and firmware. | ||
373 | * | ||
374 | * The main initialization steps followed are - | ||
375 | * - Download the correct firmware to card | ||
376 | * - Allocate and initialize the adapter structure | ||
377 | * - Initialize the private structures | ||
378 | * - Issue the init commands to firmware | ||
379 | */ | ||
380 | static int mwifiex_init_hw_fw(struct mwifiex_adapter *adapter) | ||
381 | { | ||
382 | int ret, err; | ||
383 | struct mwifiex_fw_image fw; | ||
384 | |||
385 | memset(&fw, 0, sizeof(struct mwifiex_fw_image)); | ||
386 | |||
387 | switch (adapter->revision_id) { | ||
388 | case SD8787_W0: | ||
389 | case SD8787_W1: | ||
390 | strcpy(fw_name, SD8787_W1_FW_NAME); | ||
391 | break; | ||
392 | case SD8787_A0: | ||
393 | case SD8787_A1: | ||
394 | strcpy(fw_name, SD8787_AX_FW_NAME); | ||
395 | break; | ||
396 | default: | ||
397 | break; | ||
398 | } | ||
399 | |||
400 | err = request_firmware(&adapter->firmware, fw_name, adapter->dev); | ||
401 | if (err < 0) { | ||
402 | dev_err(adapter->dev, "request_firmware() returned" | ||
403 | " error code %#x\n", err); | ||
404 | ret = -1; | ||
405 | goto done; | ||
406 | } | ||
407 | fw.fw_buf = (u8 *) adapter->firmware->data; | ||
408 | fw.fw_len = adapter->firmware->size; | ||
409 | |||
410 | ret = mwifiex_dnld_fw(adapter, &fw); | ||
411 | if (ret == -1) | ||
412 | goto done; | ||
413 | |||
414 | dev_notice(adapter->dev, "WLAN FW is active\n"); | ||
415 | |||
416 | adapter->init_wait_q_woken = false; | ||
417 | ret = mwifiex_init_fw(adapter); | ||
418 | if (ret == -1) { | ||
419 | goto done; | ||
420 | } else if (!ret) { | ||
421 | adapter->hw_status = MWIFIEX_HW_STATUS_READY; | ||
422 | goto done; | ||
423 | } | ||
424 | /* Wait for mwifiex_init to complete */ | ||
425 | wait_event_interruptible(adapter->init_wait_q, | ||
426 | adapter->init_wait_q_woken); | ||
427 | if (adapter->hw_status != MWIFIEX_HW_STATUS_READY) { | ||
428 | ret = -1; | ||
429 | goto done; | ||
430 | } | ||
431 | ret = 0; | ||
432 | |||
433 | done: | ||
434 | if (adapter->firmware) | ||
435 | release_firmware(adapter->firmware); | ||
436 | if (ret) | ||
437 | ret = -1; | ||
438 | return ret; | ||
439 | } | ||
440 | |||
441 | /* | ||
442 | * This function fills a driver buffer. | ||
443 | * | ||
444 | * The function associates a given SKB with the provided driver buffer | ||
445 | * and also updates some of the SKB parameters, including IP header, | ||
446 | * priority and timestamp. | ||
447 | */ | ||
448 | static void | ||
449 | mwifiex_fill_buffer(struct sk_buff *skb) | ||
450 | { | ||
451 | struct ethhdr *eth; | ||
452 | struct iphdr *iph; | ||
453 | struct timeval tv; | ||
454 | u8 tid = 0; | ||
455 | |||
456 | eth = (struct ethhdr *) skb->data; | ||
457 | switch (eth->h_proto) { | ||
458 | case __constant_htons(ETH_P_IP): | ||
459 | iph = ip_hdr(skb); | ||
460 | tid = IPTOS_PREC(iph->tos); | ||
461 | pr_debug("data: packet type ETH_P_IP: %04x, tid=%#x prio=%#x\n", | ||
462 | eth->h_proto, tid, skb->priority); | ||
463 | break; | ||
464 | case __constant_htons(ETH_P_ARP): | ||
465 | pr_debug("data: ARP packet: %04x\n", eth->h_proto); | ||
466 | default: | ||
467 | break; | ||
468 | } | ||
469 | /* Offset for TOS field in the IP header */ | ||
470 | #define IPTOS_OFFSET 5 | ||
471 | tid = (tid >> IPTOS_OFFSET); | ||
472 | skb->priority = tid; | ||
473 | /* Record the current time the packet was queued; used to | ||
474 | determine the amount of time the packet was queued in | ||
475 | the driver before it was sent to the firmware. | ||
476 | The delay is then sent along with the packet to the | ||
477 | firmware for aggregate delay calculation for stats and | ||
478 | MSDU lifetime expiry. | ||
479 | */ | ||
480 | do_gettimeofday(&tv); | ||
481 | skb->tstamp = timeval_to_ktime(tv); | ||
482 | } | ||
483 | |||
484 | /* | ||
485 | * CFG802.11 network device handler for open. | ||
486 | * | ||
487 | * Starts the data queue. | ||
488 | */ | ||
489 | static int | ||
490 | mwifiex_open(struct net_device *dev) | ||
491 | { | ||
492 | netif_start_queue(dev); | ||
493 | return 0; | ||
494 | } | ||
495 | |||
496 | /* | ||
497 | * CFG802.11 network device handler for close. | ||
498 | */ | ||
499 | static int | ||
500 | mwifiex_close(struct net_device *dev) | ||
501 | { | ||
502 | return 0; | ||
503 | } | ||
504 | |||
505 | /* | ||
506 | * CFG802.11 network device handler for data transmission. | ||
507 | */ | ||
508 | static int | ||
509 | mwifiex_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) | ||
510 | { | ||
511 | struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); | ||
512 | struct sk_buff *new_skb; | ||
513 | struct mwifiex_txinfo *tx_info; | ||
514 | |||
515 | dev_dbg(priv->adapter->dev, "data: %lu BSS(%d): Data <= kernel\n", | ||
516 | jiffies, priv->bss_index); | ||
517 | |||
518 | if (priv->adapter->surprise_removed) { | ||
519 | kfree_skb(skb); | ||
520 | priv->stats.tx_dropped++; | ||
521 | return 0; | ||
522 | } | ||
523 | if (!skb->len || (skb->len > ETH_FRAME_LEN)) { | ||
524 | dev_err(priv->adapter->dev, "Tx: bad skb len %d\n", skb->len); | ||
525 | kfree_skb(skb); | ||
526 | priv->stats.tx_dropped++; | ||
527 | return 0; | ||
528 | } | ||
529 | if (skb_headroom(skb) < MWIFIEX_MIN_DATA_HEADER_LEN) { | ||
530 | dev_dbg(priv->adapter->dev, | ||
531 | "data: Tx: insufficient skb headroom %d\n", | ||
532 | skb_headroom(skb)); | ||
533 | /* Insufficient skb headroom - allocate a new skb */ | ||
534 | new_skb = | ||
535 | skb_realloc_headroom(skb, MWIFIEX_MIN_DATA_HEADER_LEN); | ||
536 | if (unlikely(!new_skb)) { | ||
537 | dev_err(priv->adapter->dev, "Tx: cannot alloca new_skb\n"); | ||
538 | kfree_skb(skb); | ||
539 | priv->stats.tx_dropped++; | ||
540 | return 0; | ||
541 | } | ||
542 | kfree_skb(skb); | ||
543 | skb = new_skb; | ||
544 | dev_dbg(priv->adapter->dev, "info: new skb headroomd %d\n", | ||
545 | skb_headroom(skb)); | ||
546 | } | ||
547 | |||
548 | tx_info = MWIFIEX_SKB_TXCB(skb); | ||
549 | tx_info->bss_index = priv->bss_index; | ||
550 | mwifiex_fill_buffer(skb); | ||
551 | |||
552 | mwifiex_wmm_add_buf_txqueue(priv->adapter, skb); | ||
553 | atomic_inc(&priv->adapter->tx_pending); | ||
554 | |||
555 | if (atomic_read(&priv->adapter->tx_pending) >= MAX_TX_PENDING) { | ||
556 | netif_stop_queue(priv->netdev); | ||
557 | dev->trans_start = jiffies; | ||
558 | } | ||
559 | |||
560 | queue_work(priv->adapter->workqueue, &priv->adapter->main_work); | ||
561 | |||
562 | return 0; | ||
563 | } | ||
564 | |||
565 | /* | ||
566 | * CFG802.11 network device handler for setting MAC address. | ||
567 | */ | ||
568 | static int | ||
569 | mwifiex_set_mac_address(struct net_device *dev, void *addr) | ||
570 | { | ||
571 | struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); | ||
572 | struct sockaddr *hw_addr = (struct sockaddr *) addr; | ||
573 | int ret; | ||
574 | |||
575 | memcpy(priv->curr_addr, hw_addr->sa_data, ETH_ALEN); | ||
576 | |||
577 | /* Send request to firmware */ | ||
578 | ret = mwifiex_send_cmd_sync(priv, HostCmd_CMD_802_11_MAC_ADDRESS, | ||
579 | HostCmd_ACT_GEN_SET, 0, NULL); | ||
580 | |||
581 | if (!ret) | ||
582 | memcpy(priv->netdev->dev_addr, priv->curr_addr, ETH_ALEN); | ||
583 | else | ||
584 | dev_err(priv->adapter->dev, "set mac address failed: ret=%d" | ||
585 | "\n", ret); | ||
586 | |||
587 | memcpy(dev->dev_addr, priv->curr_addr, ETH_ALEN); | ||
588 | |||
589 | return ret; | ||
590 | } | ||
591 | |||
592 | /* | ||
593 | * CFG802.11 network device handler for setting multicast list. | ||
594 | */ | ||
595 | static void mwifiex_set_multicast_list(struct net_device *dev) | ||
596 | { | ||
597 | struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); | ||
598 | struct mwifiex_multicast_list mcast_list; | ||
599 | |||
600 | if (dev->flags & IFF_PROMISC) { | ||
601 | mcast_list.mode = MWIFIEX_PROMISC_MODE; | ||
602 | } else if (dev->flags & IFF_ALLMULTI || | ||
603 | netdev_mc_count(dev) > MWIFIEX_MAX_MULTICAST_LIST_SIZE) { | ||
604 | mcast_list.mode = MWIFIEX_ALL_MULTI_MODE; | ||
605 | } else { | ||
606 | mcast_list.mode = MWIFIEX_MULTICAST_MODE; | ||
607 | if (netdev_mc_count(dev)) | ||
608 | mcast_list.num_multicast_addr = | ||
609 | mwifiex_copy_mcast_addr(&mcast_list, dev); | ||
610 | } | ||
611 | mwifiex_request_set_multicast_list(priv, &mcast_list); | ||
612 | } | ||
613 | |||
614 | /* | ||
615 | * CFG802.11 network device handler for transmission timeout. | ||
616 | */ | ||
617 | static void | ||
618 | mwifiex_tx_timeout(struct net_device *dev) | ||
619 | { | ||
620 | struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); | ||
621 | |||
622 | dev_err(priv->adapter->dev, "%lu : Tx timeout, bss_index=%d\n", | ||
623 | jiffies, priv->bss_index); | ||
624 | dev->trans_start = jiffies; | ||
625 | priv->num_tx_timeout++; | ||
626 | } | ||
627 | |||
628 | /* | ||
629 | * CFG802.11 network device handler for statistics retrieval. | ||
630 | */ | ||
631 | static struct net_device_stats *mwifiex_get_stats(struct net_device *dev) | ||
632 | { | ||
633 | struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); | ||
634 | |||
635 | return &priv->stats; | ||
636 | } | ||
637 | |||
638 | /* Network device handlers */ | ||
639 | static const struct net_device_ops mwifiex_netdev_ops = { | ||
640 | .ndo_open = mwifiex_open, | ||
641 | .ndo_stop = mwifiex_close, | ||
642 | .ndo_start_xmit = mwifiex_hard_start_xmit, | ||
643 | .ndo_set_mac_address = mwifiex_set_mac_address, | ||
644 | .ndo_tx_timeout = mwifiex_tx_timeout, | ||
645 | .ndo_get_stats = mwifiex_get_stats, | ||
646 | .ndo_set_multicast_list = mwifiex_set_multicast_list, | ||
647 | }; | ||
648 | |||
649 | /* | ||
650 | * This function initializes the private structure parameters. | ||
651 | * | ||
652 | * The following wait queues are initialized - | ||
653 | * - IOCTL wait queue | ||
654 | * - Command wait queue | ||
655 | * - Statistics wait queue | ||
656 | * | ||
657 | * ...and the following default parameters are set - | ||
658 | * - Current key index : Set to 0 | ||
659 | * - Rate index : Set to auto | ||
660 | * - Media connected : Set to disconnected | ||
661 | * - Adhoc link sensed : Set to false | ||
662 | * - Nick name : Set to null | ||
663 | * - Number of Tx timeout : Set to 0 | ||
664 | * - Device address : Set to current address | ||
665 | * | ||
666 | * In addition, the CFG80211 work queue is also created. | ||
667 | */ | ||
668 | static void | ||
669 | mwifiex_init_priv_params(struct mwifiex_private *priv, struct net_device *dev) | ||
670 | { | ||
671 | dev->netdev_ops = &mwifiex_netdev_ops; | ||
672 | /* Initialize private structure */ | ||
673 | priv->current_key_index = 0; | ||
674 | priv->media_connected = false; | ||
675 | memset(&priv->nick_name, 0, sizeof(priv->nick_name)); | ||
676 | priv->num_tx_timeout = 0; | ||
677 | priv->workqueue = create_singlethread_workqueue("cfg80211_wq"); | ||
678 | INIT_WORK(&priv->cfg_workqueue, mwifiex_cfg80211_results); | ||
679 | memcpy(dev->dev_addr, priv->curr_addr, ETH_ALEN); | ||
680 | } | ||
681 | |||
682 | /* | ||
683 | * This function adds a new logical interface. | ||
684 | * | ||
685 | * It allocates, initializes and registers the interface by performing | ||
686 | * the following opearations - | ||
687 | * - Allocate a new net device structure | ||
688 | * - Assign device name | ||
689 | * - Register the new device with CFG80211 subsystem | ||
690 | * - Initialize semaphore and private structure | ||
691 | * - Register the new device with kernel | ||
692 | * - Create the complete debug FS structure if configured | ||
693 | */ | ||
694 | static struct mwifiex_private *mwifiex_add_interface( | ||
695 | struct mwifiex_adapter *adapter, | ||
696 | u8 bss_index, u8 bss_type) | ||
697 | { | ||
698 | struct net_device *dev; | ||
699 | struct mwifiex_private *priv; | ||
700 | void *mdev_priv; | ||
701 | |||
702 | dev = alloc_netdev_mq(sizeof(struct mwifiex_private *), "mlan%d", | ||
703 | ether_setup, 1); | ||
704 | if (!dev) { | ||
705 | dev_err(adapter->dev, "no memory available for netdevice\n"); | ||
706 | goto error; | ||
707 | } | ||
708 | |||
709 | if (mwifiex_register_cfg80211(dev, adapter->priv[bss_index]->curr_addr, | ||
710 | adapter->priv[bss_index]) != 0) { | ||
711 | dev_err(adapter->dev, "cannot register netdevice with cfg80211\n"); | ||
712 | goto error; | ||
713 | } | ||
714 | /* Save the priv pointer in netdev */ | ||
715 | priv = adapter->priv[bss_index]; | ||
716 | mdev_priv = netdev_priv(dev); | ||
717 | *((unsigned long *) mdev_priv) = (unsigned long) priv; | ||
718 | |||
719 | priv->netdev = dev; | ||
720 | |||
721 | sema_init(&priv->async_sem, 1); | ||
722 | priv->scan_pending_on_block = false; | ||
723 | |||
724 | mwifiex_init_priv_params(priv, dev); | ||
725 | |||
726 | SET_NETDEV_DEV(dev, adapter->dev); | ||
727 | |||
728 | /* Register network device */ | ||
729 | if (register_netdev(dev)) { | ||
730 | dev_err(adapter->dev, "cannot register virtual network device\n"); | ||
731 | goto error; | ||
732 | } | ||
733 | |||
734 | dev_dbg(adapter->dev, "info: %s: Marvell 802.11 Adapter\n", dev->name); | ||
735 | #ifdef CONFIG_DEBUG_FS | ||
736 | mwifiex_dev_debugfs_init(priv); | ||
737 | #endif | ||
738 | return priv; | ||
739 | error: | ||
740 | if (dev) | ||
741 | free_netdev(dev); | ||
742 | return NULL; | ||
743 | } | ||
744 | |||
745 | /* | ||
746 | * This function removes a logical interface. | ||
747 | * | ||
748 | * It deregisters, resets and frees the interface by performing | ||
749 | * the following operations - | ||
750 | * - Disconnect the device if connected, send wireless event to | ||
751 | * notify applications. | ||
752 | * - Remove the debug FS structure if configured | ||
753 | * - Unregister the device from kernel | ||
754 | * - Free the net device structure | ||
755 | * - Cancel all works and destroy work queue | ||
756 | * - Unregister and free the wireless device from CFG80211 subsystem | ||
757 | */ | ||
758 | static void | ||
759 | mwifiex_remove_interface(struct mwifiex_adapter *adapter, u8 bss_index) | ||
760 | { | ||
761 | struct net_device *dev; | ||
762 | struct mwifiex_private *priv = adapter->priv[bss_index]; | ||
763 | |||
764 | if (!priv) | ||
765 | return; | ||
766 | dev = priv->netdev; | ||
767 | |||
768 | if (priv->media_connected) | ||
769 | priv->media_connected = false; | ||
770 | |||
771 | #ifdef CONFIG_DEBUG_FS | ||
772 | mwifiex_dev_debugfs_remove(priv); | ||
773 | #endif | ||
774 | /* Last reference is our one */ | ||
775 | dev_dbg(adapter->dev, "info: %s: refcnt = %d\n", | ||
776 | dev->name, netdev_refcnt_read(dev)); | ||
777 | |||
778 | if (dev->reg_state == NETREG_REGISTERED) | ||
779 | unregister_netdev(dev); | ||
780 | |||
781 | /* Clear the priv in adapter */ | ||
782 | priv->netdev = NULL; | ||
783 | if (dev) | ||
784 | free_netdev(dev); | ||
785 | |||
786 | cancel_work_sync(&priv->cfg_workqueue); | ||
787 | flush_workqueue(priv->workqueue); | ||
788 | destroy_workqueue(priv->workqueue); | ||
789 | wiphy_unregister(priv->wdev->wiphy); | ||
790 | wiphy_free(priv->wdev->wiphy); | ||
791 | kfree(priv->wdev); | ||
792 | } | ||
793 | |||
794 | /* | ||
795 | * This function check if command is pending. | ||
796 | */ | ||
797 | int is_command_pending(struct mwifiex_adapter *adapter) | ||
798 | { | ||
799 | unsigned long flags; | ||
800 | int is_cmd_pend_q_empty; | ||
801 | |||
802 | spin_lock_irqsave(&adapter->cmd_pending_q_lock, flags); | ||
803 | is_cmd_pend_q_empty = list_empty(&adapter->cmd_pending_q); | ||
804 | spin_unlock_irqrestore(&adapter->cmd_pending_q_lock, flags); | ||
805 | |||
806 | return !is_cmd_pend_q_empty; | ||
807 | } | ||
808 | |||
809 | /* | ||
810 | * This function returns the correct private structure pointer based | ||
811 | * upon the BSS number. | ||
812 | */ | ||
813 | struct mwifiex_private * | ||
814 | mwifiex_bss_index_to_priv(struct mwifiex_adapter *adapter, u8 bss_index) | ||
815 | { | ||
816 | if (!adapter || (bss_index >= adapter->priv_num)) | ||
817 | return NULL; | ||
818 | return adapter->priv[bss_index]; | ||
819 | } | ||
820 | |||
821 | /* | ||
822 | * This is the main work queue function. | ||
823 | * | ||
824 | * It handles the main process, which in turn handles the complete | ||
825 | * driver operations. | ||
826 | */ | ||
827 | static void mwifiex_main_work_queue(struct work_struct *work) | ||
828 | { | ||
829 | struct mwifiex_adapter *adapter = | ||
830 | container_of(work, struct mwifiex_adapter, main_work); | ||
831 | |||
832 | if (adapter->surprise_removed) | ||
833 | return; | ||
834 | mwifiex_main_process(adapter); | ||
835 | } | ||
836 | |||
837 | /* | ||
838 | * This function cancels all works in the queue and destroys | ||
839 | * the main workqueue. | ||
840 | */ | ||
841 | static void | ||
842 | mwifiex_terminate_workqueue(struct mwifiex_adapter *adapter) | ||
843 | { | ||
844 | flush_workqueue(adapter->workqueue); | ||
845 | destroy_workqueue(adapter->workqueue); | ||
846 | adapter->workqueue = NULL; | ||
847 | } | ||
848 | |||
849 | /* | ||
850 | * This function adds the card. | ||
851 | * | ||
852 | * This function follows the following major steps to set up the device - | ||
853 | * - Initialize software. This includes probing the card, registering | ||
854 | * the interface operations table, and allocating/initializing the | ||
855 | * adapter structure | ||
856 | * - Set up the netlink socket | ||
857 | * - Create and start the main work queue | ||
858 | * - Register the device | ||
859 | * - Initialize firmware and hardware | ||
860 | * - Add logical interfaces | ||
861 | */ | ||
862 | int | ||
863 | mwifiex_add_card(void *card, struct semaphore *sem, | ||
864 | struct mwifiex_if_ops *if_ops) | ||
865 | { | ||
866 | int i; | ||
867 | struct mwifiex_adapter *adapter; | ||
868 | |||
869 | if (down_interruptible(sem)) | ||
870 | goto exit_sem_err; | ||
871 | |||
872 | if (mwifiex_init_sw(card, if_ops)) { | ||
873 | pr_err("%s: software init failed\n", __func__); | ||
874 | goto err_init_sw; | ||
875 | } | ||
876 | |||
877 | adapter = g_adapter; | ||
878 | |||
879 | adapter->hw_status = MWIFIEX_HW_STATUS_INITIALIZING; | ||
880 | adapter->surprise_removed = false; | ||
881 | init_waitqueue_head(&adapter->init_wait_q); | ||
882 | adapter->is_suspended = false; | ||
883 | adapter->hs_activated = false; | ||
884 | init_waitqueue_head(&adapter->hs_activate_wait_q); | ||
885 | adapter->cmd_wait_q_required = false; | ||
886 | init_waitqueue_head(&adapter->cmd_wait_q.wait); | ||
887 | adapter->cmd_wait_q.condition = false; | ||
888 | adapter->cmd_wait_q.status = 0; | ||
889 | |||
890 | adapter->workqueue = create_workqueue("MWIFIEX_WORK_QUEUE"); | ||
891 | if (!adapter->workqueue) | ||
892 | goto err_kmalloc; | ||
893 | |||
894 | INIT_WORK(&adapter->main_work, mwifiex_main_work_queue); | ||
895 | |||
896 | /* Register the device. Fill up the private data structure with relevant | ||
897 | information from the card and request for the required IRQ. */ | ||
898 | if (adapter->if_ops.register_dev(adapter)) { | ||
899 | pr_err("%s: failed to register mwifiex device\n", __func__); | ||
900 | goto err_registerdev; | ||
901 | } | ||
902 | |||
903 | if (mwifiex_init_hw_fw(adapter)) { | ||
904 | pr_err("%s: firmware init failed\n", __func__); | ||
905 | goto err_init_fw; | ||
906 | } | ||
907 | |||
908 | /* Add interfaces */ | ||
909 | for (i = 0; i < adapter->drv_mode->intf_num; i++) { | ||
910 | if (!mwifiex_add_interface(adapter, i, | ||
911 | adapter->drv_mode->bss_attr[i].bss_type)) { | ||
912 | goto err_add_intf; | ||
913 | } | ||
914 | } | ||
915 | |||
916 | up(sem); | ||
917 | |||
918 | return 0; | ||
919 | |||
920 | err_add_intf: | ||
921 | for (i = 0; i < adapter->priv_num; i++) | ||
922 | mwifiex_remove_interface(adapter, i); | ||
923 | err_init_fw: | ||
924 | pr_debug("info: %s: unregister device\n", __func__); | ||
925 | adapter->if_ops.unregister_dev(adapter); | ||
926 | err_registerdev: | ||
927 | adapter->surprise_removed = true; | ||
928 | mwifiex_terminate_workqueue(adapter); | ||
929 | err_kmalloc: | ||
930 | if ((adapter->hw_status == MWIFIEX_HW_STATUS_FW_READY) || | ||
931 | (adapter->hw_status == MWIFIEX_HW_STATUS_READY)) { | ||
932 | pr_debug("info: %s: shutdown mwifiex\n", __func__); | ||
933 | adapter->init_wait_q_woken = false; | ||
934 | |||
935 | if (mwifiex_shutdown_drv(adapter) == -EINPROGRESS) | ||
936 | wait_event_interruptible(adapter->init_wait_q, | ||
937 | adapter->init_wait_q_woken); | ||
938 | } | ||
939 | |||
940 | mwifiex_free_adapter(adapter); | ||
941 | |||
942 | err_init_sw: | ||
943 | up(sem); | ||
944 | |||
945 | exit_sem_err: | ||
946 | return -1; | ||
947 | } | ||
948 | EXPORT_SYMBOL_GPL(mwifiex_add_card); | ||
949 | |||
950 | /* | ||
951 | * This function removes the card. | ||
952 | * | ||
953 | * This function follows the following major steps to remove the device - | ||
954 | * - Stop data traffic | ||
955 | * - Shutdown firmware | ||
956 | * - Remove the logical interfaces | ||
957 | * - Terminate the work queue | ||
958 | * - Unregister the device | ||
959 | * - Free the adapter structure | ||
960 | */ | ||
961 | int mwifiex_remove_card(struct mwifiex_adapter *adapter, struct semaphore *sem) | ||
962 | { | ||
963 | struct mwifiex_private *priv = NULL; | ||
964 | int i; | ||
965 | |||
966 | if (down_interruptible(sem)) | ||
967 | goto exit_sem_err; | ||
968 | |||
969 | if (!adapter) | ||
970 | goto exit_remove; | ||
971 | |||
972 | adapter->surprise_removed = true; | ||
973 | |||
974 | /* Stop data */ | ||
975 | for (i = 0; i < adapter->priv_num; i++) { | ||
976 | priv = adapter->priv[i]; | ||
977 | if (priv) { | ||
978 | if (!netif_queue_stopped(priv->netdev)) | ||
979 | netif_stop_queue(priv->netdev); | ||
980 | if (netif_carrier_ok(priv->netdev)) | ||
981 | netif_carrier_off(priv->netdev); | ||
982 | } | ||
983 | } | ||
984 | |||
985 | dev_dbg(adapter->dev, "cmd: calling mwifiex_shutdown_drv...\n"); | ||
986 | adapter->init_wait_q_woken = false; | ||
987 | |||
988 | if (mwifiex_shutdown_drv(adapter) == -EINPROGRESS) | ||
989 | wait_event_interruptible(adapter->init_wait_q, | ||
990 | adapter->init_wait_q_woken); | ||
991 | dev_dbg(adapter->dev, "cmd: mwifiex_shutdown_drv done\n"); | ||
992 | if (atomic_read(&adapter->rx_pending) || | ||
993 | atomic_read(&adapter->tx_pending) || | ||
994 | atomic_read(&adapter->cmd_pending)) { | ||
995 | dev_err(adapter->dev, "rx_pending=%d, tx_pending=%d, " | ||
996 | "cmd_pending=%d\n", | ||
997 | atomic_read(&adapter->rx_pending), | ||
998 | atomic_read(&adapter->tx_pending), | ||
999 | atomic_read(&adapter->cmd_pending)); | ||
1000 | } | ||
1001 | |||
1002 | /* Remove interface */ | ||
1003 | for (i = 0; i < adapter->priv_num; i++) | ||
1004 | mwifiex_remove_interface(adapter, i); | ||
1005 | |||
1006 | mwifiex_terminate_workqueue(adapter); | ||
1007 | |||
1008 | /* Unregister device */ | ||
1009 | dev_dbg(adapter->dev, "info: unregister device\n"); | ||
1010 | adapter->if_ops.unregister_dev(adapter); | ||
1011 | /* Free adapter structure */ | ||
1012 | dev_dbg(adapter->dev, "info: free adapter\n"); | ||
1013 | mwifiex_free_adapter(adapter); | ||
1014 | |||
1015 | exit_remove: | ||
1016 | up(sem); | ||
1017 | exit_sem_err: | ||
1018 | return 0; | ||
1019 | } | ||
1020 | EXPORT_SYMBOL_GPL(mwifiex_remove_card); | ||
1021 | |||
1022 | /* | ||
1023 | * This function initializes the module. | ||
1024 | * | ||
1025 | * The debug FS is also initialized if configured. | ||
1026 | */ | ||
1027 | static int | ||
1028 | mwifiex_init_module(void) | ||
1029 | { | ||
1030 | #ifdef CONFIG_DEBUG_FS | ||
1031 | mwifiex_debugfs_init(); | ||
1032 | #endif | ||
1033 | return 0; | ||
1034 | } | ||
1035 | |||
1036 | /* | ||
1037 | * This function cleans up the module. | ||
1038 | * | ||
1039 | * The debug FS is removed if available. | ||
1040 | */ | ||
1041 | static void | ||
1042 | mwifiex_cleanup_module(void) | ||
1043 | { | ||
1044 | #ifdef CONFIG_DEBUG_FS | ||
1045 | mwifiex_debugfs_remove(); | ||
1046 | #endif | ||
1047 | } | ||
1048 | |||
1049 | module_init(mwifiex_init_module); | ||
1050 | module_exit(mwifiex_cleanup_module); | ||
1051 | |||
1052 | MODULE_AUTHOR("Marvell International Ltd."); | ||
1053 | MODULE_DESCRIPTION("Marvell WiFi-Ex Driver version " VERSION); | ||
1054 | MODULE_VERSION(VERSION); | ||
1055 | 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..8316b3cd92cd --- /dev/null +++ b/drivers/net/wireless/mwifiex/main.h | |||
@@ -0,0 +1,1009 @@ | |||
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_ASYNC_CMD, | ||
46 | MWIFIEX_SYNC_CMD | ||
47 | }; | ||
48 | |||
49 | #define DRV_MODE_STA 0x1 | ||
50 | |||
51 | #define SD8787_W0 0x30 | ||
52 | #define SD8787_W1 0x31 | ||
53 | #define SD8787_A0 0x40 | ||
54 | #define SD8787_A1 0x41 | ||
55 | |||
56 | #define DEFAULT_FW_NAME "mrvl/sd8787_uapsta.bin" | ||
57 | #define SD8787_W1_FW_NAME "mrvl/sd8787_uapsta_w1.bin" | ||
58 | #define SD8787_AX_FW_NAME "mrvl/sd8787_uapsta.bin" | ||
59 | |||
60 | struct mwifiex_drv_mode { | ||
61 | u16 drv_mode; | ||
62 | u16 intf_num; | ||
63 | struct mwifiex_bss_attr *bss_attr; | ||
64 | }; | ||
65 | |||
66 | |||
67 | #define MWIFIEX_DEFAULT_WATCHDOG_TIMEOUT (5 * HZ) | ||
68 | |||
69 | #define MWIFIEX_TIMER_10S 10000 | ||
70 | #define MWIFIEX_TIMER_1S 1000 | ||
71 | |||
72 | #define MAX_TX_PENDING 100 | ||
73 | #define LOW_TX_PENDING 80 | ||
74 | |||
75 | #define MWIFIEX_UPLD_SIZE (2312) | ||
76 | |||
77 | #define MAX_EVENT_SIZE 1024 | ||
78 | |||
79 | #define ARP_FILTER_MAX_BUF_SIZE 68 | ||
80 | |||
81 | #define MWIFIEX_KEY_BUFFER_SIZE 16 | ||
82 | #define MWIFIEX_DEFAULT_LISTEN_INTERVAL 10 | ||
83 | #define MWIFIEX_MAX_REGION_CODE 7 | ||
84 | |||
85 | #define DEFAULT_BCN_AVG_FACTOR 8 | ||
86 | #define DEFAULT_DATA_AVG_FACTOR 8 | ||
87 | |||
88 | #define FIRST_VALID_CHANNEL 0xff | ||
89 | #define DEFAULT_AD_HOC_CHANNEL 6 | ||
90 | #define DEFAULT_AD_HOC_CHANNEL_A 36 | ||
91 | |||
92 | #define DEFAULT_BCN_MISS_TIMEOUT 5 | ||
93 | |||
94 | #define MAX_SCAN_BEACON_BUFFER 8000 | ||
95 | |||
96 | #define SCAN_BEACON_ENTRY_PAD 6 | ||
97 | |||
98 | #define MWIFIEX_PASSIVE_SCAN_CHAN_TIME 200 | ||
99 | #define MWIFIEX_ACTIVE_SCAN_CHAN_TIME 200 | ||
100 | #define MWIFIEX_SPECIFIC_SCAN_CHAN_TIME 110 | ||
101 | |||
102 | #define SCAN_RSSI(RSSI) (0x100 - ((u8)(RSSI))) | ||
103 | |||
104 | #define MWIFIEX_MAX_TOTAL_SCAN_TIME (MWIFIEX_TIMER_10S - MWIFIEX_TIMER_1S) | ||
105 | |||
106 | #define RSN_GTK_OUI_OFFSET 2 | ||
107 | |||
108 | #define MWIFIEX_OUI_NOT_PRESENT 0 | ||
109 | #define MWIFIEX_OUI_PRESENT 1 | ||
110 | |||
111 | #define IS_CARD_RX_RCVD(adapter) (adapter->cmd_resp_received || \ | ||
112 | adapter->event_received || \ | ||
113 | adapter->data_received) | ||
114 | |||
115 | #define MWIFIEX_TYPE_CMD 1 | ||
116 | #define MWIFIEX_TYPE_DATA 0 | ||
117 | #define MWIFIEX_TYPE_EVENT 3 | ||
118 | |||
119 | #define DBG_CMD_NUM 5 | ||
120 | |||
121 | #define MAX_BITMAP_RATES_SIZE 10 | ||
122 | |||
123 | #define MAX_CHANNEL_BAND_BG 14 | ||
124 | |||
125 | #define MAX_FREQUENCY_BAND_BG 2484 | ||
126 | |||
127 | struct mwifiex_dbg { | ||
128 | u32 num_cmd_host_to_card_failure; | ||
129 | u32 num_cmd_sleep_cfm_host_to_card_failure; | ||
130 | u32 num_tx_host_to_card_failure; | ||
131 | u32 num_event_deauth; | ||
132 | u32 num_event_disassoc; | ||
133 | u32 num_event_link_lost; | ||
134 | u32 num_cmd_deauth; | ||
135 | u32 num_cmd_assoc_success; | ||
136 | u32 num_cmd_assoc_failure; | ||
137 | u32 num_tx_timeout; | ||
138 | u32 num_cmd_timeout; | ||
139 | u16 timeout_cmd_id; | ||
140 | u16 timeout_cmd_act; | ||
141 | u16 last_cmd_id[DBG_CMD_NUM]; | ||
142 | u16 last_cmd_act[DBG_CMD_NUM]; | ||
143 | u16 last_cmd_index; | ||
144 | u16 last_cmd_resp_id[DBG_CMD_NUM]; | ||
145 | u16 last_cmd_resp_index; | ||
146 | u16 last_event[DBG_CMD_NUM]; | ||
147 | u16 last_event_index; | ||
148 | }; | ||
149 | |||
150 | enum MWIFIEX_HARDWARE_STATUS { | ||
151 | MWIFIEX_HW_STATUS_READY, | ||
152 | MWIFIEX_HW_STATUS_INITIALIZING, | ||
153 | MWIFIEX_HW_STATUS_FW_READY, | ||
154 | MWIFIEX_HW_STATUS_INIT_DONE, | ||
155 | MWIFIEX_HW_STATUS_RESET, | ||
156 | MWIFIEX_HW_STATUS_CLOSING, | ||
157 | MWIFIEX_HW_STATUS_NOT_READY | ||
158 | }; | ||
159 | |||
160 | enum MWIFIEX_802_11_POWER_MODE { | ||
161 | MWIFIEX_802_11_POWER_MODE_CAM, | ||
162 | MWIFIEX_802_11_POWER_MODE_PSP | ||
163 | }; | ||
164 | |||
165 | struct mwifiex_tx_param { | ||
166 | u32 next_pkt_len; | ||
167 | }; | ||
168 | |||
169 | enum MWIFIEX_PS_STATE { | ||
170 | PS_STATE_AWAKE, | ||
171 | PS_STATE_PRE_SLEEP, | ||
172 | PS_STATE_SLEEP_CFM, | ||
173 | PS_STATE_SLEEP | ||
174 | }; | ||
175 | |||
176 | struct mwifiex_add_ba_param { | ||
177 | u32 tx_win_size; | ||
178 | u32 rx_win_size; | ||
179 | u32 timeout; | ||
180 | }; | ||
181 | |||
182 | struct mwifiex_tx_aggr { | ||
183 | u8 ampdu_user; | ||
184 | u8 ampdu_ap; | ||
185 | u8 amsdu; | ||
186 | }; | ||
187 | |||
188 | struct mwifiex_ra_list_tbl { | ||
189 | struct list_head list; | ||
190 | struct sk_buff_head skb_head; | ||
191 | u8 ra[ETH_ALEN]; | ||
192 | u32 total_pkts_size; | ||
193 | u32 is_11n_enabled; | ||
194 | }; | ||
195 | |||
196 | struct mwifiex_tid_tbl { | ||
197 | struct list_head ra_list; | ||
198 | /* spin lock for tid table */ | ||
199 | spinlock_t tid_tbl_lock; | ||
200 | struct mwifiex_ra_list_tbl *ra_list_curr; | ||
201 | }; | ||
202 | |||
203 | #define WMM_HIGHEST_PRIORITY 7 | ||
204 | #define HIGH_PRIO_TID 7 | ||
205 | #define LOW_PRIO_TID 0 | ||
206 | #define NO_PKT_PRIO_TID (-1) | ||
207 | |||
208 | struct mwifiex_wmm_desc { | ||
209 | struct mwifiex_tid_tbl tid_tbl_ptr[MAX_NUM_TID]; | ||
210 | u32 packets_out[MAX_NUM_TID]; | ||
211 | /* spin lock to protect ra_list */ | ||
212 | spinlock_t ra_list_spinlock; | ||
213 | struct mwifiex_wmm_ac_status ac_status[IEEE80211_MAX_QUEUES]; | ||
214 | enum mwifiex_wmm_ac_e ac_down_graded_vals[IEEE80211_MAX_QUEUES]; | ||
215 | u32 drv_pkt_delay_max; | ||
216 | u8 queue_priority[IEEE80211_MAX_QUEUES]; | ||
217 | u32 user_pri_pkt_tx_ctrl[WMM_HIGHEST_PRIORITY + 1]; /* UP: 0 to 7 */ | ||
218 | /* Number of transmit packets queued */ | ||
219 | atomic_t tx_pkts_queued; | ||
220 | /* Tracks highest priority with a packet queued */ | ||
221 | atomic_t highest_queued_prio; | ||
222 | }; | ||
223 | |||
224 | struct mwifiex_802_11_security { | ||
225 | u8 wpa_enabled; | ||
226 | u8 wpa2_enabled; | ||
227 | u8 wapi_enabled; | ||
228 | u8 wapi_key_on; | ||
229 | enum MWIFIEX_802_11_WEP_STATUS wep_status; | ||
230 | u32 authentication_mode; | ||
231 | u32 encryption_mode; | ||
232 | }; | ||
233 | |||
234 | struct ieee_types_header { | ||
235 | u8 element_id; | ||
236 | u8 len; | ||
237 | } __packed; | ||
238 | |||
239 | struct ieee_obss_scan_param { | ||
240 | u16 obss_scan_passive_dwell; | ||
241 | u16 obss_scan_active_dwell; | ||
242 | u16 bss_chan_width_trigger_scan_int; | ||
243 | u16 obss_scan_passive_total; | ||
244 | u16 obss_scan_active_total; | ||
245 | u16 bss_width_chan_trans_delay; | ||
246 | u16 obss_scan_active_threshold; | ||
247 | } __packed; | ||
248 | |||
249 | struct ieee_types_obss_scan_param { | ||
250 | struct ieee_types_header ieee_hdr; | ||
251 | struct ieee_obss_scan_param obss_scan; | ||
252 | } __packed; | ||
253 | |||
254 | #define MWIFIEX_SUPPORTED_RATES 14 | ||
255 | |||
256 | #define MWIFIEX_SUPPORTED_RATES_EXT 32 | ||
257 | |||
258 | #define IEEE_MAX_IE_SIZE 256 | ||
259 | |||
260 | struct ieee_types_vendor_specific { | ||
261 | struct ieee_types_vendor_header vend_hdr; | ||
262 | u8 data[IEEE_MAX_IE_SIZE - sizeof(struct ieee_types_vendor_header)]; | ||
263 | } __packed; | ||
264 | |||
265 | struct ieee_types_generic { | ||
266 | struct ieee_types_header ieee_hdr; | ||
267 | u8 data[IEEE_MAX_IE_SIZE - sizeof(struct ieee_types_header)]; | ||
268 | } __packed; | ||
269 | |||
270 | struct mwifiex_bssdescriptor { | ||
271 | u8 mac_address[ETH_ALEN]; | ||
272 | struct mwifiex_802_11_ssid ssid; | ||
273 | u32 privacy; | ||
274 | s32 rssi; | ||
275 | u32 channel; | ||
276 | u32 freq; | ||
277 | u16 beacon_period; | ||
278 | u8 erp_flags; | ||
279 | u32 bss_mode; | ||
280 | u8 supported_rates[MWIFIEX_SUPPORTED_RATES]; | ||
281 | u8 data_rates[MWIFIEX_SUPPORTED_RATES]; | ||
282 | /* Network band. | ||
283 | * BAND_B(0x01): 'b' band | ||
284 | * BAND_G(0x02): 'g' band | ||
285 | * BAND_A(0X04): 'a' band | ||
286 | */ | ||
287 | u16 bss_band; | ||
288 | u64 network_tsf; | ||
289 | u8 time_stamp[8]; | ||
290 | union ieee_types_phy_param_set phy_param_set; | ||
291 | union ieee_types_ss_param_set ss_param_set; | ||
292 | u16 cap_info_bitmap; | ||
293 | struct ieee_types_wmm_parameter wmm_ie; | ||
294 | u8 disable_11n; | ||
295 | struct ieee80211_ht_cap *bcn_ht_cap; | ||
296 | u16 ht_cap_offset; | ||
297 | struct ieee80211_ht_info *bcn_ht_info; | ||
298 | u16 ht_info_offset; | ||
299 | u8 *bcn_bss_co_2040; | ||
300 | u16 bss_co_2040_offset; | ||
301 | u8 *bcn_ext_cap; | ||
302 | u16 ext_cap_offset; | ||
303 | struct ieee_types_obss_scan_param *bcn_obss_scan; | ||
304 | u16 overlap_bss_offset; | ||
305 | struct ieee_types_vendor_specific *bcn_wpa_ie; | ||
306 | u16 wpa_offset; | ||
307 | struct ieee_types_generic *bcn_rsn_ie; | ||
308 | u16 rsn_offset; | ||
309 | struct ieee_types_generic *bcn_wapi_ie; | ||
310 | u16 wapi_offset; | ||
311 | u8 *beacon_buf; | ||
312 | u32 beacon_buf_size; | ||
313 | u32 beacon_buf_size_max; | ||
314 | |||
315 | }; | ||
316 | |||
317 | struct mwifiex_current_bss_params { | ||
318 | struct mwifiex_bssdescriptor bss_descriptor; | ||
319 | u8 wmm_enabled; | ||
320 | u8 wmm_uapsd_enabled; | ||
321 | u8 band; | ||
322 | u32 num_of_rates; | ||
323 | u8 data_rates[MWIFIEX_SUPPORTED_RATES]; | ||
324 | }; | ||
325 | |||
326 | struct mwifiex_sleep_params { | ||
327 | u16 sp_error; | ||
328 | u16 sp_offset; | ||
329 | u16 sp_stable_time; | ||
330 | u8 sp_cal_control; | ||
331 | u8 sp_ext_sleep_clk; | ||
332 | u16 sp_reserved; | ||
333 | }; | ||
334 | |||
335 | struct mwifiex_sleep_period { | ||
336 | u16 period; | ||
337 | u16 reserved; | ||
338 | }; | ||
339 | |||
340 | struct mwifiex_wep_key { | ||
341 | u32 length; | ||
342 | u32 key_index; | ||
343 | u32 key_length; | ||
344 | u8 key_material[MWIFIEX_KEY_BUFFER_SIZE]; | ||
345 | }; | ||
346 | |||
347 | #define MAX_REGION_CHANNEL_NUM 2 | ||
348 | |||
349 | struct mwifiex_chan_freq_power { | ||
350 | u16 channel; | ||
351 | u32 freq; | ||
352 | u16 max_tx_power; | ||
353 | u8 unsupported; | ||
354 | }; | ||
355 | |||
356 | enum state_11d_t { | ||
357 | DISABLE_11D = 0, | ||
358 | ENABLE_11D = 1, | ||
359 | }; | ||
360 | |||
361 | #define MWIFIEX_MAX_TRIPLET_802_11D 83 | ||
362 | |||
363 | struct mwifiex_802_11d_domain_reg { | ||
364 | u8 country_code[IEEE80211_COUNTRY_STRING_LEN]; | ||
365 | u8 no_of_triplet; | ||
366 | struct ieee80211_country_ie_triplet | ||
367 | triplet[MWIFIEX_MAX_TRIPLET_802_11D]; | ||
368 | }; | ||
369 | |||
370 | struct mwifiex_vendor_spec_cfg_ie { | ||
371 | u16 mask; | ||
372 | u16 flag; | ||
373 | u8 ie[MWIFIEX_MAX_VSIE_LEN]; | ||
374 | }; | ||
375 | |||
376 | struct wps { | ||
377 | u8 session_enable; | ||
378 | }; | ||
379 | |||
380 | struct mwifiex_adapter; | ||
381 | struct mwifiex_private; | ||
382 | |||
383 | struct mwifiex_private { | ||
384 | struct mwifiex_adapter *adapter; | ||
385 | u8 bss_index; | ||
386 | u8 bss_type; | ||
387 | u8 bss_role; | ||
388 | u8 bss_priority; | ||
389 | u8 bss_num; | ||
390 | u8 frame_type; | ||
391 | u8 curr_addr[ETH_ALEN]; | ||
392 | u8 media_connected; | ||
393 | u32 num_tx_timeout; | ||
394 | struct net_device *netdev; | ||
395 | struct net_device_stats stats; | ||
396 | u16 curr_pkt_filter; | ||
397 | u32 bss_mode; | ||
398 | u32 pkt_tx_ctrl; | ||
399 | u16 tx_power_level; | ||
400 | u8 max_tx_power_level; | ||
401 | u8 min_tx_power_level; | ||
402 | u8 tx_rate; | ||
403 | u8 tx_htinfo; | ||
404 | u8 rxpd_htinfo; | ||
405 | u8 rxpd_rate; | ||
406 | u16 rate_bitmap; | ||
407 | u16 bitmap_rates[MAX_BITMAP_RATES_SIZE]; | ||
408 | u32 data_rate; | ||
409 | u8 is_data_rate_auto; | ||
410 | u16 bcn_avg_factor; | ||
411 | u16 data_avg_factor; | ||
412 | s16 data_rssi_last; | ||
413 | s16 data_nf_last; | ||
414 | s16 data_rssi_avg; | ||
415 | s16 data_nf_avg; | ||
416 | s16 bcn_rssi_last; | ||
417 | s16 bcn_nf_last; | ||
418 | s16 bcn_rssi_avg; | ||
419 | s16 bcn_nf_avg; | ||
420 | struct mwifiex_bssdescriptor *attempted_bss_desc; | ||
421 | struct mwifiex_802_11_ssid prev_ssid; | ||
422 | u8 prev_bssid[ETH_ALEN]; | ||
423 | struct mwifiex_current_bss_params curr_bss_params; | ||
424 | u16 beacon_period; | ||
425 | u16 listen_interval; | ||
426 | u16 atim_window; | ||
427 | u8 adhoc_channel; | ||
428 | u8 adhoc_is_link_sensed; | ||
429 | u8 adhoc_state; | ||
430 | struct mwifiex_802_11_security sec_info; | ||
431 | struct mwifiex_wep_key wep_key[NUM_WEP_KEYS]; | ||
432 | u16 wep_key_curr_index; | ||
433 | u8 wpa_ie[256]; | ||
434 | u8 wpa_ie_len; | ||
435 | u8 wpa_is_gtk_set; | ||
436 | struct host_cmd_ds_802_11_key_material aes_key; | ||
437 | u8 wapi_ie[256]; | ||
438 | u8 wapi_ie_len; | ||
439 | u8 wmm_required; | ||
440 | u8 wmm_enabled; | ||
441 | u8 wmm_qosinfo; | ||
442 | struct mwifiex_wmm_desc wmm; | ||
443 | struct list_head tx_ba_stream_tbl_ptr; | ||
444 | /* spin lock for tx_ba_stream_tbl_ptr queue */ | ||
445 | spinlock_t tx_ba_stream_tbl_lock; | ||
446 | struct mwifiex_tx_aggr aggr_prio_tbl[MAX_NUM_TID]; | ||
447 | struct mwifiex_add_ba_param add_ba_param; | ||
448 | u16 rx_seq[MAX_NUM_TID]; | ||
449 | struct list_head rx_reorder_tbl_ptr; | ||
450 | /* spin lock for rx_reorder_tbl_ptr queue */ | ||
451 | spinlock_t rx_reorder_tbl_lock; | ||
452 | /* spin lock for Rx packets */ | ||
453 | spinlock_t rx_pkt_lock; | ||
454 | |||
455 | #define MWIFIEX_ASSOC_RSP_BUF_SIZE 500 | ||
456 | u8 assoc_rsp_buf[MWIFIEX_ASSOC_RSP_BUF_SIZE]; | ||
457 | u32 assoc_rsp_size; | ||
458 | |||
459 | #define MWIFIEX_GENIE_BUF_SIZE 256 | ||
460 | u8 gen_ie_buf[MWIFIEX_GENIE_BUF_SIZE]; | ||
461 | u8 gen_ie_buf_len; | ||
462 | |||
463 | struct mwifiex_vendor_spec_cfg_ie vs_ie[MWIFIEX_MAX_VSIE_NUM]; | ||
464 | |||
465 | #define MWIFIEX_ASSOC_TLV_BUF_SIZE 256 | ||
466 | u8 assoc_tlv_buf[MWIFIEX_ASSOC_TLV_BUF_SIZE]; | ||
467 | u8 assoc_tlv_buf_len; | ||
468 | |||
469 | u8 *curr_bcn_buf; | ||
470 | u32 curr_bcn_size; | ||
471 | /* spin lock for beacon buffer */ | ||
472 | spinlock_t curr_bcn_buf_lock; | ||
473 | struct wireless_dev *wdev; | ||
474 | struct mwifiex_chan_freq_power cfp; | ||
475 | char version_str[128]; | ||
476 | #ifdef CONFIG_DEBUG_FS | ||
477 | struct dentry *dfs_dev_dir; | ||
478 | #endif | ||
479 | u8 nick_name[16]; | ||
480 | struct iw_statistics w_stats; | ||
481 | u16 current_key_index; | ||
482 | struct semaphore async_sem; | ||
483 | u8 scan_pending_on_block; | ||
484 | u8 report_scan_result; | ||
485 | struct cfg80211_scan_request *scan_request; | ||
486 | int scan_result_status; | ||
487 | int assoc_request; | ||
488 | u16 assoc_result; | ||
489 | int ibss_join_request; | ||
490 | u16 ibss_join_result; | ||
491 | bool disconnect; | ||
492 | u8 cfg_bssid[6]; | ||
493 | struct workqueue_struct *workqueue; | ||
494 | struct work_struct cfg_workqueue; | ||
495 | u8 country_code[IEEE80211_COUNTRY_STRING_LEN]; | ||
496 | struct wps wps; | ||
497 | u8 scan_block; | ||
498 | }; | ||
499 | |||
500 | enum mwifiex_ba_status { | ||
501 | BA_STREAM_NOT_SETUP = 0, | ||
502 | BA_STREAM_SETUP_INPROGRESS, | ||
503 | BA_STREAM_SETUP_COMPLETE | ||
504 | }; | ||
505 | |||
506 | struct mwifiex_tx_ba_stream_tbl { | ||
507 | struct list_head list; | ||
508 | int tid; | ||
509 | u8 ra[ETH_ALEN]; | ||
510 | enum mwifiex_ba_status ba_status; | ||
511 | }; | ||
512 | |||
513 | struct mwifiex_rx_reorder_tbl; | ||
514 | |||
515 | struct reorder_tmr_cnxt { | ||
516 | struct timer_list timer; | ||
517 | struct mwifiex_rx_reorder_tbl *ptr; | ||
518 | struct mwifiex_private *priv; | ||
519 | }; | ||
520 | |||
521 | struct mwifiex_rx_reorder_tbl { | ||
522 | struct list_head list; | ||
523 | int tid; | ||
524 | u8 ta[ETH_ALEN]; | ||
525 | int start_win; | ||
526 | int win_size; | ||
527 | void **rx_reorder_ptr; | ||
528 | struct reorder_tmr_cnxt timer_context; | ||
529 | }; | ||
530 | |||
531 | struct mwifiex_bss_prio_node { | ||
532 | struct list_head list; | ||
533 | struct mwifiex_private *priv; | ||
534 | }; | ||
535 | |||
536 | struct mwifiex_bss_prio_tbl { | ||
537 | struct list_head bss_prio_head; | ||
538 | /* spin lock for bss priority */ | ||
539 | spinlock_t bss_prio_lock; | ||
540 | struct mwifiex_bss_prio_node *bss_prio_cur; | ||
541 | }; | ||
542 | |||
543 | struct cmd_ctrl_node { | ||
544 | struct list_head list; | ||
545 | struct mwifiex_private *priv; | ||
546 | u32 cmd_oid; | ||
547 | u32 cmd_flag; | ||
548 | struct sk_buff *cmd_skb; | ||
549 | struct sk_buff *resp_skb; | ||
550 | void *data_buf; | ||
551 | u32 wait_q_enabled; | ||
552 | struct sk_buff *skb; | ||
553 | }; | ||
554 | |||
555 | struct mwifiex_if_ops { | ||
556 | int (*init_if) (struct mwifiex_adapter *); | ||
557 | void (*cleanup_if) (struct mwifiex_adapter *); | ||
558 | int (*check_fw_status) (struct mwifiex_adapter *, u32, int *); | ||
559 | int (*prog_fw) (struct mwifiex_adapter *, struct mwifiex_fw_image *); | ||
560 | int (*register_dev) (struct mwifiex_adapter *); | ||
561 | void (*unregister_dev) (struct mwifiex_adapter *); | ||
562 | int (*enable_int) (struct mwifiex_adapter *); | ||
563 | int (*process_int_status) (struct mwifiex_adapter *); | ||
564 | int (*host_to_card) (struct mwifiex_adapter *, u8, | ||
565 | u8 *payload, u32 pkt_len, | ||
566 | struct mwifiex_tx_param *); | ||
567 | int (*wakeup) (struct mwifiex_adapter *); | ||
568 | int (*wakeup_complete) (struct mwifiex_adapter *); | ||
569 | |||
570 | void (*update_mp_end_port) (struct mwifiex_adapter *, u16); | ||
571 | void (*cleanup_mpa_buf) (struct mwifiex_adapter *); | ||
572 | }; | ||
573 | |||
574 | struct mwifiex_adapter { | ||
575 | struct mwifiex_private *priv[MWIFIEX_MAX_BSS_NUM]; | ||
576 | u8 priv_num; | ||
577 | struct mwifiex_drv_mode *drv_mode; | ||
578 | const struct firmware *firmware; | ||
579 | struct device *dev; | ||
580 | bool surprise_removed; | ||
581 | u32 fw_release_number; | ||
582 | u32 revision_id; | ||
583 | u16 init_wait_q_woken; | ||
584 | wait_queue_head_t init_wait_q; | ||
585 | void *card; | ||
586 | struct mwifiex_if_ops if_ops; | ||
587 | atomic_t rx_pending; | ||
588 | atomic_t tx_pending; | ||
589 | atomic_t cmd_pending; | ||
590 | struct workqueue_struct *workqueue; | ||
591 | struct work_struct main_work; | ||
592 | struct mwifiex_bss_prio_tbl bss_prio_tbl[MWIFIEX_MAX_BSS_NUM]; | ||
593 | /* spin lock for init/shutdown */ | ||
594 | spinlock_t mwifiex_lock; | ||
595 | /* spin lock for main process */ | ||
596 | spinlock_t main_proc_lock; | ||
597 | u32 mwifiex_processing; | ||
598 | u16 max_tx_buf_size; | ||
599 | u16 tx_buf_size; | ||
600 | u16 curr_tx_buf_size; | ||
601 | u32 ioport; | ||
602 | enum MWIFIEX_HARDWARE_STATUS hw_status; | ||
603 | u16 number_of_antenna; | ||
604 | u32 fw_cap_info; | ||
605 | /* spin lock for interrupt handling */ | ||
606 | spinlock_t int_lock; | ||
607 | u8 int_status; | ||
608 | u32 event_cause; | ||
609 | struct sk_buff *event_skb; | ||
610 | u8 upld_buf[MWIFIEX_UPLD_SIZE]; | ||
611 | u8 data_sent; | ||
612 | u8 cmd_sent; | ||
613 | u8 cmd_resp_received; | ||
614 | u8 event_received; | ||
615 | u8 data_received; | ||
616 | u16 seq_num; | ||
617 | struct cmd_ctrl_node *cmd_pool; | ||
618 | struct cmd_ctrl_node *curr_cmd; | ||
619 | /* spin lock for command */ | ||
620 | spinlock_t mwifiex_cmd_lock; | ||
621 | u32 num_cmd_timeout; | ||
622 | u16 last_init_cmd; | ||
623 | struct timer_list cmd_timer; | ||
624 | struct list_head cmd_free_q; | ||
625 | /* spin lock for cmd_free_q */ | ||
626 | spinlock_t cmd_free_q_lock; | ||
627 | struct list_head cmd_pending_q; | ||
628 | /* spin lock for cmd_pending_q */ | ||
629 | spinlock_t cmd_pending_q_lock; | ||
630 | struct list_head scan_pending_q; | ||
631 | /* spin lock for scan_pending_q */ | ||
632 | spinlock_t scan_pending_q_lock; | ||
633 | u32 scan_processing; | ||
634 | u16 region_code; | ||
635 | struct mwifiex_802_11d_domain_reg domain_reg; | ||
636 | struct mwifiex_bssdescriptor *scan_table; | ||
637 | u32 num_in_scan_table; | ||
638 | u16 scan_probes; | ||
639 | u32 scan_mode; | ||
640 | u16 specific_scan_time; | ||
641 | u16 active_scan_time; | ||
642 | u16 passive_scan_time; | ||
643 | u8 bcn_buf[MAX_SCAN_BEACON_BUFFER]; | ||
644 | u8 *bcn_buf_end; | ||
645 | u8 fw_bands; | ||
646 | u8 adhoc_start_band; | ||
647 | u8 config_bands; | ||
648 | struct mwifiex_chan_scan_param_set *scan_channels; | ||
649 | u8 tx_lock_flag; | ||
650 | struct mwifiex_sleep_params sleep_params; | ||
651 | struct mwifiex_sleep_period sleep_period; | ||
652 | u16 ps_mode; | ||
653 | u32 ps_state; | ||
654 | u8 need_to_wakeup; | ||
655 | u16 multiple_dtim; | ||
656 | u16 local_listen_interval; | ||
657 | u16 null_pkt_interval; | ||
658 | struct sk_buff *sleep_cfm; | ||
659 | u16 bcn_miss_time_out; | ||
660 | u16 adhoc_awake_period; | ||
661 | u8 is_deep_sleep; | ||
662 | u8 delay_null_pkt; | ||
663 | u16 delay_to_ps; | ||
664 | u16 enhanced_ps_mode; | ||
665 | u8 pm_wakeup_card_req; | ||
666 | u16 gen_null_pkt; | ||
667 | u16 pps_uapsd_mode; | ||
668 | u32 pm_wakeup_fw_try; | ||
669 | u8 is_hs_configured; | ||
670 | struct mwifiex_hs_config_param hs_cfg; | ||
671 | u8 hs_activated; | ||
672 | u16 hs_activate_wait_q_woken; | ||
673 | wait_queue_head_t hs_activate_wait_q; | ||
674 | bool is_suspended; | ||
675 | u8 event_body[MAX_EVENT_SIZE]; | ||
676 | u32 hw_dot_11n_dev_cap; | ||
677 | u8 hw_dev_mcs_support; | ||
678 | u8 adhoc_11n_enabled; | ||
679 | u8 chan_offset; | ||
680 | struct mwifiex_dbg dbg; | ||
681 | u8 arp_filter[ARP_FILTER_MAX_BUF_SIZE]; | ||
682 | u32 arp_filter_size; | ||
683 | u16 cmd_wait_q_required; | ||
684 | struct mwifiex_wait_queue cmd_wait_q; | ||
685 | }; | ||
686 | |||
687 | int mwifiex_init_lock_list(struct mwifiex_adapter *adapter); | ||
688 | void mwifiex_free_lock_list(struct mwifiex_adapter *adapter); | ||
689 | |||
690 | int mwifiex_init_fw(struct mwifiex_adapter *adapter); | ||
691 | |||
692 | int mwifiex_init_fw_complete(struct mwifiex_adapter *adapter); | ||
693 | |||
694 | int mwifiex_shutdown_drv(struct mwifiex_adapter *adapter); | ||
695 | |||
696 | int mwifiex_shutdown_fw_complete(struct mwifiex_adapter *adapter); | ||
697 | |||
698 | int mwifiex_dnld_fw(struct mwifiex_adapter *, struct mwifiex_fw_image *); | ||
699 | |||
700 | int mwifiex_recv_packet(struct mwifiex_adapter *, struct sk_buff *skb); | ||
701 | |||
702 | int mwifiex_process_event(struct mwifiex_adapter *adapter); | ||
703 | |||
704 | int mwifiex_complete_cmd(struct mwifiex_adapter *adapter); | ||
705 | |||
706 | int mwifiex_send_cmd_async(struct mwifiex_private *priv, uint16_t cmd_no, | ||
707 | u16 cmd_action, u32 cmd_oid, void *data_buf); | ||
708 | |||
709 | int mwifiex_send_cmd_sync(struct mwifiex_private *priv, uint16_t cmd_no, | ||
710 | u16 cmd_action, u32 cmd_oid, void *data_buf); | ||
711 | |||
712 | void mwifiex_cmd_timeout_func(unsigned long function_context); | ||
713 | |||
714 | int mwifiex_get_debug_info(struct mwifiex_private *, | ||
715 | struct mwifiex_debug_info *); | ||
716 | |||
717 | int mwifiex_alloc_cmd_buffer(struct mwifiex_adapter *adapter); | ||
718 | int mwifiex_free_cmd_buffer(struct mwifiex_adapter *adapter); | ||
719 | void mwifiex_cancel_all_pending_cmd(struct mwifiex_adapter *adapter); | ||
720 | void mwifiex_cancel_pending_ioctl(struct mwifiex_adapter *adapter); | ||
721 | |||
722 | void mwifiex_insert_cmd_to_free_q(struct mwifiex_adapter *adapter, | ||
723 | struct cmd_ctrl_node *cmd_node); | ||
724 | |||
725 | void mwifiex_insert_cmd_to_pending_q(struct mwifiex_adapter *adapter, | ||
726 | struct cmd_ctrl_node *cmd_node, | ||
727 | u32 addtail); | ||
728 | |||
729 | int mwifiex_exec_next_cmd(struct mwifiex_adapter *adapter); | ||
730 | int mwifiex_process_cmdresp(struct mwifiex_adapter *adapter); | ||
731 | int mwifiex_handle_rx_packet(struct mwifiex_adapter *adapter, | ||
732 | struct sk_buff *skb); | ||
733 | int mwifiex_process_tx(struct mwifiex_private *priv, struct sk_buff *skb, | ||
734 | struct mwifiex_tx_param *tx_param); | ||
735 | int mwifiex_send_null_packet(struct mwifiex_private *priv, u8 flags); | ||
736 | int mwifiex_write_data_complete(struct mwifiex_adapter *adapter, | ||
737 | struct sk_buff *skb, int status); | ||
738 | int mwifiex_recv_packet_complete(struct mwifiex_adapter *, | ||
739 | struct sk_buff *skb, int status); | ||
740 | void mwifiex_clean_txrx(struct mwifiex_private *priv); | ||
741 | u8 mwifiex_check_last_packet_indication(struct mwifiex_private *priv); | ||
742 | void mwifiex_check_ps_cond(struct mwifiex_adapter *adapter); | ||
743 | void mwifiex_process_sleep_confirm_resp(struct mwifiex_adapter *, u8 *, | ||
744 | u32); | ||
745 | int mwifiex_cmd_enh_power_mode(struct mwifiex_private *priv, | ||
746 | struct host_cmd_ds_command *cmd, | ||
747 | u16 cmd_action, uint16_t ps_bitmap, | ||
748 | void *data_buf); | ||
749 | int mwifiex_ret_enh_power_mode(struct mwifiex_private *priv, | ||
750 | struct host_cmd_ds_command *resp, | ||
751 | void *data_buf); | ||
752 | void mwifiex_process_hs_config(struct mwifiex_adapter *adapter); | ||
753 | void mwifiex_hs_activated_event(struct mwifiex_private *priv, | ||
754 | u8 activated); | ||
755 | int mwifiex_ret_802_11_hs_cfg(struct mwifiex_private *priv, | ||
756 | struct host_cmd_ds_command *resp); | ||
757 | int mwifiex_process_rx_packet(struct mwifiex_adapter *adapter, | ||
758 | struct sk_buff *skb); | ||
759 | int mwifiex_sta_prepare_cmd(struct mwifiex_private *, uint16_t cmd_no, | ||
760 | u16 cmd_action, u32 cmd_oid, | ||
761 | void *data_buf, void *cmd_buf); | ||
762 | int mwifiex_process_sta_cmdresp(struct mwifiex_private *, u16 cmdresp_no, | ||
763 | void *cmd_buf); | ||
764 | int mwifiex_process_sta_rx_packet(struct mwifiex_adapter *, | ||
765 | struct sk_buff *skb); | ||
766 | int mwifiex_process_sta_event(struct mwifiex_private *); | ||
767 | void *mwifiex_process_sta_txpd(struct mwifiex_private *, struct sk_buff *skb); | ||
768 | int mwifiex_sta_init_cmd(struct mwifiex_private *, u8 first_sta); | ||
769 | int mwifiex_scan_networks(struct mwifiex_private *priv, | ||
770 | const struct mwifiex_user_scan_cfg *user_scan_in); | ||
771 | int mwifiex_cmd_802_11_scan(struct host_cmd_ds_command *cmd, | ||
772 | void *data_buf); | ||
773 | void mwifiex_queue_scan_cmd(struct mwifiex_private *priv, | ||
774 | struct cmd_ctrl_node *cmd_node); | ||
775 | int mwifiex_ret_802_11_scan(struct mwifiex_private *priv, | ||
776 | struct host_cmd_ds_command *resp); | ||
777 | s32 mwifiex_find_ssid_in_list(struct mwifiex_private *priv, | ||
778 | struct mwifiex_802_11_ssid *ssid, u8 *bssid, | ||
779 | u32 mode); | ||
780 | s32 mwifiex_find_bssid_in_list(struct mwifiex_private *priv, u8 *bssid, | ||
781 | u32 mode); | ||
782 | int mwifiex_find_best_network(struct mwifiex_private *priv, | ||
783 | struct mwifiex_ssid_bssid *req_ssid_bssid); | ||
784 | s32 mwifiex_ssid_cmp(struct mwifiex_802_11_ssid *ssid1, | ||
785 | struct mwifiex_802_11_ssid *ssid2); | ||
786 | int mwifiex_associate(struct mwifiex_private *priv, | ||
787 | struct mwifiex_bssdescriptor *bss_desc); | ||
788 | int mwifiex_cmd_802_11_associate(struct mwifiex_private *priv, | ||
789 | struct host_cmd_ds_command | ||
790 | *cmd, void *data_buf); | ||
791 | int mwifiex_ret_802_11_associate(struct mwifiex_private *priv, | ||
792 | struct host_cmd_ds_command *resp); | ||
793 | void mwifiex_reset_connect_state(struct mwifiex_private *priv); | ||
794 | void mwifiex_2040_coex_event(struct mwifiex_private *priv); | ||
795 | u8 mwifiex_band_to_radio_type(u8 band); | ||
796 | int mwifiex_deauthenticate(struct mwifiex_private *priv, u8 *mac); | ||
797 | int mwifiex_adhoc_start(struct mwifiex_private *priv, | ||
798 | struct mwifiex_802_11_ssid *adhoc_ssid); | ||
799 | int mwifiex_adhoc_join(struct mwifiex_private *priv, | ||
800 | struct mwifiex_bssdescriptor *bss_desc); | ||
801 | int mwifiex_cmd_802_11_ad_hoc_start(struct mwifiex_private *priv, | ||
802 | struct host_cmd_ds_command *cmd, | ||
803 | void *data_buf); | ||
804 | int mwifiex_cmd_802_11_ad_hoc_join(struct mwifiex_private *priv, | ||
805 | struct host_cmd_ds_command *cmd, | ||
806 | void *data_buf); | ||
807 | int mwifiex_ret_802_11_ad_hoc(struct mwifiex_private *priv, | ||
808 | struct host_cmd_ds_command *resp); | ||
809 | int mwifiex_cmd_802_11_bg_scan_query(struct host_cmd_ds_command *cmd); | ||
810 | struct mwifiex_chan_freq_power * | ||
811 | mwifiex_get_cfp_by_band_and_channel_from_cfg80211( | ||
812 | struct mwifiex_private *priv, | ||
813 | u8 band, u16 channel); | ||
814 | struct mwifiex_chan_freq_power *mwifiex_get_cfp_by_band_and_freq_from_cfg80211( | ||
815 | struct mwifiex_private *priv, | ||
816 | u8 band, u32 freq); | ||
817 | u32 mwifiex_index_to_data_rate(u8 index, u8 ht_info); | ||
818 | u32 mwifiex_find_freq_from_band_chan(u8, u8); | ||
819 | int mwifiex_cmd_append_vsie_tlv(struct mwifiex_private *priv, u16 vsie_mask, | ||
820 | u8 **buffer); | ||
821 | u32 mwifiex_get_active_data_rates(struct mwifiex_private *priv, | ||
822 | u8 *rates); | ||
823 | u32 mwifiex_get_supported_rates(struct mwifiex_private *priv, u8 *rates); | ||
824 | u8 mwifiex_data_rate_to_index(u32 rate); | ||
825 | u8 mwifiex_is_rate_auto(struct mwifiex_private *priv); | ||
826 | int mwifiex_get_rate_index(u16 *rateBitmap, int size); | ||
827 | extern u16 region_code_index[MWIFIEX_MAX_REGION_CODE]; | ||
828 | void mwifiex_save_curr_bcn(struct mwifiex_private *priv); | ||
829 | void mwifiex_free_curr_bcn(struct mwifiex_private *priv); | ||
830 | int mwifiex_cmd_get_hw_spec(struct mwifiex_private *priv, | ||
831 | struct host_cmd_ds_command *cmd); | ||
832 | int mwifiex_ret_get_hw_spec(struct mwifiex_private *priv, | ||
833 | struct host_cmd_ds_command *resp); | ||
834 | int is_command_pending(struct mwifiex_adapter *adapter); | ||
835 | |||
836 | /* | ||
837 | * This function checks if the queuing is RA based or not. | ||
838 | */ | ||
839 | static inline u8 | ||
840 | mwifiex_queuing_ra_based(struct mwifiex_private *priv) | ||
841 | { | ||
842 | /* | ||
843 | * Currently we assume if we are in Infra, then DA=RA. This might not be | ||
844 | * true in the future | ||
845 | */ | ||
846 | if ((priv->bss_mode == NL80211_IFTYPE_STATION) && | ||
847 | (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA)) | ||
848 | return false; | ||
849 | |||
850 | return true; | ||
851 | } | ||
852 | |||
853 | /* | ||
854 | * This function copies rates. | ||
855 | */ | ||
856 | static inline u32 | ||
857 | mwifiex_copy_rates(u8 *dest, u32 pos, u8 *src, int len) | ||
858 | { | ||
859 | int i; | ||
860 | |||
861 | for (i = 0; i < len && src[i]; i++, pos++) { | ||
862 | if (pos >= MWIFIEX_SUPPORTED_RATES) | ||
863 | break; | ||
864 | dest[pos] = src[i]; | ||
865 | } | ||
866 | |||
867 | return pos; | ||
868 | } | ||
869 | |||
870 | /* | ||
871 | * This function returns the correct private structure pointer based | ||
872 | * upon the BSS type and BSS number. | ||
873 | */ | ||
874 | static inline struct mwifiex_private * | ||
875 | mwifiex_get_priv_by_id(struct mwifiex_adapter *adapter, | ||
876 | u8 bss_num, u8 bss_type) | ||
877 | { | ||
878 | int i; | ||
879 | |||
880 | for (i = 0; i < adapter->priv_num; i++) { | ||
881 | if (adapter->priv[i]) { | ||
882 | if ((adapter->priv[i]->bss_num == bss_num) | ||
883 | && (adapter->priv[i]->bss_type == bss_type)) | ||
884 | break; | ||
885 | } | ||
886 | } | ||
887 | return ((i < adapter->priv_num) ? adapter->priv[i] : NULL); | ||
888 | } | ||
889 | |||
890 | /* | ||
891 | * This function returns the first available private structure pointer | ||
892 | * based upon the BSS role. | ||
893 | */ | ||
894 | static inline struct mwifiex_private * | ||
895 | mwifiex_get_priv(struct mwifiex_adapter *adapter, | ||
896 | enum mwifiex_bss_role bss_role) | ||
897 | { | ||
898 | int i; | ||
899 | |||
900 | for (i = 0; i < adapter->priv_num; i++) { | ||
901 | if (adapter->priv[i]) { | ||
902 | if (bss_role == MWIFIEX_BSS_ROLE_ANY || | ||
903 | GET_BSS_ROLE(adapter->priv[i]) == bss_role) | ||
904 | break; | ||
905 | } | ||
906 | } | ||
907 | |||
908 | return ((i < adapter->priv_num) ? adapter->priv[i] : NULL); | ||
909 | } | ||
910 | |||
911 | /* | ||
912 | * This function returns the driver private structure of a network device. | ||
913 | */ | ||
914 | static inline struct mwifiex_private * | ||
915 | mwifiex_netdev_get_priv(struct net_device *dev) | ||
916 | { | ||
917 | return (struct mwifiex_private *) (*(unsigned long *) netdev_priv(dev)); | ||
918 | } | ||
919 | |||
920 | struct mwifiex_private *mwifiex_bss_index_to_priv(struct mwifiex_adapter | ||
921 | *adapter, u8 bss_index); | ||
922 | int mwifiex_init_shutdown_fw(struct mwifiex_private *priv, | ||
923 | u32 func_init_shutdown); | ||
924 | int mwifiex_add_card(void *, struct semaphore *, struct mwifiex_if_ops *); | ||
925 | int mwifiex_remove_card(struct mwifiex_adapter *, struct semaphore *); | ||
926 | |||
927 | void mwifiex_get_version(struct mwifiex_adapter *adapter, char *version, | ||
928 | int maxlen); | ||
929 | int mwifiex_request_set_multicast_list(struct mwifiex_private *priv, | ||
930 | struct mwifiex_multicast_list *mcast_list); | ||
931 | int mwifiex_copy_mcast_addr(struct mwifiex_multicast_list *mlist, | ||
932 | struct net_device *dev); | ||
933 | int mwifiex_wait_queue_complete(struct mwifiex_adapter *adapter); | ||
934 | int mwifiex_bss_start(struct mwifiex_private *priv, | ||
935 | struct mwifiex_ssid_bssid *ssid_bssid); | ||
936 | int mwifiex_set_hs_params(struct mwifiex_private *priv, | ||
937 | u16 action, int cmd_type, | ||
938 | struct mwifiex_ds_hs_cfg *hscfg); | ||
939 | int mwifiex_cancel_hs(struct mwifiex_private *priv, int cmd_type); | ||
940 | int mwifiex_enable_hs(struct mwifiex_adapter *adapter); | ||
941 | int mwifiex_get_signal_info(struct mwifiex_private *priv, | ||
942 | struct mwifiex_ds_get_signal *signal); | ||
943 | int mwifiex_drv_get_data_rate(struct mwifiex_private *priv, | ||
944 | struct mwifiex_rate_cfg *rate); | ||
945 | int mwifiex_find_best_bss(struct mwifiex_private *priv, | ||
946 | struct mwifiex_ssid_bssid *ssid_bssid); | ||
947 | int mwifiex_request_scan(struct mwifiex_private *priv, | ||
948 | struct mwifiex_802_11_ssid *req_ssid); | ||
949 | int mwifiex_set_user_scan_ioctl(struct mwifiex_private *priv, | ||
950 | struct mwifiex_user_scan_cfg *scan_req); | ||
951 | int mwifiex_change_adhoc_chan(struct mwifiex_private *priv, int channel); | ||
952 | int mwifiex_set_radio(struct mwifiex_private *priv, u8 option); | ||
953 | |||
954 | int mwifiex_drv_change_adhoc_chan(struct mwifiex_private *priv, int channel); | ||
955 | |||
956 | int mwifiex_set_encode(struct mwifiex_private *priv, const u8 *key, | ||
957 | int key_len, u8 key_index, int disable); | ||
958 | |||
959 | int mwifiex_set_gen_ie(struct mwifiex_private *priv, u8 *ie, int ie_len); | ||
960 | |||
961 | int mwifiex_get_ver_ext(struct mwifiex_private *priv); | ||
962 | |||
963 | int mwifiex_get_stats_info(struct mwifiex_private *priv, | ||
964 | struct mwifiex_ds_get_stats *log); | ||
965 | |||
966 | int mwifiex_reg_write(struct mwifiex_private *priv, u32 reg_type, | ||
967 | u32 reg_offset, u32 reg_value); | ||
968 | |||
969 | int mwifiex_reg_read(struct mwifiex_private *priv, u32 reg_type, | ||
970 | u32 reg_offset, u32 *value); | ||
971 | |||
972 | int mwifiex_eeprom_read(struct mwifiex_private *priv, u16 offset, u16 bytes, | ||
973 | u8 *value); | ||
974 | |||
975 | int mwifiex_set_11n_httx_cfg(struct mwifiex_private *priv, int data); | ||
976 | |||
977 | int mwifiex_get_11n_httx_cfg(struct mwifiex_private *priv, int *data); | ||
978 | |||
979 | int mwifiex_set_tx_rate_cfg(struct mwifiex_private *priv, int tx_rate_index); | ||
980 | |||
981 | int mwifiex_get_tx_rate_cfg(struct mwifiex_private *priv, int *tx_rate_index); | ||
982 | |||
983 | int mwifiex_drv_set_power(struct mwifiex_private *priv, u32 *ps_mode); | ||
984 | |||
985 | int mwifiex_drv_get_driver_version(struct mwifiex_adapter *adapter, | ||
986 | char *version, int max_len); | ||
987 | |||
988 | int mwifiex_set_tx_power(struct mwifiex_private *priv, | ||
989 | struct mwifiex_power_cfg *power_cfg); | ||
990 | |||
991 | int mwifiex_main_process(struct mwifiex_adapter *); | ||
992 | |||
993 | int mwifiex_bss_set_channel(struct mwifiex_private *, | ||
994 | struct mwifiex_chan_freq_power *cfp); | ||
995 | int mwifiex_bss_ioctl_find_bss(struct mwifiex_private *, | ||
996 | struct mwifiex_ssid_bssid *); | ||
997 | int mwifiex_set_radio_band_cfg(struct mwifiex_private *, | ||
998 | struct mwifiex_ds_band_cfg *); | ||
999 | int mwifiex_get_bss_info(struct mwifiex_private *, | ||
1000 | struct mwifiex_bss_info *); | ||
1001 | |||
1002 | #ifdef CONFIG_DEBUG_FS | ||
1003 | void mwifiex_debugfs_init(void); | ||
1004 | void mwifiex_debugfs_remove(void); | ||
1005 | |||
1006 | void mwifiex_dev_debugfs_init(struct mwifiex_private *priv); | ||
1007 | void mwifiex_dev_debugfs_remove(struct mwifiex_private *priv); | ||
1008 | #endif | ||
1009 | #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..5c22860fb40a --- /dev/null +++ b/drivers/net/wireless/mwifiex/scan.c | |||
@@ -0,0 +1,3025 @@ | |||
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; | ||
121 | struct ie_body *iebody; | ||
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; | ||
148 | struct ie_body *iebody; | ||
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 | struct mwifiex_ssid_bssid *ssid_bssid) | ||
182 | { | ||
183 | struct mwifiex_ssid_bssid tmp_ssid_bssid; | ||
184 | u8 *mac; | ||
185 | |||
186 | if (!ssid_bssid) | ||
187 | return -1; | ||
188 | |||
189 | memcpy(&tmp_ssid_bssid, ssid_bssid, | ||
190 | sizeof(struct mwifiex_ssid_bssid)); | ||
191 | |||
192 | if (!mwifiex_bss_ioctl_find_bss(priv, &tmp_ssid_bssid)) { | ||
193 | memcpy(ssid_bssid, &tmp_ssid_bssid, | ||
194 | sizeof(struct mwifiex_ssid_bssid)); | ||
195 | mac = (u8 *) &ssid_bssid->bssid; | ||
196 | dev_dbg(priv->adapter->dev, "cmd: found network: ssid=%s," | ||
197 | " %pM\n", ssid_bssid->ssid.ssid, mac); | ||
198 | return 0; | ||
199 | } | ||
200 | |||
201 | return -1; | ||
202 | } | ||
203 | |||
204 | /* | ||
205 | * Sends IOCTL request to start a scan with user configurations. | ||
206 | * | ||
207 | * This function allocates the IOCTL request buffer, fills it | ||
208 | * with requisite parameters and calls the IOCTL handler. | ||
209 | * | ||
210 | * Upon completion, it also generates a wireless event to notify | ||
211 | * applications. | ||
212 | */ | ||
213 | int mwifiex_set_user_scan_ioctl(struct mwifiex_private *priv, | ||
214 | struct mwifiex_user_scan_cfg *scan_req) | ||
215 | { | ||
216 | int status; | ||
217 | |||
218 | priv->adapter->cmd_wait_q.condition = false; | ||
219 | |||
220 | status = mwifiex_scan_networks(priv, scan_req); | ||
221 | if (!status) | ||
222 | status = mwifiex_wait_queue_complete(priv->adapter); | ||
223 | |||
224 | return status; | ||
225 | } | ||
226 | |||
227 | /* | ||
228 | * This function checks if wapi is enabled in driver and scanned network is | ||
229 | * compatible with it. | ||
230 | */ | ||
231 | static bool | ||
232 | mwifiex_is_network_compatible_for_wapi(struct mwifiex_private *priv, | ||
233 | struct mwifiex_bssdescriptor *bss_desc) | ||
234 | { | ||
235 | if (priv->sec_info.wapi_enabled && | ||
236 | (bss_desc->bcn_wapi_ie && | ||
237 | ((*(bss_desc->bcn_wapi_ie)).ieee_hdr.element_id == | ||
238 | WLAN_EID_BSS_AC_ACCESS_DELAY))) { | ||
239 | return true; | ||
240 | } | ||
241 | return false; | ||
242 | } | ||
243 | |||
244 | /* | ||
245 | * This function checks if driver is configured with no security mode and | ||
246 | * scanned network is compatible with it. | ||
247 | */ | ||
248 | static bool | ||
249 | mwifiex_is_network_compatible_for_no_sec(struct mwifiex_private *priv, | ||
250 | struct mwifiex_bssdescriptor *bss_desc) | ||
251 | { | ||
252 | if (priv->sec_info.wep_status == MWIFIEX_802_11_WEP_DISABLED | ||
253 | && !priv->sec_info.wpa_enabled && !priv->sec_info.wpa2_enabled | ||
254 | && ((!bss_desc->bcn_wpa_ie) || | ||
255 | ((*(bss_desc->bcn_wpa_ie)).vend_hdr.element_id != | ||
256 | WLAN_EID_WPA)) | ||
257 | && ((!bss_desc->bcn_rsn_ie) || | ||
258 | ((*(bss_desc->bcn_rsn_ie)).ieee_hdr.element_id != | ||
259 | WLAN_EID_RSN)) | ||
260 | && !priv->sec_info.encryption_mode | ||
261 | && !bss_desc->privacy) { | ||
262 | return true; | ||
263 | } | ||
264 | return false; | ||
265 | } | ||
266 | |||
267 | /* | ||
268 | * This function checks if static WEP is enabled in driver and scanned network | ||
269 | * is compatible with it. | ||
270 | */ | ||
271 | static bool | ||
272 | mwifiex_is_network_compatible_for_static_wep(struct mwifiex_private *priv, | ||
273 | struct mwifiex_bssdescriptor *bss_desc) | ||
274 | { | ||
275 | if (priv->sec_info.wep_status == MWIFIEX_802_11_WEP_ENABLED | ||
276 | && !priv->sec_info.wpa_enabled && !priv->sec_info.wpa2_enabled | ||
277 | && bss_desc->privacy) { | ||
278 | return true; | ||
279 | } | ||
280 | return false; | ||
281 | } | ||
282 | |||
283 | /* | ||
284 | * This function checks if wpa is enabled in driver and scanned network is | ||
285 | * compatible with it. | ||
286 | */ | ||
287 | static bool | ||
288 | mwifiex_is_network_compatible_for_wpa(struct mwifiex_private *priv, | ||
289 | struct mwifiex_bssdescriptor *bss_desc, | ||
290 | int index) | ||
291 | { | ||
292 | if (priv->sec_info.wep_status == MWIFIEX_802_11_WEP_DISABLED | ||
293 | && priv->sec_info.wpa_enabled && !priv->sec_info.wpa2_enabled | ||
294 | && ((bss_desc->bcn_wpa_ie) && ((*(bss_desc->bcn_wpa_ie)).vend_hdr. | ||
295 | element_id == WLAN_EID_WPA)) | ||
296 | /* | ||
297 | * Privacy bit may NOT be set in some APs like | ||
298 | * LinkSys WRT54G && bss_desc->privacy | ||
299 | */ | ||
300 | ) { | ||
301 | dev_dbg(priv->adapter->dev, "info: %s: WPA: index=%d" | ||
302 | " wpa_ie=%#x wpa2_ie=%#x WEP=%s WPA=%s WPA2=%s " | ||
303 | "EncMode=%#x privacy=%#x\n", __func__, index, | ||
304 | (bss_desc->bcn_wpa_ie) ? | ||
305 | (*(bss_desc->bcn_wpa_ie)). | ||
306 | vend_hdr.element_id : 0, | ||
307 | (bss_desc->bcn_rsn_ie) ? | ||
308 | (*(bss_desc->bcn_rsn_ie)). | ||
309 | ieee_hdr.element_id : 0, | ||
310 | (priv->sec_info.wep_status == | ||
311 | MWIFIEX_802_11_WEP_ENABLED) ? "e" : "d", | ||
312 | (priv->sec_info.wpa_enabled) ? "e" : "d", | ||
313 | (priv->sec_info.wpa2_enabled) ? "e" : "d", | ||
314 | priv->sec_info.encryption_mode, | ||
315 | bss_desc->privacy); | ||
316 | return true; | ||
317 | } | ||
318 | return false; | ||
319 | } | ||
320 | |||
321 | /* | ||
322 | * This function checks if wpa2 is enabled in driver and scanned network is | ||
323 | * compatible with it. | ||
324 | */ | ||
325 | static bool | ||
326 | mwifiex_is_network_compatible_for_wpa2(struct mwifiex_private *priv, | ||
327 | struct mwifiex_bssdescriptor *bss_desc, | ||
328 | int index) | ||
329 | { | ||
330 | if (priv->sec_info.wep_status == MWIFIEX_802_11_WEP_DISABLED | ||
331 | && !priv->sec_info.wpa_enabled && priv->sec_info.wpa2_enabled | ||
332 | && ((bss_desc->bcn_rsn_ie) && ((*(bss_desc->bcn_rsn_ie)).ieee_hdr. | ||
333 | element_id == WLAN_EID_RSN)) | ||
334 | /* | ||
335 | * Privacy bit may NOT be set in some APs like | ||
336 | * LinkSys WRT54G && bss_desc->privacy | ||
337 | */ | ||
338 | ) { | ||
339 | dev_dbg(priv->adapter->dev, "info: %s: WPA2: index=%d" | ||
340 | " wpa_ie=%#x wpa2_ie=%#x WEP=%s WPA=%s WPA2=%s " | ||
341 | "EncMode=%#x privacy=%#x\n", __func__, index, | ||
342 | (bss_desc->bcn_wpa_ie) ? | ||
343 | (*(bss_desc->bcn_wpa_ie)). | ||
344 | vend_hdr.element_id : 0, | ||
345 | (bss_desc->bcn_rsn_ie) ? | ||
346 | (*(bss_desc->bcn_rsn_ie)). | ||
347 | ieee_hdr.element_id : 0, | ||
348 | (priv->sec_info.wep_status == | ||
349 | MWIFIEX_802_11_WEP_ENABLED) ? "e" : "d", | ||
350 | (priv->sec_info.wpa_enabled) ? "e" : "d", | ||
351 | (priv->sec_info.wpa2_enabled) ? "e" : "d", | ||
352 | priv->sec_info.encryption_mode, | ||
353 | bss_desc->privacy); | ||
354 | return true; | ||
355 | } | ||
356 | return false; | ||
357 | } | ||
358 | |||
359 | /* | ||
360 | * This function checks if adhoc AES is enabled in driver and scanned network is | ||
361 | * compatible with it. | ||
362 | */ | ||
363 | static bool | ||
364 | mwifiex_is_network_compatible_for_adhoc_aes(struct mwifiex_private *priv, | ||
365 | struct mwifiex_bssdescriptor *bss_desc) | ||
366 | { | ||
367 | if (priv->sec_info.wep_status == MWIFIEX_802_11_WEP_DISABLED | ||
368 | && !priv->sec_info.wpa_enabled && !priv->sec_info.wpa2_enabled | ||
369 | && ((!bss_desc->bcn_wpa_ie) || ((*(bss_desc->bcn_wpa_ie)).vend_hdr. | ||
370 | element_id != WLAN_EID_WPA)) | ||
371 | && ((!bss_desc->bcn_rsn_ie) || ((*(bss_desc->bcn_rsn_ie)).ieee_hdr. | ||
372 | element_id != WLAN_EID_RSN)) | ||
373 | && !priv->sec_info.encryption_mode | ||
374 | && bss_desc->privacy) { | ||
375 | return true; | ||
376 | } | ||
377 | return false; | ||
378 | } | ||
379 | |||
380 | /* | ||
381 | * This function checks if dynamic WEP is enabled in driver and scanned network | ||
382 | * is compatible with it. | ||
383 | */ | ||
384 | static bool | ||
385 | mwifiex_is_network_compatible_for_dynamic_wep(struct mwifiex_private *priv, | ||
386 | struct mwifiex_bssdescriptor *bss_desc, | ||
387 | int index) | ||
388 | { | ||
389 | if (priv->sec_info.wep_status == MWIFIEX_802_11_WEP_DISABLED | ||
390 | && !priv->sec_info.wpa_enabled && !priv->sec_info.wpa2_enabled | ||
391 | && ((!bss_desc->bcn_wpa_ie) || ((*(bss_desc->bcn_wpa_ie)).vend_hdr. | ||
392 | element_id != WLAN_EID_WPA)) | ||
393 | && ((!bss_desc->bcn_rsn_ie) || ((*(bss_desc->bcn_rsn_ie)).ieee_hdr. | ||
394 | element_id != WLAN_EID_RSN)) | ||
395 | && priv->sec_info.encryption_mode | ||
396 | && bss_desc->privacy) { | ||
397 | dev_dbg(priv->adapter->dev, "info: %s: dynamic " | ||
398 | "WEP: index=%d wpa_ie=%#x wpa2_ie=%#x " | ||
399 | "EncMode=%#x privacy=%#x\n", | ||
400 | __func__, index, | ||
401 | (bss_desc->bcn_wpa_ie) ? | ||
402 | (*(bss_desc->bcn_wpa_ie)). | ||
403 | vend_hdr.element_id : 0, | ||
404 | (bss_desc->bcn_rsn_ie) ? | ||
405 | (*(bss_desc->bcn_rsn_ie)). | ||
406 | ieee_hdr.element_id : 0, | ||
407 | priv->sec_info.encryption_mode, | ||
408 | bss_desc->privacy); | ||
409 | return true; | ||
410 | } | ||
411 | return false; | ||
412 | } | ||
413 | |||
414 | /* | ||
415 | * This function checks if a scanned network is compatible with the driver | ||
416 | * settings. | ||
417 | * | ||
418 | * WEP WPA WPA2 ad-hoc encrypt Network | ||
419 | * enabled enabled enabled AES mode Privacy WPA WPA2 Compatible | ||
420 | * 0 0 0 0 NONE 0 0 0 yes No security | ||
421 | * 0 1 0 0 x 1x 1 x yes WPA (disable | ||
422 | * HT if no AES) | ||
423 | * 0 0 1 0 x 1x x 1 yes WPA2 (disable | ||
424 | * HT if no AES) | ||
425 | * 0 0 0 1 NONE 1 0 0 yes Ad-hoc AES | ||
426 | * 1 0 0 0 NONE 1 0 0 yes Static WEP | ||
427 | * (disable HT) | ||
428 | * 0 0 0 0 !=NONE 1 0 0 yes Dynamic WEP | ||
429 | * | ||
430 | * Compatibility is not matched while roaming, except for mode. | ||
431 | */ | ||
432 | static s32 | ||
433 | mwifiex_is_network_compatible(struct mwifiex_private *priv, u32 index, u32 mode) | ||
434 | { | ||
435 | struct mwifiex_adapter *adapter = priv->adapter; | ||
436 | struct mwifiex_bssdescriptor *bss_desc; | ||
437 | |||
438 | bss_desc = &adapter->scan_table[index]; | ||
439 | bss_desc->disable_11n = false; | ||
440 | |||
441 | /* Don't check for compatibility if roaming */ | ||
442 | if (priv->media_connected && (priv->bss_mode == NL80211_IFTYPE_STATION) | ||
443 | && (bss_desc->bss_mode == NL80211_IFTYPE_STATION)) | ||
444 | return index; | ||
445 | |||
446 | if (priv->wps.session_enable) { | ||
447 | dev_dbg(adapter->dev, | ||
448 | "info: return success directly in WPS period\n"); | ||
449 | return index; | ||
450 | } | ||
451 | |||
452 | if (mwifiex_is_network_compatible_for_wapi(priv, bss_desc)) { | ||
453 | dev_dbg(adapter->dev, "info: return success for WAPI AP\n"); | ||
454 | return index; | ||
455 | } | ||
456 | |||
457 | if (bss_desc->bss_mode == mode) { | ||
458 | if (mwifiex_is_network_compatible_for_no_sec(priv, bss_desc)) { | ||
459 | /* No security */ | ||
460 | return index; | ||
461 | } else if (mwifiex_is_network_compatible_for_static_wep(priv, | ||
462 | bss_desc)) { | ||
463 | /* Static WEP enabled */ | ||
464 | dev_dbg(adapter->dev, "info: Disable 11n in WEP mode.\n"); | ||
465 | bss_desc->disable_11n = true; | ||
466 | return index; | ||
467 | } else if (mwifiex_is_network_compatible_for_wpa(priv, bss_desc, | ||
468 | index)) { | ||
469 | /* WPA enabled */ | ||
470 | if (((priv->adapter->config_bands & BAND_GN | ||
471 | || priv->adapter->config_bands & BAND_AN) | ||
472 | && bss_desc->bcn_ht_cap) | ||
473 | && !mwifiex_is_wpa_oui_present(bss_desc, | ||
474 | CIPHER_SUITE_CCMP)) { | ||
475 | |||
476 | if (mwifiex_is_wpa_oui_present(bss_desc, | ||
477 | CIPHER_SUITE_TKIP)) { | ||
478 | dev_dbg(adapter->dev, | ||
479 | "info: Disable 11n if AES " | ||
480 | "is not supported by AP\n"); | ||
481 | bss_desc->disable_11n = true; | ||
482 | } else { | ||
483 | return -1; | ||
484 | } | ||
485 | } | ||
486 | return index; | ||
487 | } else if (mwifiex_is_network_compatible_for_wpa2(priv, | ||
488 | bss_desc, index)) { | ||
489 | /* WPA2 enabled */ | ||
490 | if (((priv->adapter->config_bands & BAND_GN | ||
491 | || priv->adapter->config_bands & BAND_AN) | ||
492 | && bss_desc->bcn_ht_cap) | ||
493 | && !mwifiex_is_rsn_oui_present(bss_desc, | ||
494 | CIPHER_SUITE_CCMP)) { | ||
495 | |||
496 | if (mwifiex_is_rsn_oui_present(bss_desc, | ||
497 | CIPHER_SUITE_TKIP)) { | ||
498 | dev_dbg(adapter->dev, | ||
499 | "info: Disable 11n if AES " | ||
500 | "is not supported by AP\n"); | ||
501 | bss_desc->disable_11n = true; | ||
502 | } else { | ||
503 | return -1; | ||
504 | } | ||
505 | } | ||
506 | return index; | ||
507 | } else if (mwifiex_is_network_compatible_for_adhoc_aes(priv, | ||
508 | bss_desc)) { | ||
509 | /* Ad-hoc AES enabled */ | ||
510 | return index; | ||
511 | } else if (mwifiex_is_network_compatible_for_dynamic_wep(priv, | ||
512 | bss_desc, index)) { | ||
513 | /* Dynamic WEP enabled */ | ||
514 | return index; | ||
515 | } | ||
516 | |||
517 | /* Security doesn't match */ | ||
518 | dev_dbg(adapter->dev, "info: %s: failed: index=%d " | ||
519 | "wpa_ie=%#x wpa2_ie=%#x WEP=%s WPA=%s WPA2=%s EncMode" | ||
520 | "=%#x privacy=%#x\n", | ||
521 | __func__, index, | ||
522 | (bss_desc->bcn_wpa_ie) ? | ||
523 | (*(bss_desc->bcn_wpa_ie)).vend_hdr. | ||
524 | element_id : 0, | ||
525 | (bss_desc->bcn_rsn_ie) ? | ||
526 | (*(bss_desc->bcn_rsn_ie)).ieee_hdr. | ||
527 | element_id : 0, | ||
528 | (priv->sec_info.wep_status == | ||
529 | MWIFIEX_802_11_WEP_ENABLED) ? "e" : "d", | ||
530 | (priv->sec_info.wpa_enabled) ? "e" : "d", | ||
531 | (priv->sec_info.wpa2_enabled) ? "e" : "d", | ||
532 | priv->sec_info.encryption_mode, bss_desc->privacy); | ||
533 | return -1; | ||
534 | } | ||
535 | |||
536 | /* Mode doesn't match */ | ||
537 | return -1; | ||
538 | } | ||
539 | |||
540 | /* | ||
541 | * This function finds the best SSID in the scan list. | ||
542 | * | ||
543 | * It searches the scan table for the best SSID that also matches the current | ||
544 | * adapter network preference (mode, security etc.). | ||
545 | */ | ||
546 | static s32 | ||
547 | mwifiex_find_best_network_in_list(struct mwifiex_private *priv) | ||
548 | { | ||
549 | struct mwifiex_adapter *adapter = priv->adapter; | ||
550 | u32 mode = priv->bss_mode; | ||
551 | s32 best_net = -1; | ||
552 | s32 best_rssi = 0; | ||
553 | u32 i; | ||
554 | |||
555 | dev_dbg(adapter->dev, "info: num of BSSIDs = %d\n", | ||
556 | adapter->num_in_scan_table); | ||
557 | |||
558 | for (i = 0; i < adapter->num_in_scan_table; i++) { | ||
559 | switch (mode) { | ||
560 | case NL80211_IFTYPE_STATION: | ||
561 | case NL80211_IFTYPE_ADHOC: | ||
562 | if (mwifiex_is_network_compatible(priv, i, mode) >= 0) { | ||
563 | if (SCAN_RSSI(adapter->scan_table[i].rssi) > | ||
564 | best_rssi) { | ||
565 | best_rssi = SCAN_RSSI(adapter-> | ||
566 | scan_table[i].rssi); | ||
567 | best_net = i; | ||
568 | } | ||
569 | } | ||
570 | break; | ||
571 | case NL80211_IFTYPE_UNSPECIFIED: | ||
572 | default: | ||
573 | if (SCAN_RSSI(adapter->scan_table[i].rssi) > | ||
574 | best_rssi) { | ||
575 | best_rssi = SCAN_RSSI(adapter->scan_table[i]. | ||
576 | rssi); | ||
577 | best_net = i; | ||
578 | } | ||
579 | break; | ||
580 | } | ||
581 | } | ||
582 | |||
583 | return best_net; | ||
584 | } | ||
585 | |||
586 | /* | ||
587 | * This function creates a channel list for the driver to scan, based | ||
588 | * on region/band information. | ||
589 | * | ||
590 | * This routine is used for any scan that is not provided with a | ||
591 | * specific channel list to scan. | ||
592 | */ | ||
593 | static void | ||
594 | mwifiex_scan_create_channel_list(struct mwifiex_private *priv, | ||
595 | const struct mwifiex_user_scan_cfg | ||
596 | *user_scan_in, | ||
597 | struct mwifiex_chan_scan_param_set | ||
598 | *scan_chan_list, | ||
599 | u8 filtered_scan) | ||
600 | { | ||
601 | enum ieee80211_band band; | ||
602 | struct ieee80211_supported_band *sband; | ||
603 | struct ieee80211_channel *ch; | ||
604 | struct mwifiex_adapter *adapter = priv->adapter; | ||
605 | int chan_idx = 0, i; | ||
606 | u8 scan_type; | ||
607 | |||
608 | for (band = 0; (band < IEEE80211_NUM_BANDS) ; band++) { | ||
609 | |||
610 | if (!priv->wdev->wiphy->bands[band]) | ||
611 | continue; | ||
612 | |||
613 | sband = priv->wdev->wiphy->bands[band]; | ||
614 | |||
615 | for (i = 0; (i < sband->n_channels) ; i++, chan_idx++) { | ||
616 | ch = &sband->channels[i]; | ||
617 | if (ch->flags & IEEE80211_CHAN_DISABLED) | ||
618 | continue; | ||
619 | scan_chan_list[chan_idx].radio_type = band; | ||
620 | scan_type = ch->flags & IEEE80211_CHAN_PASSIVE_SCAN; | ||
621 | if (user_scan_in && | ||
622 | user_scan_in->chan_list[0].scan_time) | ||
623 | scan_chan_list[chan_idx].max_scan_time = | ||
624 | cpu_to_le16((u16) user_scan_in-> | ||
625 | chan_list[0].scan_time); | ||
626 | else if (scan_type == MWIFIEX_SCAN_TYPE_PASSIVE) | ||
627 | scan_chan_list[chan_idx].max_scan_time = | ||
628 | cpu_to_le16(adapter->passive_scan_time); | ||
629 | else | ||
630 | scan_chan_list[chan_idx].max_scan_time = | ||
631 | cpu_to_le16(adapter->active_scan_time); | ||
632 | if (scan_type == MWIFIEX_SCAN_TYPE_PASSIVE) | ||
633 | scan_chan_list[chan_idx].chan_scan_mode_bitmap | ||
634 | |= MWIFIEX_PASSIVE_SCAN; | ||
635 | else | ||
636 | scan_chan_list[chan_idx].chan_scan_mode_bitmap | ||
637 | &= ~MWIFIEX_PASSIVE_SCAN; | ||
638 | scan_chan_list[chan_idx].chan_number = | ||
639 | (u32) ch->hw_value; | ||
640 | if (filtered_scan) { | ||
641 | scan_chan_list[chan_idx].max_scan_time = | ||
642 | cpu_to_le16(adapter->specific_scan_time); | ||
643 | scan_chan_list[chan_idx].chan_scan_mode_bitmap | ||
644 | |= MWIFIEX_DISABLE_CHAN_FILT; | ||
645 | } | ||
646 | } | ||
647 | |||
648 | } | ||
649 | } | ||
650 | |||
651 | /* | ||
652 | * This function constructs and sends multiple scan config commands to | ||
653 | * the firmware. | ||
654 | * | ||
655 | * Previous routines in the code flow have created a scan command configuration | ||
656 | * with any requested TLVs. This function splits the channel TLV into maximum | ||
657 | * channels supported per scan lists and sends the portion of the channel TLV, | ||
658 | * along with the other TLVs, to the firmware. | ||
659 | */ | ||
660 | static int | ||
661 | mwifiex_scan_channel_list(struct mwifiex_private *priv, | ||
662 | u32 max_chan_per_scan, u8 filtered_scan, | ||
663 | struct mwifiex_scan_cmd_config *scan_cfg_out, | ||
664 | struct mwifiex_ie_types_chan_list_param_set | ||
665 | *chan_tlv_out, | ||
666 | struct mwifiex_chan_scan_param_set *scan_chan_list) | ||
667 | { | ||
668 | int ret = 0; | ||
669 | struct mwifiex_chan_scan_param_set *tmp_chan_list; | ||
670 | struct mwifiex_chan_scan_param_set *start_chan; | ||
671 | |||
672 | u32 tlv_idx; | ||
673 | u32 total_scan_time; | ||
674 | u32 done_early; | ||
675 | |||
676 | if (!scan_cfg_out || !chan_tlv_out || !scan_chan_list) { | ||
677 | dev_dbg(priv->adapter->dev, | ||
678 | "info: Scan: Null detect: %p, %p, %p\n", | ||
679 | scan_cfg_out, chan_tlv_out, scan_chan_list); | ||
680 | return -1; | ||
681 | } | ||
682 | |||
683 | chan_tlv_out->header.type = cpu_to_le16(TLV_TYPE_CHANLIST); | ||
684 | |||
685 | /* Set the temp channel struct pointer to the start of the desired | ||
686 | list */ | ||
687 | tmp_chan_list = scan_chan_list; | ||
688 | |||
689 | /* Loop through the desired channel list, sending a new firmware scan | ||
690 | commands for each max_chan_per_scan channels (or for 1,6,11 | ||
691 | individually if configured accordingly) */ | ||
692 | while (tmp_chan_list->chan_number) { | ||
693 | |||
694 | tlv_idx = 0; | ||
695 | total_scan_time = 0; | ||
696 | chan_tlv_out->header.len = 0; | ||
697 | start_chan = tmp_chan_list; | ||
698 | done_early = false; | ||
699 | |||
700 | /* | ||
701 | * Construct the Channel TLV for the scan command. Continue to | ||
702 | * insert channel TLVs until: | ||
703 | * - the tlv_idx hits the maximum configured per scan command | ||
704 | * - the next channel to insert is 0 (end of desired channel | ||
705 | * list) | ||
706 | * - done_early is set (controlling individual scanning of | ||
707 | * 1,6,11) | ||
708 | */ | ||
709 | while (tlv_idx < max_chan_per_scan | ||
710 | && tmp_chan_list->chan_number && !done_early) { | ||
711 | |||
712 | dev_dbg(priv->adapter->dev, | ||
713 | "info: Scan: Chan(%3d), Radio(%d)," | ||
714 | " Mode(%d, %d), Dur(%d)\n", | ||
715 | tmp_chan_list->chan_number, | ||
716 | tmp_chan_list->radio_type, | ||
717 | tmp_chan_list->chan_scan_mode_bitmap | ||
718 | & MWIFIEX_PASSIVE_SCAN, | ||
719 | (tmp_chan_list->chan_scan_mode_bitmap | ||
720 | & MWIFIEX_DISABLE_CHAN_FILT) >> 1, | ||
721 | le16_to_cpu(tmp_chan_list->max_scan_time)); | ||
722 | |||
723 | /* Copy the current channel TLV to the command being | ||
724 | prepared */ | ||
725 | memcpy(chan_tlv_out->chan_scan_param + tlv_idx, | ||
726 | tmp_chan_list, | ||
727 | sizeof(chan_tlv_out->chan_scan_param)); | ||
728 | |||
729 | /* Increment the TLV header length by the size | ||
730 | appended */ | ||
731 | chan_tlv_out->header.len = | ||
732 | cpu_to_le16(le16_to_cpu(chan_tlv_out->header.len) + | ||
733 | (sizeof(chan_tlv_out->chan_scan_param))); | ||
734 | |||
735 | /* | ||
736 | * The tlv buffer length is set to the number of bytes | ||
737 | * of the between the channel tlv pointer and the start | ||
738 | * of the tlv buffer. This compensates for any TLVs | ||
739 | * that were appended before the channel list. | ||
740 | */ | ||
741 | scan_cfg_out->tlv_buf_len = (u32) ((u8 *) chan_tlv_out - | ||
742 | scan_cfg_out->tlv_buf); | ||
743 | |||
744 | /* Add the size of the channel tlv header and the data | ||
745 | length */ | ||
746 | scan_cfg_out->tlv_buf_len += | ||
747 | (sizeof(chan_tlv_out->header) | ||
748 | + le16_to_cpu(chan_tlv_out->header.len)); | ||
749 | |||
750 | /* Increment the index to the channel tlv we are | ||
751 | constructing */ | ||
752 | tlv_idx++; | ||
753 | |||
754 | /* Count the total scan time per command */ | ||
755 | total_scan_time += | ||
756 | le16_to_cpu(tmp_chan_list->max_scan_time); | ||
757 | |||
758 | done_early = false; | ||
759 | |||
760 | /* Stop the loop if the *current* channel is in the | ||
761 | 1,6,11 set and we are not filtering on a BSSID | ||
762 | or SSID. */ | ||
763 | if (!filtered_scan && (tmp_chan_list->chan_number == 1 | ||
764 | || tmp_chan_list->chan_number == 6 | ||
765 | || tmp_chan_list->chan_number == 11)) | ||
766 | done_early = true; | ||
767 | |||
768 | /* Increment the tmp pointer to the next channel to | ||
769 | be scanned */ | ||
770 | tmp_chan_list++; | ||
771 | |||
772 | /* Stop the loop if the *next* channel is in the 1,6,11 | ||
773 | set. This will cause it to be the only channel | ||
774 | scanned on the next interation */ | ||
775 | if (!filtered_scan && (tmp_chan_list->chan_number == 1 | ||
776 | || tmp_chan_list->chan_number == 6 | ||
777 | || tmp_chan_list->chan_number == 11)) | ||
778 | done_early = true; | ||
779 | } | ||
780 | |||
781 | /* The total scan time should be less than scan command timeout | ||
782 | value */ | ||
783 | if (total_scan_time > MWIFIEX_MAX_TOTAL_SCAN_TIME) { | ||
784 | dev_err(priv->adapter->dev, "total scan time %dms" | ||
785 | " is over limit (%dms), scan skipped\n", | ||
786 | total_scan_time, MWIFIEX_MAX_TOTAL_SCAN_TIME); | ||
787 | ret = -1; | ||
788 | break; | ||
789 | } | ||
790 | |||
791 | priv->adapter->scan_channels = start_chan; | ||
792 | |||
793 | /* Send the scan command to the firmware with the specified | ||
794 | cfg */ | ||
795 | ret = mwifiex_send_cmd_async(priv, HostCmd_CMD_802_11_SCAN, | ||
796 | HostCmd_ACT_GEN_SET, 0, | ||
797 | scan_cfg_out); | ||
798 | if (ret) | ||
799 | break; | ||
800 | } | ||
801 | |||
802 | if (ret) | ||
803 | return -1; | ||
804 | |||
805 | return 0; | ||
806 | } | ||
807 | |||
808 | /* | ||
809 | * This function constructs a scan command configuration structure to use | ||
810 | * in scan commands. | ||
811 | * | ||
812 | * Application layer or other functions can invoke network scanning | ||
813 | * with a scan configuration supplied in a user scan configuration structure. | ||
814 | * This structure is used as the basis of one or many scan command configuration | ||
815 | * commands that are sent to the command processing module and eventually to the | ||
816 | * firmware. | ||
817 | * | ||
818 | * This function creates a scan command configuration structure based on the | ||
819 | * following user supplied parameters (if present): | ||
820 | * - SSID filter | ||
821 | * - BSSID filter | ||
822 | * - Number of Probes to be sent | ||
823 | * - Channel list | ||
824 | * | ||
825 | * If the SSID or BSSID filter is not present, the filter is disabled/cleared. | ||
826 | * If the number of probes is not set, adapter default setting is used. | ||
827 | */ | ||
828 | static void | ||
829 | mwifiex_scan_setup_scan_config(struct mwifiex_private *priv, | ||
830 | const struct mwifiex_user_scan_cfg *user_scan_in, | ||
831 | struct mwifiex_scan_cmd_config *scan_cfg_out, | ||
832 | struct mwifiex_ie_types_chan_list_param_set | ||
833 | **chan_list_out, | ||
834 | struct mwifiex_chan_scan_param_set | ||
835 | *scan_chan_list, | ||
836 | u8 *max_chan_per_scan, u8 *filtered_scan, | ||
837 | u8 *scan_current_only) | ||
838 | { | ||
839 | struct mwifiex_adapter *adapter = priv->adapter; | ||
840 | struct mwifiex_ie_types_num_probes *num_probes_tlv; | ||
841 | struct mwifiex_ie_types_wildcard_ssid_params *wildcard_ssid_tlv; | ||
842 | struct mwifiex_ie_types_rates_param_set *rates_tlv; | ||
843 | const u8 zero_mac[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 }; | ||
844 | u8 *tlv_pos; | ||
845 | u32 num_probes; | ||
846 | u32 ssid_len; | ||
847 | u32 chan_idx; | ||
848 | u32 scan_type; | ||
849 | u16 scan_dur; | ||
850 | u8 channel; | ||
851 | u8 radio_type; | ||
852 | u32 ssid_idx; | ||
853 | u8 ssid_filter; | ||
854 | u8 rates[MWIFIEX_SUPPORTED_RATES]; | ||
855 | u32 rates_size; | ||
856 | struct mwifiex_ie_types_htcap *ht_cap; | ||
857 | |||
858 | /* The tlv_buf_len is calculated for each scan command. The TLVs added | ||
859 | in this routine will be preserved since the routine that sends the | ||
860 | command will append channelTLVs at *chan_list_out. The difference | ||
861 | between the *chan_list_out and the tlv_buf start will be used to | ||
862 | calculate the size of anything we add in this routine. */ | ||
863 | scan_cfg_out->tlv_buf_len = 0; | ||
864 | |||
865 | /* Running tlv pointer. Assigned to chan_list_out at end of function | ||
866 | so later routines know where channels can be added to the command | ||
867 | buf */ | ||
868 | tlv_pos = scan_cfg_out->tlv_buf; | ||
869 | |||
870 | /* Initialize the scan as un-filtered; the flag is later set to TRUE | ||
871 | below if a SSID or BSSID filter is sent in the command */ | ||
872 | *filtered_scan = false; | ||
873 | |||
874 | /* Initialize the scan as not being only on the current channel. If | ||
875 | the channel list is customized, only contains one channel, and is | ||
876 | the active channel, this is set true and data flow is not halted. */ | ||
877 | *scan_current_only = false; | ||
878 | |||
879 | if (user_scan_in) { | ||
880 | |||
881 | /* Default the ssid_filter flag to TRUE, set false under | ||
882 | certain wildcard conditions and qualified by the existence | ||
883 | of an SSID list before marking the scan as filtered */ | ||
884 | ssid_filter = true; | ||
885 | |||
886 | /* Set the BSS type scan filter, use Adapter setting if | ||
887 | unset */ | ||
888 | scan_cfg_out->bss_mode = | ||
889 | (user_scan_in->bss_mode ? (u8) user_scan_in-> | ||
890 | bss_mode : (u8) adapter->scan_mode); | ||
891 | |||
892 | /* Set the number of probes to send, use Adapter setting | ||
893 | if unset */ | ||
894 | num_probes = | ||
895 | (user_scan_in->num_probes ? user_scan_in-> | ||
896 | num_probes : adapter->scan_probes); | ||
897 | |||
898 | /* | ||
899 | * Set the BSSID filter to the incoming configuration, | ||
900 | * if non-zero. If not set, it will remain disabled | ||
901 | * (all zeros). | ||
902 | */ | ||
903 | memcpy(scan_cfg_out->specific_bssid, | ||
904 | user_scan_in->specific_bssid, | ||
905 | sizeof(scan_cfg_out->specific_bssid)); | ||
906 | |||
907 | for (ssid_idx = 0; | ||
908 | ((ssid_idx < ARRAY_SIZE(user_scan_in->ssid_list)) | ||
909 | && (*user_scan_in->ssid_list[ssid_idx].ssid | ||
910 | || user_scan_in->ssid_list[ssid_idx].max_len)); | ||
911 | ssid_idx++) { | ||
912 | |||
913 | ssid_len = strlen(user_scan_in->ssid_list[ssid_idx]. | ||
914 | ssid) + 1; | ||
915 | |||
916 | wildcard_ssid_tlv = | ||
917 | (struct mwifiex_ie_types_wildcard_ssid_params *) | ||
918 | tlv_pos; | ||
919 | wildcard_ssid_tlv->header.type = | ||
920 | cpu_to_le16(TLV_TYPE_WILDCARDSSID); | ||
921 | wildcard_ssid_tlv->header.len = cpu_to_le16( | ||
922 | (u16) (ssid_len + sizeof(wildcard_ssid_tlv-> | ||
923 | max_ssid_length))); | ||
924 | wildcard_ssid_tlv->max_ssid_length = | ||
925 | user_scan_in->ssid_list[ssid_idx].max_len; | ||
926 | |||
927 | memcpy(wildcard_ssid_tlv->ssid, | ||
928 | user_scan_in->ssid_list[ssid_idx].ssid, | ||
929 | ssid_len); | ||
930 | |||
931 | tlv_pos += (sizeof(wildcard_ssid_tlv->header) | ||
932 | + le16_to_cpu(wildcard_ssid_tlv->header.len)); | ||
933 | |||
934 | dev_dbg(adapter->dev, "info: scan: ssid_list[%d]: %s, %d\n", | ||
935 | ssid_idx, wildcard_ssid_tlv->ssid, | ||
936 | wildcard_ssid_tlv->max_ssid_length); | ||
937 | |||
938 | /* Empty wildcard ssid with a maxlen will match many or | ||
939 | potentially all SSIDs (maxlen == 32), therefore do | ||
940 | not treat the scan as | ||
941 | filtered. */ | ||
942 | if (!ssid_len && wildcard_ssid_tlv->max_ssid_length) | ||
943 | ssid_filter = false; | ||
944 | |||
945 | } | ||
946 | |||
947 | /* | ||
948 | * The default number of channels sent in the command is low to | ||
949 | * ensure the response buffer from the firmware does not | ||
950 | * truncate scan results. That is not an issue with an SSID | ||
951 | * or BSSID filter applied to the scan results in the firmware. | ||
952 | */ | ||
953 | if ((ssid_idx && ssid_filter) | ||
954 | || memcmp(scan_cfg_out->specific_bssid, &zero_mac, | ||
955 | sizeof(zero_mac))) | ||
956 | *filtered_scan = true; | ||
957 | } else { | ||
958 | scan_cfg_out->bss_mode = (u8) adapter->scan_mode; | ||
959 | num_probes = adapter->scan_probes; | ||
960 | } | ||
961 | |||
962 | /* | ||
963 | * If a specific BSSID or SSID is used, the number of channels in the | ||
964 | * scan command will be increased to the absolute maximum. | ||
965 | */ | ||
966 | if (*filtered_scan) | ||
967 | *max_chan_per_scan = MWIFIEX_MAX_CHANNELS_PER_SPECIFIC_SCAN; | ||
968 | else | ||
969 | *max_chan_per_scan = MWIFIEX_CHANNELS_PER_SCAN_CMD; | ||
970 | |||
971 | /* If the input config or adapter has the number of Probes set, | ||
972 | add tlv */ | ||
973 | if (num_probes) { | ||
974 | |||
975 | dev_dbg(adapter->dev, "info: scan: num_probes = %d\n", | ||
976 | num_probes); | ||
977 | |||
978 | num_probes_tlv = (struct mwifiex_ie_types_num_probes *) tlv_pos; | ||
979 | num_probes_tlv->header.type = cpu_to_le16(TLV_TYPE_NUMPROBES); | ||
980 | num_probes_tlv->header.len = | ||
981 | cpu_to_le16(sizeof(num_probes_tlv->num_probes)); | ||
982 | num_probes_tlv->num_probes = cpu_to_le16((u16) num_probes); | ||
983 | |||
984 | tlv_pos += sizeof(num_probes_tlv->header) + | ||
985 | le16_to_cpu(num_probes_tlv->header.len); | ||
986 | |||
987 | } | ||
988 | |||
989 | /* Append rates tlv */ | ||
990 | memset(rates, 0, sizeof(rates)); | ||
991 | |||
992 | rates_size = mwifiex_get_supported_rates(priv, rates); | ||
993 | |||
994 | rates_tlv = (struct mwifiex_ie_types_rates_param_set *) tlv_pos; | ||
995 | rates_tlv->header.type = cpu_to_le16(WLAN_EID_SUPP_RATES); | ||
996 | rates_tlv->header.len = cpu_to_le16((u16) rates_size); | ||
997 | memcpy(rates_tlv->rates, rates, rates_size); | ||
998 | tlv_pos += sizeof(rates_tlv->header) + rates_size; | ||
999 | |||
1000 | dev_dbg(adapter->dev, "info: SCAN_CMD: Rates size = %d\n", rates_size); | ||
1001 | |||
1002 | if (ISSUPP_11NENABLED(priv->adapter->fw_cap_info) | ||
1003 | && (priv->adapter->config_bands & BAND_GN | ||
1004 | || priv->adapter->config_bands & BAND_AN)) { | ||
1005 | ht_cap = (struct mwifiex_ie_types_htcap *) tlv_pos; | ||
1006 | memset(ht_cap, 0, sizeof(struct mwifiex_ie_types_htcap)); | ||
1007 | ht_cap->header.type = cpu_to_le16(WLAN_EID_HT_CAPABILITY); | ||
1008 | ht_cap->header.len = | ||
1009 | cpu_to_le16(sizeof(struct ieee80211_ht_cap)); | ||
1010 | radio_type = | ||
1011 | mwifiex_band_to_radio_type(priv->adapter->config_bands); | ||
1012 | mwifiex_fill_cap_info(priv, radio_type, ht_cap); | ||
1013 | tlv_pos += sizeof(struct mwifiex_ie_types_htcap); | ||
1014 | } | ||
1015 | |||
1016 | /* Append vendor specific IE TLV */ | ||
1017 | mwifiex_cmd_append_vsie_tlv(priv, MWIFIEX_VSIE_MASK_SCAN, &tlv_pos); | ||
1018 | |||
1019 | /* | ||
1020 | * Set the output for the channel TLV to the address in the tlv buffer | ||
1021 | * past any TLVs that were added in this function (SSID, num_probes). | ||
1022 | * Channel TLVs will be added past this for each scan command, | ||
1023 | * preserving the TLVs that were previously added. | ||
1024 | */ | ||
1025 | *chan_list_out = | ||
1026 | (struct mwifiex_ie_types_chan_list_param_set *) tlv_pos; | ||
1027 | |||
1028 | if (user_scan_in && user_scan_in->chan_list[0].chan_number) { | ||
1029 | |||
1030 | dev_dbg(adapter->dev, "info: Scan: Using supplied channel list\n"); | ||
1031 | |||
1032 | for (chan_idx = 0; | ||
1033 | chan_idx < MWIFIEX_USER_SCAN_CHAN_MAX | ||
1034 | && user_scan_in->chan_list[chan_idx].chan_number; | ||
1035 | chan_idx++) { | ||
1036 | |||
1037 | channel = user_scan_in->chan_list[chan_idx].chan_number; | ||
1038 | (scan_chan_list + chan_idx)->chan_number = channel; | ||
1039 | |||
1040 | radio_type = | ||
1041 | user_scan_in->chan_list[chan_idx].radio_type; | ||
1042 | (scan_chan_list + chan_idx)->radio_type = radio_type; | ||
1043 | |||
1044 | scan_type = user_scan_in->chan_list[chan_idx].scan_type; | ||
1045 | |||
1046 | if (scan_type == MWIFIEX_SCAN_TYPE_PASSIVE) | ||
1047 | (scan_chan_list + | ||
1048 | chan_idx)->chan_scan_mode_bitmap | ||
1049 | |= MWIFIEX_PASSIVE_SCAN; | ||
1050 | else | ||
1051 | (scan_chan_list + | ||
1052 | chan_idx)->chan_scan_mode_bitmap | ||
1053 | &= ~MWIFIEX_PASSIVE_SCAN; | ||
1054 | |||
1055 | if (user_scan_in->chan_list[chan_idx].scan_time) { | ||
1056 | scan_dur = (u16) user_scan_in-> | ||
1057 | chan_list[chan_idx].scan_time; | ||
1058 | } else { | ||
1059 | if (scan_type == MWIFIEX_SCAN_TYPE_PASSIVE) | ||
1060 | scan_dur = adapter->passive_scan_time; | ||
1061 | else if (*filtered_scan) | ||
1062 | scan_dur = adapter->specific_scan_time; | ||
1063 | else | ||
1064 | scan_dur = adapter->active_scan_time; | ||
1065 | } | ||
1066 | |||
1067 | (scan_chan_list + chan_idx)->min_scan_time = | ||
1068 | cpu_to_le16(scan_dur); | ||
1069 | (scan_chan_list + chan_idx)->max_scan_time = | ||
1070 | cpu_to_le16(scan_dur); | ||
1071 | } | ||
1072 | |||
1073 | /* Check if we are only scanning the current channel */ | ||
1074 | if ((chan_idx == 1) | ||
1075 | && (user_scan_in->chan_list[0].chan_number | ||
1076 | == priv->curr_bss_params.bss_descriptor.channel)) { | ||
1077 | *scan_current_only = true; | ||
1078 | dev_dbg(adapter->dev, | ||
1079 | "info: Scan: Scanning current channel only\n"); | ||
1080 | } | ||
1081 | |||
1082 | } else { | ||
1083 | dev_dbg(adapter->dev, | ||
1084 | "info: Scan: Creating full region channel list\n"); | ||
1085 | mwifiex_scan_create_channel_list(priv, user_scan_in, | ||
1086 | scan_chan_list, | ||
1087 | *filtered_scan); | ||
1088 | } | ||
1089 | } | ||
1090 | |||
1091 | /* | ||
1092 | * This function inspects the scan response buffer for pointers to | ||
1093 | * expected TLVs. | ||
1094 | * | ||
1095 | * TLVs can be included at the end of the scan response BSS information. | ||
1096 | * | ||
1097 | * Data in the buffer is parsed pointers to TLVs that can potentially | ||
1098 | * be passed back in the response. | ||
1099 | */ | ||
1100 | static void | ||
1101 | mwifiex_ret_802_11_scan_get_tlv_ptrs(struct mwifiex_adapter *adapter, | ||
1102 | struct mwifiex_ie_types_data *tlv, | ||
1103 | u32 tlv_buf_size, u32 req_tlv_type, | ||
1104 | struct mwifiex_ie_types_data **tlv_data) | ||
1105 | { | ||
1106 | struct mwifiex_ie_types_data *current_tlv; | ||
1107 | u32 tlv_buf_left; | ||
1108 | u32 tlv_type; | ||
1109 | u32 tlv_len; | ||
1110 | |||
1111 | current_tlv = tlv; | ||
1112 | tlv_buf_left = tlv_buf_size; | ||
1113 | *tlv_data = NULL; | ||
1114 | |||
1115 | dev_dbg(adapter->dev, "info: SCAN_RESP: tlv_buf_size = %d\n", | ||
1116 | tlv_buf_size); | ||
1117 | |||
1118 | while (tlv_buf_left >= sizeof(struct mwifiex_ie_types_header)) { | ||
1119 | |||
1120 | tlv_type = le16_to_cpu(current_tlv->header.type); | ||
1121 | tlv_len = le16_to_cpu(current_tlv->header.len); | ||
1122 | |||
1123 | if (sizeof(tlv->header) + tlv_len > tlv_buf_left) { | ||
1124 | dev_err(adapter->dev, "SCAN_RESP: TLV buffer corrupt\n"); | ||
1125 | break; | ||
1126 | } | ||
1127 | |||
1128 | if (req_tlv_type == tlv_type) { | ||
1129 | switch (tlv_type) { | ||
1130 | case TLV_TYPE_TSFTIMESTAMP: | ||
1131 | dev_dbg(adapter->dev, "info: SCAN_RESP: TSF " | ||
1132 | "timestamp TLV, len = %d\n", tlv_len); | ||
1133 | *tlv_data = (struct mwifiex_ie_types_data *) | ||
1134 | current_tlv; | ||
1135 | break; | ||
1136 | case TLV_TYPE_CHANNELBANDLIST: | ||
1137 | dev_dbg(adapter->dev, "info: SCAN_RESP: channel" | ||
1138 | " band list TLV, len = %d\n", tlv_len); | ||
1139 | *tlv_data = (struct mwifiex_ie_types_data *) | ||
1140 | current_tlv; | ||
1141 | break; | ||
1142 | default: | ||
1143 | dev_err(adapter->dev, | ||
1144 | "SCAN_RESP: unhandled TLV = %d\n", | ||
1145 | tlv_type); | ||
1146 | /* Give up, this seems corrupted */ | ||
1147 | return; | ||
1148 | } | ||
1149 | } | ||
1150 | |||
1151 | if (*tlv_data) | ||
1152 | break; | ||
1153 | |||
1154 | |||
1155 | tlv_buf_left -= (sizeof(tlv->header) + tlv_len); | ||
1156 | current_tlv = | ||
1157 | (struct mwifiex_ie_types_data *) (current_tlv->data + | ||
1158 | tlv_len); | ||
1159 | |||
1160 | } /* while */ | ||
1161 | } | ||
1162 | |||
1163 | /* | ||
1164 | * This function interprets a BSS scan response returned from the firmware. | ||
1165 | * | ||
1166 | * The various fixed fields and IEs are parsed and passed back for a BSS | ||
1167 | * probe response or beacon from scan command. Information is recorded as | ||
1168 | * needed in the scan table for that entry. | ||
1169 | * | ||
1170 | * The following IE types are recognized and parsed - | ||
1171 | * - SSID | ||
1172 | * - Supported rates | ||
1173 | * - FH parameters set | ||
1174 | * - DS parameters set | ||
1175 | * - CF parameters set | ||
1176 | * - IBSS parameters set | ||
1177 | * - ERP information | ||
1178 | * - Extended supported rates | ||
1179 | * - Vendor specific (221) | ||
1180 | * - RSN IE | ||
1181 | * - WAPI IE | ||
1182 | * - HT capability | ||
1183 | * - HT operation | ||
1184 | * - BSS Coexistence 20/40 | ||
1185 | * - Extended capability | ||
1186 | * - Overlapping BSS scan parameters | ||
1187 | */ | ||
1188 | static int | ||
1189 | mwifiex_interpret_bss_desc_with_ie(struct mwifiex_adapter *adapter, | ||
1190 | struct mwifiex_bssdescriptor *bss_entry, | ||
1191 | u8 **beacon_info, u32 *bytes_left) | ||
1192 | { | ||
1193 | int ret = 0; | ||
1194 | u8 element_id; | ||
1195 | struct ieee_types_fh_param_set *fh_param_set; | ||
1196 | struct ieee_types_ds_param_set *ds_param_set; | ||
1197 | struct ieee_types_cf_param_set *cf_param_set; | ||
1198 | struct ieee_types_ibss_param_set *ibss_param_set; | ||
1199 | __le16 beacon_interval; | ||
1200 | __le16 capabilities; | ||
1201 | u8 *current_ptr; | ||
1202 | u8 *rate; | ||
1203 | u8 element_len; | ||
1204 | u16 total_ie_len; | ||
1205 | u8 bytes_to_copy; | ||
1206 | u8 rate_size; | ||
1207 | u16 beacon_size; | ||
1208 | u8 found_data_rate_ie; | ||
1209 | u32 bytes_left_for_current_beacon; | ||
1210 | struct ieee_types_vendor_specific *vendor_ie; | ||
1211 | const u8 wpa_oui[4] = { 0x00, 0x50, 0xf2, 0x01 }; | ||
1212 | const u8 wmm_oui[4] = { 0x00, 0x50, 0xf2, 0x02 }; | ||
1213 | |||
1214 | found_data_rate_ie = false; | ||
1215 | rate_size = 0; | ||
1216 | beacon_size = 0; | ||
1217 | |||
1218 | if (*bytes_left >= sizeof(beacon_size)) { | ||
1219 | /* Extract & convert beacon size from the command buffer */ | ||
1220 | memcpy(&beacon_size, *beacon_info, sizeof(beacon_size)); | ||
1221 | *bytes_left -= sizeof(beacon_size); | ||
1222 | *beacon_info += sizeof(beacon_size); | ||
1223 | } | ||
1224 | |||
1225 | if (!beacon_size || beacon_size > *bytes_left) { | ||
1226 | *beacon_info += *bytes_left; | ||
1227 | *bytes_left = 0; | ||
1228 | return -1; | ||
1229 | } | ||
1230 | |||
1231 | /* Initialize the current working beacon pointer for this BSS | ||
1232 | iteration */ | ||
1233 | current_ptr = *beacon_info; | ||
1234 | |||
1235 | /* Advance the return beacon pointer past the current beacon */ | ||
1236 | *beacon_info += beacon_size; | ||
1237 | *bytes_left -= beacon_size; | ||
1238 | |||
1239 | bytes_left_for_current_beacon = beacon_size; | ||
1240 | |||
1241 | memcpy(bss_entry->mac_address, current_ptr, ETH_ALEN); | ||
1242 | dev_dbg(adapter->dev, "info: InterpretIE: AP MAC Addr: %pM\n", | ||
1243 | bss_entry->mac_address); | ||
1244 | |||
1245 | current_ptr += ETH_ALEN; | ||
1246 | bytes_left_for_current_beacon -= ETH_ALEN; | ||
1247 | |||
1248 | if (bytes_left_for_current_beacon < 12) { | ||
1249 | dev_err(adapter->dev, "InterpretIE: not enough bytes left\n"); | ||
1250 | return -1; | ||
1251 | } | ||
1252 | |||
1253 | /* | ||
1254 | * Next 4 fields are RSSI, time stamp, beacon interval, | ||
1255 | * and capability information | ||
1256 | */ | ||
1257 | |||
1258 | /* RSSI is 1 byte long */ | ||
1259 | bss_entry->rssi = (s32) (*current_ptr); | ||
1260 | dev_dbg(adapter->dev, "info: InterpretIE: RSSI=%02X\n", *current_ptr); | ||
1261 | current_ptr += 1; | ||
1262 | bytes_left_for_current_beacon -= 1; | ||
1263 | |||
1264 | /* | ||
1265 | * The RSSI is not part of the beacon/probe response. After we have | ||
1266 | * advanced current_ptr past the RSSI field, save the remaining | ||
1267 | * data for use at the application layer | ||
1268 | */ | ||
1269 | bss_entry->beacon_buf = current_ptr; | ||
1270 | bss_entry->beacon_buf_size = bytes_left_for_current_beacon; | ||
1271 | |||
1272 | /* Time stamp is 8 bytes long */ | ||
1273 | memcpy(bss_entry->time_stamp, current_ptr, 8); | ||
1274 | current_ptr += 8; | ||
1275 | bytes_left_for_current_beacon -= 8; | ||
1276 | |||
1277 | /* Beacon interval is 2 bytes long */ | ||
1278 | memcpy(&beacon_interval, current_ptr, 2); | ||
1279 | bss_entry->beacon_period = le16_to_cpu(beacon_interval); | ||
1280 | current_ptr += 2; | ||
1281 | bytes_left_for_current_beacon -= 2; | ||
1282 | |||
1283 | /* Capability information is 2 bytes long */ | ||
1284 | memcpy(&capabilities, current_ptr, 2); | ||
1285 | dev_dbg(adapter->dev, "info: InterpretIE: capabilities=0x%X\n", | ||
1286 | capabilities); | ||
1287 | bss_entry->cap_info_bitmap = le16_to_cpu(capabilities); | ||
1288 | current_ptr += 2; | ||
1289 | bytes_left_for_current_beacon -= 2; | ||
1290 | |||
1291 | /* Rest of the current buffer are IE's */ | ||
1292 | dev_dbg(adapter->dev, "info: InterpretIE: IELength for this AP = %d\n", | ||
1293 | bytes_left_for_current_beacon); | ||
1294 | |||
1295 | if (bss_entry->cap_info_bitmap & WLAN_CAPABILITY_PRIVACY) { | ||
1296 | dev_dbg(adapter->dev, "info: InterpretIE: AP WEP enabled\n"); | ||
1297 | bss_entry->privacy = MWIFIEX_802_11_PRIV_FILTER_8021X_WEP; | ||
1298 | } else { | ||
1299 | bss_entry->privacy = MWIFIEX_802_11_PRIV_FILTER_ACCEPT_ALL; | ||
1300 | } | ||
1301 | |||
1302 | if (bss_entry->cap_info_bitmap & WLAN_CAPABILITY_IBSS) | ||
1303 | bss_entry->bss_mode = NL80211_IFTYPE_ADHOC; | ||
1304 | else | ||
1305 | bss_entry->bss_mode = NL80211_IFTYPE_STATION; | ||
1306 | |||
1307 | |||
1308 | /* Process variable IE */ | ||
1309 | while (bytes_left_for_current_beacon >= 2) { | ||
1310 | element_id = *current_ptr; | ||
1311 | element_len = *(current_ptr + 1); | ||
1312 | total_ie_len = element_len + sizeof(struct ieee_types_header); | ||
1313 | |||
1314 | if (bytes_left_for_current_beacon < total_ie_len) { | ||
1315 | dev_err(adapter->dev, "err: InterpretIE: in processing" | ||
1316 | " IE, bytes left < IE length\n"); | ||
1317 | bytes_left_for_current_beacon = 0; | ||
1318 | ret = -1; | ||
1319 | continue; | ||
1320 | } | ||
1321 | switch (element_id) { | ||
1322 | case WLAN_EID_SSID: | ||
1323 | bss_entry->ssid.ssid_len = element_len; | ||
1324 | memcpy(bss_entry->ssid.ssid, (current_ptr + 2), | ||
1325 | element_len); | ||
1326 | dev_dbg(adapter->dev, "info: InterpretIE: ssid: %-32s\n", | ||
1327 | bss_entry->ssid.ssid); | ||
1328 | break; | ||
1329 | |||
1330 | case WLAN_EID_SUPP_RATES: | ||
1331 | memcpy(bss_entry->data_rates, current_ptr + 2, | ||
1332 | element_len); | ||
1333 | memcpy(bss_entry->supported_rates, current_ptr + 2, | ||
1334 | element_len); | ||
1335 | rate_size = element_len; | ||
1336 | found_data_rate_ie = true; | ||
1337 | break; | ||
1338 | |||
1339 | case WLAN_EID_FH_PARAMS: | ||
1340 | fh_param_set = | ||
1341 | (struct ieee_types_fh_param_set *) current_ptr; | ||
1342 | memcpy(&bss_entry->phy_param_set.fh_param_set, | ||
1343 | fh_param_set, | ||
1344 | sizeof(struct ieee_types_fh_param_set)); | ||
1345 | break; | ||
1346 | |||
1347 | case WLAN_EID_DS_PARAMS: | ||
1348 | ds_param_set = | ||
1349 | (struct ieee_types_ds_param_set *) current_ptr; | ||
1350 | |||
1351 | bss_entry->channel = ds_param_set->current_chan; | ||
1352 | |||
1353 | memcpy(&bss_entry->phy_param_set.ds_param_set, | ||
1354 | ds_param_set, | ||
1355 | sizeof(struct ieee_types_ds_param_set)); | ||
1356 | break; | ||
1357 | |||
1358 | case WLAN_EID_CF_PARAMS: | ||
1359 | cf_param_set = | ||
1360 | (struct ieee_types_cf_param_set *) current_ptr; | ||
1361 | memcpy(&bss_entry->ss_param_set.cf_param_set, | ||
1362 | cf_param_set, | ||
1363 | sizeof(struct ieee_types_cf_param_set)); | ||
1364 | break; | ||
1365 | |||
1366 | case WLAN_EID_IBSS_PARAMS: | ||
1367 | ibss_param_set = | ||
1368 | (struct ieee_types_ibss_param_set *) | ||
1369 | current_ptr; | ||
1370 | memcpy(&bss_entry->ss_param_set.ibss_param_set, | ||
1371 | ibss_param_set, | ||
1372 | sizeof(struct ieee_types_ibss_param_set)); | ||
1373 | break; | ||
1374 | |||
1375 | case WLAN_EID_ERP_INFO: | ||
1376 | bss_entry->erp_flags = *(current_ptr + 2); | ||
1377 | break; | ||
1378 | |||
1379 | case WLAN_EID_EXT_SUPP_RATES: | ||
1380 | /* | ||
1381 | * Only process extended supported rate | ||
1382 | * if data rate is already found. | ||
1383 | * Data rate IE should come before | ||
1384 | * extended supported rate IE | ||
1385 | */ | ||
1386 | if (found_data_rate_ie) { | ||
1387 | if ((element_len + rate_size) > | ||
1388 | MWIFIEX_SUPPORTED_RATES) | ||
1389 | bytes_to_copy = | ||
1390 | (MWIFIEX_SUPPORTED_RATES - | ||
1391 | rate_size); | ||
1392 | else | ||
1393 | bytes_to_copy = element_len; | ||
1394 | |||
1395 | rate = (u8 *) bss_entry->data_rates; | ||
1396 | rate += rate_size; | ||
1397 | memcpy(rate, current_ptr + 2, bytes_to_copy); | ||
1398 | |||
1399 | rate = (u8 *) bss_entry->supported_rates; | ||
1400 | rate += rate_size; | ||
1401 | memcpy(rate, current_ptr + 2, bytes_to_copy); | ||
1402 | } | ||
1403 | break; | ||
1404 | |||
1405 | case WLAN_EID_VENDOR_SPECIFIC: | ||
1406 | vendor_ie = (struct ieee_types_vendor_specific *) | ||
1407 | current_ptr; | ||
1408 | |||
1409 | if (!memcmp | ||
1410 | (vendor_ie->vend_hdr.oui, wpa_oui, | ||
1411 | sizeof(wpa_oui))) { | ||
1412 | bss_entry->bcn_wpa_ie = | ||
1413 | (struct ieee_types_vendor_specific *) | ||
1414 | current_ptr; | ||
1415 | bss_entry->wpa_offset = (u16) (current_ptr - | ||
1416 | bss_entry->beacon_buf); | ||
1417 | } else if (!memcmp(vendor_ie->vend_hdr.oui, wmm_oui, | ||
1418 | sizeof(wmm_oui))) { | ||
1419 | if (total_ie_len == | ||
1420 | sizeof(struct ieee_types_wmm_parameter) | ||
1421 | || total_ie_len == | ||
1422 | sizeof(struct ieee_types_wmm_info)) | ||
1423 | /* | ||
1424 | * Only accept and copy the WMM IE if | ||
1425 | * it matches the size expected for the | ||
1426 | * WMM Info IE or the WMM Parameter IE. | ||
1427 | */ | ||
1428 | memcpy((u8 *) &bss_entry->wmm_ie, | ||
1429 | current_ptr, total_ie_len); | ||
1430 | } | ||
1431 | break; | ||
1432 | case WLAN_EID_RSN: | ||
1433 | bss_entry->bcn_rsn_ie = | ||
1434 | (struct ieee_types_generic *) current_ptr; | ||
1435 | bss_entry->rsn_offset = (u16) (current_ptr - | ||
1436 | bss_entry->beacon_buf); | ||
1437 | break; | ||
1438 | case WLAN_EID_BSS_AC_ACCESS_DELAY: | ||
1439 | bss_entry->bcn_wapi_ie = | ||
1440 | (struct ieee_types_generic *) current_ptr; | ||
1441 | bss_entry->wapi_offset = (u16) (current_ptr - | ||
1442 | bss_entry->beacon_buf); | ||
1443 | break; | ||
1444 | case WLAN_EID_HT_CAPABILITY: | ||
1445 | bss_entry->bcn_ht_cap = (struct ieee80211_ht_cap *) | ||
1446 | (current_ptr + | ||
1447 | sizeof(struct ieee_types_header)); | ||
1448 | bss_entry->ht_cap_offset = (u16) (current_ptr + | ||
1449 | sizeof(struct ieee_types_header) - | ||
1450 | bss_entry->beacon_buf); | ||
1451 | break; | ||
1452 | case WLAN_EID_HT_INFORMATION: | ||
1453 | bss_entry->bcn_ht_info = (struct ieee80211_ht_info *) | ||
1454 | (current_ptr + | ||
1455 | sizeof(struct ieee_types_header)); | ||
1456 | bss_entry->ht_info_offset = (u16) (current_ptr + | ||
1457 | sizeof(struct ieee_types_header) - | ||
1458 | bss_entry->beacon_buf); | ||
1459 | break; | ||
1460 | case WLAN_EID_BSS_COEX_2040: | ||
1461 | bss_entry->bcn_bss_co_2040 = (u8 *) (current_ptr + | ||
1462 | sizeof(struct ieee_types_header)); | ||
1463 | bss_entry->bss_co_2040_offset = (u16) (current_ptr + | ||
1464 | sizeof(struct ieee_types_header) - | ||
1465 | bss_entry->beacon_buf); | ||
1466 | break; | ||
1467 | case WLAN_EID_EXT_CAPABILITY: | ||
1468 | bss_entry->bcn_ext_cap = (u8 *) (current_ptr + | ||
1469 | sizeof(struct ieee_types_header)); | ||
1470 | bss_entry->ext_cap_offset = (u16) (current_ptr + | ||
1471 | sizeof(struct ieee_types_header) - | ||
1472 | bss_entry->beacon_buf); | ||
1473 | break; | ||
1474 | case WLAN_EID_OVERLAP_BSS_SCAN_PARAM: | ||
1475 | bss_entry->bcn_obss_scan = | ||
1476 | (struct ieee_types_obss_scan_param *) | ||
1477 | current_ptr; | ||
1478 | bss_entry->overlap_bss_offset = (u16) (current_ptr - | ||
1479 | bss_entry->beacon_buf); | ||
1480 | break; | ||
1481 | default: | ||
1482 | break; | ||
1483 | } | ||
1484 | |||
1485 | current_ptr += element_len + 2; | ||
1486 | |||
1487 | /* Need to account for IE ID and IE Len */ | ||
1488 | bytes_left_for_current_beacon -= (element_len + 2); | ||
1489 | |||
1490 | } /* while (bytes_left_for_current_beacon > 2) */ | ||
1491 | return ret; | ||
1492 | } | ||
1493 | |||
1494 | /* | ||
1495 | * This function adjusts the pointers used in beacon buffers to reflect | ||
1496 | * shifts. | ||
1497 | * | ||
1498 | * The memory allocated for beacon buffers is of fixed sizes where all the | ||
1499 | * saved beacons must be stored. New beacons are added in the free portion | ||
1500 | * of this memory, space permitting; while duplicate beacon buffers are | ||
1501 | * placed at the same start location. However, since duplicate beacon | ||
1502 | * buffers may not match the size of the old one, all the following buffers | ||
1503 | * in the memory must be shifted to either make space, or to fill up freed | ||
1504 | * up space. | ||
1505 | * | ||
1506 | * This function is used to update the beacon buffer pointers that are past | ||
1507 | * an existing beacon buffer that is updated with a new one of different | ||
1508 | * size. The pointers are shifted by a fixed amount, either forward or | ||
1509 | * backward. | ||
1510 | * | ||
1511 | * the following pointers in every affected beacon buffers are changed, if | ||
1512 | * present - | ||
1513 | * - WPA IE pointer | ||
1514 | * - RSN IE pointer | ||
1515 | * - WAPI IE pointer | ||
1516 | * - HT capability IE pointer | ||
1517 | * - HT information IE pointer | ||
1518 | * - BSS coexistence 20/40 IE pointer | ||
1519 | * - Extended capability IE pointer | ||
1520 | * - Overlapping BSS scan parameter IE pointer | ||
1521 | */ | ||
1522 | static void | ||
1523 | mwifiex_adjust_beacon_buffer_ptrs(struct mwifiex_private *priv, u8 advance, | ||
1524 | u8 *bcn_store, u32 rem_bcn_size, | ||
1525 | u32 num_of_ent) | ||
1526 | { | ||
1527 | struct mwifiex_adapter *adapter = priv->adapter; | ||
1528 | u32 adj_idx; | ||
1529 | for (adj_idx = 0; adj_idx < num_of_ent; adj_idx++) { | ||
1530 | if (adapter->scan_table[adj_idx].beacon_buf > bcn_store) { | ||
1531 | |||
1532 | if (advance) | ||
1533 | adapter->scan_table[adj_idx].beacon_buf += | ||
1534 | rem_bcn_size; | ||
1535 | else | ||
1536 | adapter->scan_table[adj_idx].beacon_buf -= | ||
1537 | rem_bcn_size; | ||
1538 | |||
1539 | if (adapter->scan_table[adj_idx].bcn_wpa_ie) | ||
1540 | adapter->scan_table[adj_idx].bcn_wpa_ie = | ||
1541 | (struct ieee_types_vendor_specific *) | ||
1542 | (adapter->scan_table[adj_idx].beacon_buf + | ||
1543 | adapter->scan_table[adj_idx].wpa_offset); | ||
1544 | if (adapter->scan_table[adj_idx].bcn_rsn_ie) | ||
1545 | adapter->scan_table[adj_idx].bcn_rsn_ie = | ||
1546 | (struct ieee_types_generic *) | ||
1547 | (adapter->scan_table[adj_idx].beacon_buf + | ||
1548 | adapter->scan_table[adj_idx].rsn_offset); | ||
1549 | if (adapter->scan_table[adj_idx].bcn_wapi_ie) | ||
1550 | adapter->scan_table[adj_idx].bcn_wapi_ie = | ||
1551 | (struct ieee_types_generic *) | ||
1552 | (adapter->scan_table[adj_idx].beacon_buf + | ||
1553 | adapter->scan_table[adj_idx].wapi_offset); | ||
1554 | if (adapter->scan_table[adj_idx].bcn_ht_cap) | ||
1555 | adapter->scan_table[adj_idx].bcn_ht_cap = | ||
1556 | (struct ieee80211_ht_cap *) | ||
1557 | (adapter->scan_table[adj_idx].beacon_buf + | ||
1558 | adapter->scan_table[adj_idx].ht_cap_offset); | ||
1559 | |||
1560 | if (adapter->scan_table[adj_idx].bcn_ht_info) | ||
1561 | adapter->scan_table[adj_idx].bcn_ht_info = | ||
1562 | (struct ieee80211_ht_info *) | ||
1563 | (adapter->scan_table[adj_idx].beacon_buf + | ||
1564 | adapter->scan_table[adj_idx].ht_info_offset); | ||
1565 | if (adapter->scan_table[adj_idx].bcn_bss_co_2040) | ||
1566 | adapter->scan_table[adj_idx].bcn_bss_co_2040 = | ||
1567 | (u8 *) | ||
1568 | (adapter->scan_table[adj_idx].beacon_buf + | ||
1569 | adapter->scan_table[adj_idx].bss_co_2040_offset); | ||
1570 | if (adapter->scan_table[adj_idx].bcn_ext_cap) | ||
1571 | adapter->scan_table[adj_idx].bcn_ext_cap = | ||
1572 | (u8 *) | ||
1573 | (adapter->scan_table[adj_idx].beacon_buf + | ||
1574 | adapter->scan_table[adj_idx].ext_cap_offset); | ||
1575 | if (adapter->scan_table[adj_idx].bcn_obss_scan) | ||
1576 | adapter->scan_table[adj_idx].bcn_obss_scan = | ||
1577 | (struct ieee_types_obss_scan_param *) | ||
1578 | (adapter->scan_table[adj_idx].beacon_buf + | ||
1579 | adapter->scan_table[adj_idx].overlap_bss_offset); | ||
1580 | } | ||
1581 | } | ||
1582 | } | ||
1583 | |||
1584 | /* | ||
1585 | * This function updates the pointers used in beacon buffer for given bss | ||
1586 | * descriptor to reflect shifts | ||
1587 | * | ||
1588 | * Following pointers are updated | ||
1589 | * - WPA IE pointer | ||
1590 | * - RSN IE pointer | ||
1591 | * - WAPI IE pointer | ||
1592 | * - HT capability IE pointer | ||
1593 | * - HT information IE pointer | ||
1594 | * - BSS coexistence 20/40 IE pointer | ||
1595 | * - Extended capability IE pointer | ||
1596 | * - Overlapping BSS scan parameter IE pointer | ||
1597 | */ | ||
1598 | static void | ||
1599 | mwifiex_update_beacon_buffer_ptrs(struct mwifiex_bssdescriptor *beacon) | ||
1600 | { | ||
1601 | if (beacon->bcn_wpa_ie) | ||
1602 | beacon->bcn_wpa_ie = (struct ieee_types_vendor_specific *) | ||
1603 | (beacon->beacon_buf + beacon->wpa_offset); | ||
1604 | if (beacon->bcn_rsn_ie) | ||
1605 | beacon->bcn_rsn_ie = (struct ieee_types_generic *) | ||
1606 | (beacon->beacon_buf + beacon->rsn_offset); | ||
1607 | if (beacon->bcn_wapi_ie) | ||
1608 | beacon->bcn_wapi_ie = (struct ieee_types_generic *) | ||
1609 | (beacon->beacon_buf + beacon->wapi_offset); | ||
1610 | if (beacon->bcn_ht_cap) | ||
1611 | beacon->bcn_ht_cap = (struct ieee80211_ht_cap *) | ||
1612 | (beacon->beacon_buf + beacon->ht_cap_offset); | ||
1613 | if (beacon->bcn_ht_info) | ||
1614 | beacon->bcn_ht_info = (struct ieee80211_ht_info *) | ||
1615 | (beacon->beacon_buf + beacon->ht_info_offset); | ||
1616 | if (beacon->bcn_bss_co_2040) | ||
1617 | beacon->bcn_bss_co_2040 = (u8 *) (beacon->beacon_buf + | ||
1618 | beacon->bss_co_2040_offset); | ||
1619 | if (beacon->bcn_ext_cap) | ||
1620 | beacon->bcn_ext_cap = (u8 *) (beacon->beacon_buf + | ||
1621 | beacon->ext_cap_offset); | ||
1622 | if (beacon->bcn_obss_scan) | ||
1623 | beacon->bcn_obss_scan = (struct ieee_types_obss_scan_param *) | ||
1624 | (beacon->beacon_buf + beacon->overlap_bss_offset); | ||
1625 | } | ||
1626 | |||
1627 | /* | ||
1628 | * This function stores a beacon or probe response for a BSS returned | ||
1629 | * in the scan. | ||
1630 | * | ||
1631 | * This stores a new scan response or an update for a previous scan response. | ||
1632 | * New entries need to verify that they do not exceed the total amount of | ||
1633 | * memory allocated for the table. | ||
1634 | * | ||
1635 | * Replacement entries need to take into consideration the amount of space | ||
1636 | * currently allocated for the beacon/probe response and adjust the entry | ||
1637 | * as needed. | ||
1638 | * | ||
1639 | * A small amount of extra pad (SCAN_BEACON_ENTRY_PAD) is generally reserved | ||
1640 | * for an entry in case it is a beacon since a probe response for the | ||
1641 | * network will by larger per the standard. This helps to reduce the | ||
1642 | * amount of memory copying to fit a new probe response into an entry | ||
1643 | * already occupied by a network's previously stored beacon. | ||
1644 | */ | ||
1645 | static void | ||
1646 | mwifiex_ret_802_11_scan_store_beacon(struct mwifiex_private *priv, | ||
1647 | u32 beacon_idx, u32 num_of_ent, | ||
1648 | struct mwifiex_bssdescriptor *new_beacon) | ||
1649 | { | ||
1650 | struct mwifiex_adapter *adapter = priv->adapter; | ||
1651 | u8 *bcn_store; | ||
1652 | u32 new_bcn_size; | ||
1653 | u32 old_bcn_size; | ||
1654 | u32 bcn_space; | ||
1655 | |||
1656 | if (adapter->scan_table[beacon_idx].beacon_buf) { | ||
1657 | |||
1658 | new_bcn_size = new_beacon->beacon_buf_size; | ||
1659 | old_bcn_size = adapter->scan_table[beacon_idx].beacon_buf_size; | ||
1660 | bcn_space = adapter->scan_table[beacon_idx].beacon_buf_size_max; | ||
1661 | bcn_store = adapter->scan_table[beacon_idx].beacon_buf; | ||
1662 | |||
1663 | /* Set the max to be the same as current entry unless changed | ||
1664 | below */ | ||
1665 | new_beacon->beacon_buf_size_max = bcn_space; | ||
1666 | if (new_bcn_size == old_bcn_size) { | ||
1667 | /* | ||
1668 | * Beacon is the same size as the previous entry. | ||
1669 | * Replace the previous contents with the scan result | ||
1670 | */ | ||
1671 | memcpy(bcn_store, new_beacon->beacon_buf, | ||
1672 | new_beacon->beacon_buf_size); | ||
1673 | |||
1674 | } else if (new_bcn_size <= bcn_space) { | ||
1675 | /* | ||
1676 | * New beacon size will fit in the amount of space | ||
1677 | * we have previously allocated for it | ||
1678 | */ | ||
1679 | |||
1680 | /* Copy the new beacon buffer entry over the old one */ | ||
1681 | memcpy(bcn_store, new_beacon->beacon_buf, new_bcn_size); | ||
1682 | |||
1683 | /* | ||
1684 | * If the old beacon size was less than the maximum | ||
1685 | * we had alloted for the entry, and the new entry | ||
1686 | * is even smaller, reset the max size to the old | ||
1687 | * beacon entry and compress the storage space | ||
1688 | * (leaving a new pad space of (old_bcn_size - | ||
1689 | * new_bcn_size). | ||
1690 | */ | ||
1691 | if (old_bcn_size < bcn_space | ||
1692 | && new_bcn_size <= old_bcn_size) { | ||
1693 | /* | ||
1694 | * Old Beacon size is smaller than the alloted | ||
1695 | * storage size. Shrink the alloted storage | ||
1696 | * space. | ||
1697 | */ | ||
1698 | dev_dbg(adapter->dev, "info: AppControl:" | ||
1699 | " smaller duplicate beacon " | ||
1700 | "(%d), old = %d, new = %d, space = %d," | ||
1701 | "left = %d\n", | ||
1702 | beacon_idx, old_bcn_size, new_bcn_size, | ||
1703 | bcn_space, | ||
1704 | (int)(sizeof(adapter->bcn_buf) - | ||
1705 | (adapter->bcn_buf_end - | ||
1706 | adapter->bcn_buf))); | ||
1707 | |||
1708 | /* | ||
1709 | * memmove (since the memory overlaps) the | ||
1710 | * data after the beacon we just stored to the | ||
1711 | * end of the current beacon. This cleans up | ||
1712 | * any unused space the old larger beacon was | ||
1713 | * using in the buffer | ||
1714 | */ | ||
1715 | memmove(bcn_store + old_bcn_size, | ||
1716 | bcn_store + bcn_space, | ||
1717 | adapter->bcn_buf_end - (bcn_store + | ||
1718 | bcn_space)); | ||
1719 | |||
1720 | /* | ||
1721 | * Decrement the end pointer by the difference | ||
1722 | * between the old larger size and the new | ||
1723 | * smaller size since we are using less space | ||
1724 | * due to the new beacon being smaller | ||
1725 | */ | ||
1726 | adapter->bcn_buf_end -= | ||
1727 | (bcn_space - old_bcn_size); | ||
1728 | |||
1729 | /* Set the maximum storage size to the old | ||
1730 | beacon size */ | ||
1731 | new_beacon->beacon_buf_size_max = old_bcn_size; | ||
1732 | |||
1733 | /* Adjust beacon buffer pointers that are past | ||
1734 | the current */ | ||
1735 | mwifiex_adjust_beacon_buffer_ptrs(priv, 0, | ||
1736 | bcn_store, (bcn_space - old_bcn_size), | ||
1737 | num_of_ent); | ||
1738 | } | ||
1739 | } else if (adapter->bcn_buf_end + (new_bcn_size - bcn_space) | ||
1740 | < (adapter->bcn_buf + sizeof(adapter->bcn_buf))) { | ||
1741 | /* | ||
1742 | * Beacon is larger than space previously allocated | ||
1743 | * (bcn_space) and there is enough space left in the | ||
1744 | * beaconBuffer to store the additional data | ||
1745 | */ | ||
1746 | dev_dbg(adapter->dev, "info: AppControl:" | ||
1747 | " larger duplicate beacon (%d), " | ||
1748 | "old = %d, new = %d, space = %d, left = %d\n", | ||
1749 | beacon_idx, old_bcn_size, new_bcn_size, | ||
1750 | bcn_space, | ||
1751 | (int)(sizeof(adapter->bcn_buf) - | ||
1752 | (adapter->bcn_buf_end - | ||
1753 | adapter->bcn_buf))); | ||
1754 | |||
1755 | /* | ||
1756 | * memmove (since the memory overlaps) the data | ||
1757 | * after the beacon we just stored to the end of | ||
1758 | * the current beacon. This moves the data for | ||
1759 | * the beacons after this further in memory to | ||
1760 | * make space for the new larger beacon we are | ||
1761 | * about to copy in. | ||
1762 | */ | ||
1763 | memmove(bcn_store + new_bcn_size, | ||
1764 | bcn_store + bcn_space, | ||
1765 | adapter->bcn_buf_end - (bcn_store + bcn_space)); | ||
1766 | |||
1767 | /* Copy the new beacon buffer entry over the old one */ | ||
1768 | memcpy(bcn_store, new_beacon->beacon_buf, new_bcn_size); | ||
1769 | |||
1770 | /* Move the beacon end pointer by the amount of new | ||
1771 | beacon data we are adding */ | ||
1772 | adapter->bcn_buf_end += (new_bcn_size - bcn_space); | ||
1773 | |||
1774 | /* | ||
1775 | * This entry is bigger than the alloted max space | ||
1776 | * previously reserved. Increase the max space to | ||
1777 | * be equal to the new beacon size | ||
1778 | */ | ||
1779 | new_beacon->beacon_buf_size_max = new_bcn_size; | ||
1780 | |||
1781 | /* Adjust beacon buffer pointers that are past the | ||
1782 | current */ | ||
1783 | mwifiex_adjust_beacon_buffer_ptrs(priv, 1, bcn_store, | ||
1784 | (new_bcn_size - bcn_space), | ||
1785 | num_of_ent); | ||
1786 | } else { | ||
1787 | /* | ||
1788 | * Beacon is larger than the previously allocated space, | ||
1789 | * but there is not enough free space to store the | ||
1790 | * additional data. | ||
1791 | */ | ||
1792 | dev_err(adapter->dev, "AppControl: larger duplicate " | ||
1793 | " beacon (%d), old = %d new = %d, space = %d," | ||
1794 | " left = %d\n", beacon_idx, old_bcn_size, | ||
1795 | new_bcn_size, bcn_space, | ||
1796 | (int)(sizeof(adapter->bcn_buf) - | ||
1797 | (adapter->bcn_buf_end - adapter->bcn_buf))); | ||
1798 | |||
1799 | /* Storage failure, keep old beacon intact */ | ||
1800 | new_beacon->beacon_buf_size = old_bcn_size; | ||
1801 | if (new_beacon->bcn_wpa_ie) | ||
1802 | new_beacon->wpa_offset = | ||
1803 | adapter->scan_table[beacon_idx]. | ||
1804 | wpa_offset; | ||
1805 | if (new_beacon->bcn_rsn_ie) | ||
1806 | new_beacon->rsn_offset = | ||
1807 | adapter->scan_table[beacon_idx]. | ||
1808 | rsn_offset; | ||
1809 | if (new_beacon->bcn_wapi_ie) | ||
1810 | new_beacon->wapi_offset = | ||
1811 | adapter->scan_table[beacon_idx]. | ||
1812 | wapi_offset; | ||
1813 | if (new_beacon->bcn_ht_cap) | ||
1814 | new_beacon->ht_cap_offset = | ||
1815 | adapter->scan_table[beacon_idx]. | ||
1816 | ht_cap_offset; | ||
1817 | if (new_beacon->bcn_ht_info) | ||
1818 | new_beacon->ht_info_offset = | ||
1819 | adapter->scan_table[beacon_idx]. | ||
1820 | ht_info_offset; | ||
1821 | if (new_beacon->bcn_bss_co_2040) | ||
1822 | new_beacon->bss_co_2040_offset = | ||
1823 | adapter->scan_table[beacon_idx]. | ||
1824 | bss_co_2040_offset; | ||
1825 | if (new_beacon->bcn_ext_cap) | ||
1826 | new_beacon->ext_cap_offset = | ||
1827 | adapter->scan_table[beacon_idx]. | ||
1828 | ext_cap_offset; | ||
1829 | if (new_beacon->bcn_obss_scan) | ||
1830 | new_beacon->overlap_bss_offset = | ||
1831 | adapter->scan_table[beacon_idx]. | ||
1832 | overlap_bss_offset; | ||
1833 | } | ||
1834 | /* Point the new entry to its permanent storage space */ | ||
1835 | new_beacon->beacon_buf = bcn_store; | ||
1836 | mwifiex_update_beacon_buffer_ptrs(new_beacon); | ||
1837 | } else { | ||
1838 | /* | ||
1839 | * No existing beacon data exists for this entry, check to see | ||
1840 | * if we can fit it in the remaining space | ||
1841 | */ | ||
1842 | if (adapter->bcn_buf_end + new_beacon->beacon_buf_size + | ||
1843 | SCAN_BEACON_ENTRY_PAD < (adapter->bcn_buf + | ||
1844 | sizeof(adapter->bcn_buf))) { | ||
1845 | |||
1846 | /* | ||
1847 | * Copy the beacon buffer data from the local entry to | ||
1848 | * the adapter dev struct buffer space used to store | ||
1849 | * the raw beacon data for each entry in the scan table | ||
1850 | */ | ||
1851 | memcpy(adapter->bcn_buf_end, new_beacon->beacon_buf, | ||
1852 | new_beacon->beacon_buf_size); | ||
1853 | |||
1854 | /* Update the beacon ptr to point to the table save | ||
1855 | area */ | ||
1856 | new_beacon->beacon_buf = adapter->bcn_buf_end; | ||
1857 | new_beacon->beacon_buf_size_max = | ||
1858 | (new_beacon->beacon_buf_size + | ||
1859 | SCAN_BEACON_ENTRY_PAD); | ||
1860 | |||
1861 | mwifiex_update_beacon_buffer_ptrs(new_beacon); | ||
1862 | |||
1863 | /* Increment the end pointer by the size reserved */ | ||
1864 | adapter->bcn_buf_end += new_beacon->beacon_buf_size_max; | ||
1865 | |||
1866 | dev_dbg(adapter->dev, "info: AppControl: beacon[%02d]" | ||
1867 | " sz=%03d, used = %04d, left = %04d\n", | ||
1868 | beacon_idx, | ||
1869 | new_beacon->beacon_buf_size, | ||
1870 | (int)(adapter->bcn_buf_end - adapter->bcn_buf), | ||
1871 | (int)(sizeof(adapter->bcn_buf) - | ||
1872 | (adapter->bcn_buf_end - | ||
1873 | adapter->bcn_buf))); | ||
1874 | } else { | ||
1875 | /* No space for new beacon */ | ||
1876 | dev_dbg(adapter->dev, "info: AppControl: no space for" | ||
1877 | " beacon (%d): %pM sz=%03d, left=%03d\n", | ||
1878 | beacon_idx, new_beacon->mac_address, | ||
1879 | new_beacon->beacon_buf_size, | ||
1880 | (int)(sizeof(adapter->bcn_buf) - | ||
1881 | (adapter->bcn_buf_end - | ||
1882 | adapter->bcn_buf))); | ||
1883 | |||
1884 | /* Storage failure; clear storage records for this | ||
1885 | bcn */ | ||
1886 | new_beacon->beacon_buf = NULL; | ||
1887 | new_beacon->beacon_buf_size = 0; | ||
1888 | new_beacon->beacon_buf_size_max = 0; | ||
1889 | new_beacon->bcn_wpa_ie = NULL; | ||
1890 | new_beacon->wpa_offset = 0; | ||
1891 | new_beacon->bcn_rsn_ie = NULL; | ||
1892 | new_beacon->rsn_offset = 0; | ||
1893 | new_beacon->bcn_wapi_ie = NULL; | ||
1894 | new_beacon->wapi_offset = 0; | ||
1895 | new_beacon->bcn_ht_cap = NULL; | ||
1896 | new_beacon->ht_cap_offset = 0; | ||
1897 | new_beacon->bcn_ht_info = NULL; | ||
1898 | new_beacon->ht_info_offset = 0; | ||
1899 | new_beacon->bcn_bss_co_2040 = NULL; | ||
1900 | new_beacon->bss_co_2040_offset = 0; | ||
1901 | new_beacon->bcn_ext_cap = NULL; | ||
1902 | new_beacon->ext_cap_offset = 0; | ||
1903 | new_beacon->bcn_obss_scan = NULL; | ||
1904 | new_beacon->overlap_bss_offset = 0; | ||
1905 | } | ||
1906 | } | ||
1907 | } | ||
1908 | |||
1909 | /* | ||
1910 | * This function restores a beacon buffer of the current BSS descriptor. | ||
1911 | */ | ||
1912 | static void mwifiex_restore_curr_bcn(struct mwifiex_private *priv) | ||
1913 | { | ||
1914 | struct mwifiex_adapter *adapter = priv->adapter; | ||
1915 | struct mwifiex_bssdescriptor *curr_bss = | ||
1916 | &priv->curr_bss_params.bss_descriptor; | ||
1917 | unsigned long flags; | ||
1918 | |||
1919 | if (priv->curr_bcn_buf && | ||
1920 | ((adapter->bcn_buf_end + priv->curr_bcn_size) < | ||
1921 | (adapter->bcn_buf + sizeof(adapter->bcn_buf)))) { | ||
1922 | spin_lock_irqsave(&priv->curr_bcn_buf_lock, flags); | ||
1923 | |||
1924 | /* restore the current beacon buffer */ | ||
1925 | memcpy(adapter->bcn_buf_end, priv->curr_bcn_buf, | ||
1926 | priv->curr_bcn_size); | ||
1927 | curr_bss->beacon_buf = adapter->bcn_buf_end; | ||
1928 | curr_bss->beacon_buf_size = priv->curr_bcn_size; | ||
1929 | adapter->bcn_buf_end += priv->curr_bcn_size; | ||
1930 | |||
1931 | /* adjust the pointers in the current BSS descriptor */ | ||
1932 | if (curr_bss->bcn_wpa_ie) | ||
1933 | curr_bss->bcn_wpa_ie = | ||
1934 | (struct ieee_types_vendor_specific *) | ||
1935 | (curr_bss->beacon_buf + | ||
1936 | curr_bss->wpa_offset); | ||
1937 | |||
1938 | if (curr_bss->bcn_rsn_ie) | ||
1939 | curr_bss->bcn_rsn_ie = (struct ieee_types_generic *) | ||
1940 | (curr_bss->beacon_buf + | ||
1941 | curr_bss->rsn_offset); | ||
1942 | |||
1943 | if (curr_bss->bcn_ht_cap) | ||
1944 | curr_bss->bcn_ht_cap = (struct ieee80211_ht_cap *) | ||
1945 | (curr_bss->beacon_buf + | ||
1946 | curr_bss->ht_cap_offset); | ||
1947 | |||
1948 | if (curr_bss->bcn_ht_info) | ||
1949 | curr_bss->bcn_ht_info = (struct ieee80211_ht_info *) | ||
1950 | (curr_bss->beacon_buf + | ||
1951 | curr_bss->ht_info_offset); | ||
1952 | |||
1953 | if (curr_bss->bcn_bss_co_2040) | ||
1954 | curr_bss->bcn_bss_co_2040 = | ||
1955 | (u8 *) (curr_bss->beacon_buf + | ||
1956 | curr_bss->bss_co_2040_offset); | ||
1957 | |||
1958 | if (curr_bss->bcn_ext_cap) | ||
1959 | curr_bss->bcn_ext_cap = (u8 *) (curr_bss->beacon_buf + | ||
1960 | curr_bss->ext_cap_offset); | ||
1961 | |||
1962 | if (curr_bss->bcn_obss_scan) | ||
1963 | curr_bss->bcn_obss_scan = | ||
1964 | (struct ieee_types_obss_scan_param *) | ||
1965 | (curr_bss->beacon_buf + | ||
1966 | curr_bss->overlap_bss_offset); | ||
1967 | |||
1968 | spin_unlock_irqrestore(&priv->curr_bcn_buf_lock, flags); | ||
1969 | |||
1970 | dev_dbg(adapter->dev, "info: current beacon restored %d\n", | ||
1971 | priv->curr_bcn_size); | ||
1972 | } else { | ||
1973 | dev_warn(adapter->dev, | ||
1974 | "curr_bcn_buf not saved or bcn_buf has no space\n"); | ||
1975 | } | ||
1976 | } | ||
1977 | |||
1978 | /* | ||
1979 | * This function post processes the scan table after a new scan command has | ||
1980 | * completed. | ||
1981 | * | ||
1982 | * It inspects each entry of the scan table and tries to find an entry that | ||
1983 | * matches with our current associated/joined network from the scan. If | ||
1984 | * one is found, the stored copy of the BSS descriptor of our current network | ||
1985 | * is updated. | ||
1986 | * | ||
1987 | * It also debug dumps the current scan table contents after processing is over. | ||
1988 | */ | ||
1989 | static void | ||
1990 | mwifiex_process_scan_results(struct mwifiex_private *priv) | ||
1991 | { | ||
1992 | struct mwifiex_adapter *adapter = priv->adapter; | ||
1993 | s32 j; | ||
1994 | u32 i; | ||
1995 | unsigned long flags; | ||
1996 | |||
1997 | if (priv->media_connected) { | ||
1998 | |||
1999 | j = mwifiex_find_ssid_in_list(priv, &priv->curr_bss_params. | ||
2000 | bss_descriptor.ssid, | ||
2001 | priv->curr_bss_params. | ||
2002 | bss_descriptor.mac_address, | ||
2003 | priv->bss_mode); | ||
2004 | |||
2005 | if (j >= 0) { | ||
2006 | spin_lock_irqsave(&priv->curr_bcn_buf_lock, flags); | ||
2007 | priv->curr_bss_params.bss_descriptor.bcn_wpa_ie = NULL; | ||
2008 | priv->curr_bss_params.bss_descriptor.wpa_offset = 0; | ||
2009 | priv->curr_bss_params.bss_descriptor.bcn_rsn_ie = NULL; | ||
2010 | priv->curr_bss_params.bss_descriptor.rsn_offset = 0; | ||
2011 | priv->curr_bss_params.bss_descriptor.bcn_wapi_ie = NULL; | ||
2012 | priv->curr_bss_params.bss_descriptor.wapi_offset = 0; | ||
2013 | priv->curr_bss_params.bss_descriptor.bcn_ht_cap = NULL; | ||
2014 | priv->curr_bss_params.bss_descriptor.ht_cap_offset = | ||
2015 | 0; | ||
2016 | priv->curr_bss_params.bss_descriptor.bcn_ht_info = NULL; | ||
2017 | priv->curr_bss_params.bss_descriptor.ht_info_offset = | ||
2018 | 0; | ||
2019 | priv->curr_bss_params.bss_descriptor.bcn_bss_co_2040 = | ||
2020 | NULL; | ||
2021 | priv->curr_bss_params.bss_descriptor. | ||
2022 | bss_co_2040_offset = 0; | ||
2023 | priv->curr_bss_params.bss_descriptor.bcn_ext_cap = NULL; | ||
2024 | priv->curr_bss_params.bss_descriptor.ext_cap_offset = 0; | ||
2025 | priv->curr_bss_params.bss_descriptor. | ||
2026 | bcn_obss_scan = NULL; | ||
2027 | priv->curr_bss_params.bss_descriptor. | ||
2028 | overlap_bss_offset = 0; | ||
2029 | priv->curr_bss_params.bss_descriptor.beacon_buf = NULL; | ||
2030 | priv->curr_bss_params.bss_descriptor.beacon_buf_size = | ||
2031 | 0; | ||
2032 | priv->curr_bss_params.bss_descriptor. | ||
2033 | beacon_buf_size_max = 0; | ||
2034 | |||
2035 | dev_dbg(adapter->dev, "info: Found current ssid/bssid" | ||
2036 | " in list @ index #%d\n", j); | ||
2037 | /* Make a copy of current BSSID descriptor */ | ||
2038 | memcpy(&priv->curr_bss_params.bss_descriptor, | ||
2039 | &adapter->scan_table[j], | ||
2040 | sizeof(priv->curr_bss_params.bss_descriptor)); | ||
2041 | |||
2042 | mwifiex_save_curr_bcn(priv); | ||
2043 | spin_unlock_irqrestore(&priv->curr_bcn_buf_lock, flags); | ||
2044 | |||
2045 | } else { | ||
2046 | mwifiex_restore_curr_bcn(priv); | ||
2047 | } | ||
2048 | } | ||
2049 | |||
2050 | for (i = 0; i < adapter->num_in_scan_table; i++) | ||
2051 | dev_dbg(adapter->dev, "info: scan:(%02d) %pM " | ||
2052 | "RSSI[%03d], SSID[%s]\n", | ||
2053 | i, adapter->scan_table[i].mac_address, | ||
2054 | (s32) adapter->scan_table[i].rssi, | ||
2055 | adapter->scan_table[i].ssid.ssid); | ||
2056 | } | ||
2057 | |||
2058 | /* | ||
2059 | * This function converts radio type scan parameter to a band configuration | ||
2060 | * to be used in join command. | ||
2061 | */ | ||
2062 | static u8 | ||
2063 | mwifiex_radio_type_to_band(u8 radio_type) | ||
2064 | { | ||
2065 | switch (radio_type) { | ||
2066 | case HostCmd_SCAN_RADIO_TYPE_A: | ||
2067 | return BAND_A; | ||
2068 | case HostCmd_SCAN_RADIO_TYPE_BG: | ||
2069 | default: | ||
2070 | return BAND_G; | ||
2071 | } | ||
2072 | } | ||
2073 | |||
2074 | /* | ||
2075 | * This function deletes a specific indexed entry from the scan table. | ||
2076 | * | ||
2077 | * This also compacts the remaining entries and adjusts any buffering | ||
2078 | * of beacon/probe response data if needed. | ||
2079 | */ | ||
2080 | static void | ||
2081 | mwifiex_scan_delete_table_entry(struct mwifiex_private *priv, s32 table_idx) | ||
2082 | { | ||
2083 | struct mwifiex_adapter *adapter = priv->adapter; | ||
2084 | u32 del_idx; | ||
2085 | u32 beacon_buf_adj; | ||
2086 | u8 *beacon_buf; | ||
2087 | |||
2088 | /* | ||
2089 | * Shift the saved beacon buffer data for the scan table back over the | ||
2090 | * entry being removed. Update the end of buffer pointer. Save the | ||
2091 | * deleted buffer allocation size for pointer adjustments for entries | ||
2092 | * compacted after the deleted index. | ||
2093 | */ | ||
2094 | beacon_buf_adj = adapter->scan_table[table_idx].beacon_buf_size_max; | ||
2095 | |||
2096 | dev_dbg(adapter->dev, "info: Scan: Delete Entry %d, beacon buffer " | ||
2097 | "removal = %d bytes\n", table_idx, beacon_buf_adj); | ||
2098 | |||
2099 | /* Check if the table entry had storage allocated for its beacon */ | ||
2100 | if (beacon_buf_adj) { | ||
2101 | beacon_buf = adapter->scan_table[table_idx].beacon_buf; | ||
2102 | |||
2103 | /* | ||
2104 | * Remove the entry's buffer space, decrement the table end | ||
2105 | * pointer by the amount we are removing | ||
2106 | */ | ||
2107 | adapter->bcn_buf_end -= beacon_buf_adj; | ||
2108 | |||
2109 | dev_dbg(adapter->dev, "info: scan: delete entry %d," | ||
2110 | " compact data: %p <- %p (sz = %d)\n", | ||
2111 | table_idx, beacon_buf, | ||
2112 | beacon_buf + beacon_buf_adj, | ||
2113 | (int)(adapter->bcn_buf_end - beacon_buf)); | ||
2114 | |||
2115 | /* | ||
2116 | * Compact data storage. Copy all data after the deleted | ||
2117 | * entry's end address (beacon_buf + beacon_buf_adj) back | ||
2118 | * to the original start address (beacon_buf). | ||
2119 | * | ||
2120 | * Scan table entries affected by the move will have their | ||
2121 | * entry pointer adjusted below. | ||
2122 | * | ||
2123 | * Use memmove since the dest/src memory regions overlap. | ||
2124 | */ | ||
2125 | memmove(beacon_buf, beacon_buf + beacon_buf_adj, | ||
2126 | adapter->bcn_buf_end - beacon_buf); | ||
2127 | } | ||
2128 | |||
2129 | dev_dbg(adapter->dev, | ||
2130 | "info: Scan: Delete Entry %d, num_in_scan_table = %d\n", | ||
2131 | table_idx, adapter->num_in_scan_table); | ||
2132 | |||
2133 | /* Shift all of the entries after the table_idx back by one, compacting | ||
2134 | the table and removing the requested entry */ | ||
2135 | for (del_idx = table_idx; (del_idx + 1) < adapter->num_in_scan_table; | ||
2136 | del_idx++) { | ||
2137 | /* Copy the next entry over this one */ | ||
2138 | memcpy(adapter->scan_table + del_idx, | ||
2139 | adapter->scan_table + del_idx + 1, | ||
2140 | sizeof(struct mwifiex_bssdescriptor)); | ||
2141 | |||
2142 | /* | ||
2143 | * Adjust this entry's pointer to its beacon buffer based on | ||
2144 | * the removed/compacted entry from the deleted index. Don't | ||
2145 | * decrement if the buffer pointer is NULL (no data stored for | ||
2146 | * this entry). | ||
2147 | */ | ||
2148 | if (adapter->scan_table[del_idx].beacon_buf) { | ||
2149 | adapter->scan_table[del_idx].beacon_buf -= | ||
2150 | beacon_buf_adj; | ||
2151 | if (adapter->scan_table[del_idx].bcn_wpa_ie) | ||
2152 | adapter->scan_table[del_idx].bcn_wpa_ie = | ||
2153 | (struct ieee_types_vendor_specific *) | ||
2154 | (adapter->scan_table[del_idx]. | ||
2155 | beacon_buf + | ||
2156 | adapter->scan_table[del_idx]. | ||
2157 | wpa_offset); | ||
2158 | if (adapter->scan_table[del_idx].bcn_rsn_ie) | ||
2159 | adapter->scan_table[del_idx].bcn_rsn_ie = | ||
2160 | (struct ieee_types_generic *) | ||
2161 | (adapter->scan_table[del_idx]. | ||
2162 | beacon_buf + | ||
2163 | adapter->scan_table[del_idx]. | ||
2164 | rsn_offset); | ||
2165 | if (adapter->scan_table[del_idx].bcn_wapi_ie) | ||
2166 | adapter->scan_table[del_idx].bcn_wapi_ie = | ||
2167 | (struct ieee_types_generic *) | ||
2168 | (adapter->scan_table[del_idx].beacon_buf | ||
2169 | + adapter->scan_table[del_idx]. | ||
2170 | wapi_offset); | ||
2171 | if (adapter->scan_table[del_idx].bcn_ht_cap) | ||
2172 | adapter->scan_table[del_idx].bcn_ht_cap = | ||
2173 | (struct ieee80211_ht_cap *) | ||
2174 | (adapter->scan_table[del_idx].beacon_buf | ||
2175 | + adapter->scan_table[del_idx]. | ||
2176 | ht_cap_offset); | ||
2177 | |||
2178 | if (adapter->scan_table[del_idx].bcn_ht_info) | ||
2179 | adapter->scan_table[del_idx].bcn_ht_info = | ||
2180 | (struct ieee80211_ht_info *) | ||
2181 | (adapter->scan_table[del_idx].beacon_buf | ||
2182 | + adapter->scan_table[del_idx]. | ||
2183 | ht_info_offset); | ||
2184 | if (adapter->scan_table[del_idx].bcn_bss_co_2040) | ||
2185 | adapter->scan_table[del_idx].bcn_bss_co_2040 = | ||
2186 | (u8 *) | ||
2187 | (adapter->scan_table[del_idx].beacon_buf | ||
2188 | + adapter->scan_table[del_idx]. | ||
2189 | bss_co_2040_offset); | ||
2190 | if (adapter->scan_table[del_idx].bcn_ext_cap) | ||
2191 | adapter->scan_table[del_idx].bcn_ext_cap = | ||
2192 | (u8 *) | ||
2193 | (adapter->scan_table[del_idx].beacon_buf | ||
2194 | + adapter->scan_table[del_idx]. | ||
2195 | ext_cap_offset); | ||
2196 | if (adapter->scan_table[del_idx].bcn_obss_scan) | ||
2197 | adapter->scan_table[del_idx]. | ||
2198 | bcn_obss_scan = | ||
2199 | (struct ieee_types_obss_scan_param *) | ||
2200 | (adapter->scan_table[del_idx].beacon_buf | ||
2201 | + adapter->scan_table[del_idx]. | ||
2202 | overlap_bss_offset); | ||
2203 | } | ||
2204 | } | ||
2205 | |||
2206 | /* The last entry is invalid now that it has been deleted or moved | ||
2207 | back */ | ||
2208 | memset(adapter->scan_table + adapter->num_in_scan_table - 1, | ||
2209 | 0x00, sizeof(struct mwifiex_bssdescriptor)); | ||
2210 | |||
2211 | adapter->num_in_scan_table--; | ||
2212 | } | ||
2213 | |||
2214 | /* | ||
2215 | * This function deletes all occurrences of a given SSID from the scan table. | ||
2216 | * | ||
2217 | * This iterates through the scan table and deletes all entries that match | ||
2218 | * the given SSID. It also compacts the remaining scan table entries. | ||
2219 | */ | ||
2220 | static int | ||
2221 | mwifiex_scan_delete_ssid_table_entry(struct mwifiex_private *priv, | ||
2222 | struct mwifiex_802_11_ssid *del_ssid) | ||
2223 | { | ||
2224 | s32 table_idx = -1; | ||
2225 | |||
2226 | dev_dbg(priv->adapter->dev, "info: scan: delete ssid entry: %-32s\n", | ||
2227 | del_ssid->ssid); | ||
2228 | |||
2229 | /* If the requested SSID is found in the table, delete it. Then keep | ||
2230 | searching the table for multiple entires for the SSID until no | ||
2231 | more are found */ | ||
2232 | while ((table_idx = mwifiex_find_ssid_in_list(priv, del_ssid, NULL, | ||
2233 | NL80211_IFTYPE_UNSPECIFIED)) >= 0) { | ||
2234 | dev_dbg(priv->adapter->dev, | ||
2235 | "info: Scan: Delete SSID Entry: Found Idx = %d\n", | ||
2236 | table_idx); | ||
2237 | mwifiex_scan_delete_table_entry(priv, table_idx); | ||
2238 | } | ||
2239 | |||
2240 | return table_idx == -1 ? -1 : 0; | ||
2241 | } | ||
2242 | |||
2243 | /* | ||
2244 | * This is an internal function used to start a scan based on an input | ||
2245 | * configuration. | ||
2246 | * | ||
2247 | * This uses the input user scan configuration information when provided in | ||
2248 | * order to send the appropriate scan commands to firmware to populate or | ||
2249 | * update the internal driver scan table. | ||
2250 | */ | ||
2251 | int mwifiex_scan_networks(struct mwifiex_private *priv, | ||
2252 | const struct mwifiex_user_scan_cfg *user_scan_in) | ||
2253 | { | ||
2254 | int ret = 0; | ||
2255 | struct mwifiex_adapter *adapter = priv->adapter; | ||
2256 | struct cmd_ctrl_node *cmd_node; | ||
2257 | union mwifiex_scan_cmd_config_tlv *scan_cfg_out; | ||
2258 | struct mwifiex_ie_types_chan_list_param_set *chan_list_out; | ||
2259 | u32 buf_size; | ||
2260 | struct mwifiex_chan_scan_param_set *scan_chan_list; | ||
2261 | u8 keep_previous_scan; | ||
2262 | u8 filtered_scan; | ||
2263 | u8 scan_current_chan_only; | ||
2264 | u8 max_chan_per_scan; | ||
2265 | unsigned long flags; | ||
2266 | |||
2267 | if (adapter->scan_processing) { | ||
2268 | dev_dbg(adapter->dev, "cmd: Scan already in process...\n"); | ||
2269 | return ret; | ||
2270 | } | ||
2271 | |||
2272 | spin_lock_irqsave(&adapter->mwifiex_cmd_lock, flags); | ||
2273 | adapter->scan_processing = true; | ||
2274 | spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, flags); | ||
2275 | |||
2276 | if (priv->scan_block) { | ||
2277 | dev_dbg(adapter->dev, | ||
2278 | "cmd: Scan is blocked during association...\n"); | ||
2279 | return ret; | ||
2280 | } | ||
2281 | |||
2282 | scan_cfg_out = kzalloc(sizeof(union mwifiex_scan_cmd_config_tlv), | ||
2283 | GFP_KERNEL); | ||
2284 | if (!scan_cfg_out) { | ||
2285 | dev_err(adapter->dev, "failed to alloc scan_cfg_out\n"); | ||
2286 | return -ENOMEM; | ||
2287 | } | ||
2288 | |||
2289 | buf_size = sizeof(struct mwifiex_chan_scan_param_set) * | ||
2290 | MWIFIEX_USER_SCAN_CHAN_MAX; | ||
2291 | scan_chan_list = kzalloc(buf_size, GFP_KERNEL); | ||
2292 | if (!scan_chan_list) { | ||
2293 | dev_err(adapter->dev, "failed to alloc scan_chan_list\n"); | ||
2294 | kfree(scan_cfg_out); | ||
2295 | return -ENOMEM; | ||
2296 | } | ||
2297 | |||
2298 | keep_previous_scan = false; | ||
2299 | |||
2300 | mwifiex_scan_setup_scan_config(priv, user_scan_in, | ||
2301 | &scan_cfg_out->config, &chan_list_out, | ||
2302 | scan_chan_list, &max_chan_per_scan, | ||
2303 | &filtered_scan, &scan_current_chan_only); | ||
2304 | |||
2305 | if (user_scan_in) | ||
2306 | keep_previous_scan = user_scan_in->keep_previous_scan; | ||
2307 | |||
2308 | |||
2309 | if (!keep_previous_scan) { | ||
2310 | memset(adapter->scan_table, 0x00, | ||
2311 | sizeof(struct mwifiex_bssdescriptor) * IW_MAX_AP); | ||
2312 | adapter->num_in_scan_table = 0; | ||
2313 | adapter->bcn_buf_end = adapter->bcn_buf; | ||
2314 | } | ||
2315 | |||
2316 | ret = mwifiex_scan_channel_list(priv, max_chan_per_scan, filtered_scan, | ||
2317 | &scan_cfg_out->config, chan_list_out, | ||
2318 | scan_chan_list); | ||
2319 | |||
2320 | /* Get scan command from scan_pending_q and put to cmd_pending_q */ | ||
2321 | if (!ret) { | ||
2322 | spin_lock_irqsave(&adapter->scan_pending_q_lock, flags); | ||
2323 | if (!list_empty(&adapter->scan_pending_q)) { | ||
2324 | cmd_node = list_first_entry(&adapter->scan_pending_q, | ||
2325 | struct cmd_ctrl_node, list); | ||
2326 | list_del(&cmd_node->list); | ||
2327 | spin_unlock_irqrestore(&adapter->scan_pending_q_lock, | ||
2328 | flags); | ||
2329 | mwifiex_insert_cmd_to_pending_q(adapter, cmd_node, | ||
2330 | true); | ||
2331 | } else { | ||
2332 | spin_unlock_irqrestore(&adapter->scan_pending_q_lock, | ||
2333 | flags); | ||
2334 | } | ||
2335 | } else { | ||
2336 | spin_lock_irqsave(&adapter->mwifiex_cmd_lock, flags); | ||
2337 | adapter->scan_processing = true; | ||
2338 | spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, flags); | ||
2339 | } | ||
2340 | |||
2341 | kfree(scan_cfg_out); | ||
2342 | kfree(scan_chan_list); | ||
2343 | return ret; | ||
2344 | } | ||
2345 | |||
2346 | /* | ||
2347 | * This function prepares a scan command to be sent to the firmware. | ||
2348 | * | ||
2349 | * This uses the scan command configuration sent to the command processing | ||
2350 | * module in command preparation stage to configure a scan command structure | ||
2351 | * to send to firmware. | ||
2352 | * | ||
2353 | * The fixed fields specifying the BSS type and BSSID filters as well as a | ||
2354 | * variable number/length of TLVs are sent in the command to firmware. | ||
2355 | * | ||
2356 | * Preparation also includes - | ||
2357 | * - Setting command ID, and proper size | ||
2358 | * - Ensuring correct endian-ness | ||
2359 | */ | ||
2360 | int mwifiex_cmd_802_11_scan(struct host_cmd_ds_command *cmd, void *data_buf) | ||
2361 | { | ||
2362 | struct host_cmd_ds_802_11_scan *scan_cmd = &cmd->params.scan; | ||
2363 | struct mwifiex_scan_cmd_config *scan_cfg; | ||
2364 | |||
2365 | scan_cfg = (struct mwifiex_scan_cmd_config *) data_buf; | ||
2366 | |||
2367 | /* Set fixed field variables in scan command */ | ||
2368 | scan_cmd->bss_mode = scan_cfg->bss_mode; | ||
2369 | memcpy(scan_cmd->bssid, scan_cfg->specific_bssid, | ||
2370 | sizeof(scan_cmd->bssid)); | ||
2371 | memcpy(scan_cmd->tlv_buffer, scan_cfg->tlv_buf, scan_cfg->tlv_buf_len); | ||
2372 | |||
2373 | cmd->command = cpu_to_le16(HostCmd_CMD_802_11_SCAN); | ||
2374 | |||
2375 | /* Size is equal to the sizeof(fixed portions) + the TLV len + header */ | ||
2376 | cmd->size = cpu_to_le16((u16) (sizeof(scan_cmd->bss_mode) | ||
2377 | + sizeof(scan_cmd->bssid) | ||
2378 | + scan_cfg->tlv_buf_len + S_DS_GEN)); | ||
2379 | |||
2380 | return 0; | ||
2381 | } | ||
2382 | |||
2383 | /* | ||
2384 | * This function handles the command response of scan. | ||
2385 | * | ||
2386 | * The response buffer for the scan command has the following | ||
2387 | * memory layout: | ||
2388 | * | ||
2389 | * .-------------------------------------------------------------. | ||
2390 | * | Header (4 * sizeof(t_u16)): Standard command response hdr | | ||
2391 | * .-------------------------------------------------------------. | ||
2392 | * | BufSize (t_u16) : sizeof the BSS Description data | | ||
2393 | * .-------------------------------------------------------------. | ||
2394 | * | NumOfSet (t_u8) : Number of BSS Descs returned | | ||
2395 | * .-------------------------------------------------------------. | ||
2396 | * | BSSDescription data (variable, size given in BufSize) | | ||
2397 | * .-------------------------------------------------------------. | ||
2398 | * | TLV data (variable, size calculated using Header->Size, | | ||
2399 | * | BufSize and sizeof the fixed fields above) | | ||
2400 | * .-------------------------------------------------------------. | ||
2401 | */ | ||
2402 | int mwifiex_ret_802_11_scan(struct mwifiex_private *priv, | ||
2403 | struct host_cmd_ds_command *resp) | ||
2404 | { | ||
2405 | int ret = 0; | ||
2406 | struct mwifiex_adapter *adapter = priv->adapter; | ||
2407 | struct cmd_ctrl_node *cmd_node; | ||
2408 | struct host_cmd_ds_802_11_scan_rsp *scan_rsp; | ||
2409 | struct mwifiex_bssdescriptor *bss_new_entry = NULL; | ||
2410 | struct mwifiex_ie_types_data *tlv_data; | ||
2411 | struct mwifiex_ie_types_tsf_timestamp *tsf_tlv; | ||
2412 | u8 *bss_info; | ||
2413 | u32 scan_resp_size; | ||
2414 | u32 bytes_left; | ||
2415 | u32 num_in_table; | ||
2416 | u32 bss_idx; | ||
2417 | u32 idx; | ||
2418 | u32 tlv_buf_size; | ||
2419 | long long tsf_val; | ||
2420 | struct mwifiex_chan_freq_power *cfp; | ||
2421 | struct mwifiex_ie_types_chan_band_list_param_set *chan_band_tlv; | ||
2422 | struct chan_band_param_set *chan_band; | ||
2423 | u8 band; | ||
2424 | u8 is_bgscan_resp; | ||
2425 | unsigned long flags; | ||
2426 | |||
2427 | is_bgscan_resp = (le16_to_cpu(resp->command) | ||
2428 | == HostCmd_CMD_802_11_BG_SCAN_QUERY); | ||
2429 | if (is_bgscan_resp) | ||
2430 | scan_rsp = &resp->params.bg_scan_query_resp.scan_resp; | ||
2431 | else | ||
2432 | scan_rsp = &resp->params.scan_resp; | ||
2433 | |||
2434 | |||
2435 | if (scan_rsp->number_of_sets > IW_MAX_AP) { | ||
2436 | dev_err(adapter->dev, "SCAN_RESP: too many AP returned (%d)\n", | ||
2437 | scan_rsp->number_of_sets); | ||
2438 | ret = -1; | ||
2439 | goto done; | ||
2440 | } | ||
2441 | |||
2442 | bytes_left = le16_to_cpu(scan_rsp->bss_descript_size); | ||
2443 | dev_dbg(adapter->dev, "info: SCAN_RESP: bss_descript_size %d\n", | ||
2444 | bytes_left); | ||
2445 | |||
2446 | scan_resp_size = le16_to_cpu(resp->size); | ||
2447 | |||
2448 | dev_dbg(adapter->dev, | ||
2449 | "info: SCAN_RESP: returned %d APs before parsing\n", | ||
2450 | scan_rsp->number_of_sets); | ||
2451 | |||
2452 | num_in_table = adapter->num_in_scan_table; | ||
2453 | bss_info = scan_rsp->bss_desc_and_tlv_buffer; | ||
2454 | |||
2455 | /* | ||
2456 | * The size of the TLV buffer is equal to the entire command response | ||
2457 | * size (scan_resp_size) minus the fixed fields (sizeof()'s), the | ||
2458 | * BSS Descriptions (bss_descript_size as bytesLef) and the command | ||
2459 | * response header (S_DS_GEN) | ||
2460 | */ | ||
2461 | tlv_buf_size = scan_resp_size - (bytes_left | ||
2462 | + sizeof(scan_rsp->bss_descript_size) | ||
2463 | + sizeof(scan_rsp->number_of_sets) | ||
2464 | + S_DS_GEN); | ||
2465 | |||
2466 | tlv_data = (struct mwifiex_ie_types_data *) (scan_rsp-> | ||
2467 | bss_desc_and_tlv_buffer + | ||
2468 | bytes_left); | ||
2469 | |||
2470 | /* Search the TLV buffer space in the scan response for any valid | ||
2471 | TLVs */ | ||
2472 | mwifiex_ret_802_11_scan_get_tlv_ptrs(adapter, tlv_data, tlv_buf_size, | ||
2473 | TLV_TYPE_TSFTIMESTAMP, | ||
2474 | (struct mwifiex_ie_types_data **) | ||
2475 | &tsf_tlv); | ||
2476 | |||
2477 | /* Search the TLV buffer space in the scan response for any valid | ||
2478 | TLVs */ | ||
2479 | mwifiex_ret_802_11_scan_get_tlv_ptrs(adapter, tlv_data, tlv_buf_size, | ||
2480 | TLV_TYPE_CHANNELBANDLIST, | ||
2481 | (struct mwifiex_ie_types_data **) | ||
2482 | &chan_band_tlv); | ||
2483 | |||
2484 | /* | ||
2485 | * Process each scan response returned (scan_rsp->number_of_sets). | ||
2486 | * Save the information in the bss_new_entry and then insert into the | ||
2487 | * driver scan table either as an update to an existing entry | ||
2488 | * or as an addition at the end of the table | ||
2489 | */ | ||
2490 | bss_new_entry = kzalloc(sizeof(struct mwifiex_bssdescriptor), | ||
2491 | GFP_KERNEL); | ||
2492 | if (!bss_new_entry) { | ||
2493 | dev_err(adapter->dev, " failed to alloc bss_new_entry\n"); | ||
2494 | return -ENOMEM; | ||
2495 | } | ||
2496 | |||
2497 | for (idx = 0; idx < scan_rsp->number_of_sets && bytes_left; idx++) { | ||
2498 | /* Zero out the bss_new_entry we are about to store info in */ | ||
2499 | memset(bss_new_entry, 0x00, | ||
2500 | sizeof(struct mwifiex_bssdescriptor)); | ||
2501 | |||
2502 | if (mwifiex_interpret_bss_desc_with_ie(adapter, bss_new_entry, | ||
2503 | &bss_info, | ||
2504 | &bytes_left)) { | ||
2505 | /* Error parsing/interpreting scan response, skipped */ | ||
2506 | dev_err(adapter->dev, "SCAN_RESP: " | ||
2507 | "mwifiex_interpret_bss_desc_with_ie " | ||
2508 | "returned ERROR\n"); | ||
2509 | continue; | ||
2510 | } | ||
2511 | |||
2512 | /* Process the data fields and IEs returned for this BSS */ | ||
2513 | dev_dbg(adapter->dev, "info: SCAN_RESP: BSSID = %pM\n", | ||
2514 | bss_new_entry->mac_address); | ||
2515 | |||
2516 | /* Search the scan table for the same bssid */ | ||
2517 | for (bss_idx = 0; bss_idx < num_in_table; bss_idx++) { | ||
2518 | if (memcmp(bss_new_entry->mac_address, | ||
2519 | adapter->scan_table[bss_idx].mac_address, | ||
2520 | sizeof(bss_new_entry->mac_address))) { | ||
2521 | continue; | ||
2522 | } | ||
2523 | /* | ||
2524 | * If the SSID matches as well, it is a | ||
2525 | * duplicate of this entry. Keep the bss_idx | ||
2526 | * set to this entry so we replace the old | ||
2527 | * contents in the table | ||
2528 | */ | ||
2529 | if ((bss_new_entry->ssid.ssid_len | ||
2530 | == adapter->scan_table[bss_idx]. ssid.ssid_len) | ||
2531 | && (!memcmp(bss_new_entry->ssid.ssid, | ||
2532 | adapter->scan_table[bss_idx].ssid.ssid, | ||
2533 | bss_new_entry->ssid.ssid_len))) { | ||
2534 | dev_dbg(adapter->dev, "info: SCAN_RESP:" | ||
2535 | " duplicate of index: %d\n", bss_idx); | ||
2536 | break; | ||
2537 | } | ||
2538 | } | ||
2539 | /* | ||
2540 | * If the bss_idx is equal to the number of entries in | ||
2541 | * the table, the new entry was not a duplicate; append | ||
2542 | * it to the scan table | ||
2543 | */ | ||
2544 | if (bss_idx == num_in_table) { | ||
2545 | /* Range check the bss_idx, keep it limited to | ||
2546 | the last entry */ | ||
2547 | if (bss_idx == IW_MAX_AP) | ||
2548 | bss_idx--; | ||
2549 | else | ||
2550 | num_in_table++; | ||
2551 | } | ||
2552 | |||
2553 | /* | ||
2554 | * Save the beacon/probe response returned for later application | ||
2555 | * retrieval. Duplicate beacon/probe responses are updated if | ||
2556 | * possible | ||
2557 | */ | ||
2558 | mwifiex_ret_802_11_scan_store_beacon(priv, bss_idx, | ||
2559 | num_in_table, bss_new_entry); | ||
2560 | /* | ||
2561 | * If the TSF TLV was appended to the scan results, save this | ||
2562 | * entry's TSF value in the networkTSF field.The networkTSF is | ||
2563 | * the firmware's TSF value at the time the beacon or probe | ||
2564 | * response was received. | ||
2565 | */ | ||
2566 | if (tsf_tlv) { | ||
2567 | memcpy(&tsf_val, &tsf_tlv->tsf_data[idx * TSF_DATA_SIZE] | ||
2568 | , sizeof(tsf_val)); | ||
2569 | memcpy(&bss_new_entry->network_tsf, &tsf_val, | ||
2570 | sizeof(bss_new_entry->network_tsf)); | ||
2571 | } | ||
2572 | band = BAND_G; | ||
2573 | if (chan_band_tlv) { | ||
2574 | chan_band = &chan_band_tlv->chan_band_param[idx]; | ||
2575 | band = mwifiex_radio_type_to_band(chan_band->radio_type | ||
2576 | & (BIT(0) | BIT(1))); | ||
2577 | } | ||
2578 | |||
2579 | /* Save the band designation for this entry for use in join */ | ||
2580 | bss_new_entry->bss_band = band; | ||
2581 | cfp = mwifiex_get_cfp_by_band_and_channel_from_cfg80211(priv, | ||
2582 | (u8) bss_new_entry->bss_band, | ||
2583 | (u16)bss_new_entry->channel); | ||
2584 | |||
2585 | if (cfp) | ||
2586 | bss_new_entry->freq = cfp->freq; | ||
2587 | else | ||
2588 | bss_new_entry->freq = 0; | ||
2589 | |||
2590 | /* Copy the locally created bss_new_entry to the scan table */ | ||
2591 | memcpy(&adapter->scan_table[bss_idx], bss_new_entry, | ||
2592 | sizeof(adapter->scan_table[bss_idx])); | ||
2593 | |||
2594 | } | ||
2595 | |||
2596 | dev_dbg(adapter->dev, | ||
2597 | "info: SCAN_RESP: Scanned %2d APs, %d valid, %d total\n", | ||
2598 | scan_rsp->number_of_sets, | ||
2599 | num_in_table - adapter->num_in_scan_table, num_in_table); | ||
2600 | |||
2601 | /* Update the total number of BSSIDs in the scan table */ | ||
2602 | adapter->num_in_scan_table = num_in_table; | ||
2603 | |||
2604 | spin_lock_irqsave(&adapter->scan_pending_q_lock, flags); | ||
2605 | if (list_empty(&adapter->scan_pending_q)) { | ||
2606 | spin_unlock_irqrestore(&adapter->scan_pending_q_lock, flags); | ||
2607 | spin_lock_irqsave(&adapter->mwifiex_cmd_lock, flags); | ||
2608 | adapter->scan_processing = false; | ||
2609 | spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, flags); | ||
2610 | /* | ||
2611 | * Process the resulting scan table: | ||
2612 | * - Remove any bad ssids | ||
2613 | * - Update our current BSS information from scan data | ||
2614 | */ | ||
2615 | mwifiex_process_scan_results(priv); | ||
2616 | |||
2617 | /* Need to indicate IOCTL complete */ | ||
2618 | if (adapter->curr_cmd->wait_q_enabled) { | ||
2619 | adapter->cmd_wait_q.status = 0; | ||
2620 | mwifiex_complete_cmd(adapter); | ||
2621 | } | ||
2622 | if (priv->report_scan_result) | ||
2623 | priv->report_scan_result = false; | ||
2624 | if (priv->scan_pending_on_block) { | ||
2625 | priv->scan_pending_on_block = false; | ||
2626 | up(&priv->async_sem); | ||
2627 | } | ||
2628 | |||
2629 | } else { | ||
2630 | /* Get scan command from scan_pending_q and put to | ||
2631 | cmd_pending_q */ | ||
2632 | cmd_node = list_first_entry(&adapter->scan_pending_q, | ||
2633 | struct cmd_ctrl_node, list); | ||
2634 | list_del(&cmd_node->list); | ||
2635 | spin_unlock_irqrestore(&adapter->scan_pending_q_lock, flags); | ||
2636 | |||
2637 | mwifiex_insert_cmd_to_pending_q(adapter, cmd_node, true); | ||
2638 | } | ||
2639 | |||
2640 | done: | ||
2641 | kfree((u8 *) bss_new_entry); | ||
2642 | return ret; | ||
2643 | } | ||
2644 | |||
2645 | /* | ||
2646 | * This function prepares command for background scan query. | ||
2647 | * | ||
2648 | * Preparation includes - | ||
2649 | * - Setting command ID and proper size | ||
2650 | * - Setting background scan flush parameter | ||
2651 | * - Ensuring correct endian-ness | ||
2652 | */ | ||
2653 | int mwifiex_cmd_802_11_bg_scan_query(struct host_cmd_ds_command *cmd) | ||
2654 | { | ||
2655 | struct host_cmd_ds_802_11_bg_scan_query *bg_query = | ||
2656 | &cmd->params.bg_scan_query; | ||
2657 | |||
2658 | cmd->command = cpu_to_le16(HostCmd_CMD_802_11_BG_SCAN_QUERY); | ||
2659 | cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_802_11_bg_scan_query) | ||
2660 | + S_DS_GEN); | ||
2661 | |||
2662 | bg_query->flush = 1; | ||
2663 | |||
2664 | return 0; | ||
2665 | } | ||
2666 | |||
2667 | /* | ||
2668 | * This function finds a SSID in the scan table. | ||
2669 | * | ||
2670 | * A BSSID may optionally be provided to qualify the SSID. | ||
2671 | * For non-Auto mode, further check is made to make sure the | ||
2672 | * BSS found in the scan table is compatible with the current | ||
2673 | * settings of the driver. | ||
2674 | */ | ||
2675 | s32 | ||
2676 | mwifiex_find_ssid_in_list(struct mwifiex_private *priv, | ||
2677 | struct mwifiex_802_11_ssid *ssid, u8 *bssid, | ||
2678 | u32 mode) | ||
2679 | { | ||
2680 | struct mwifiex_adapter *adapter = priv->adapter; | ||
2681 | s32 net = -1, j; | ||
2682 | u8 best_rssi = 0; | ||
2683 | u32 i; | ||
2684 | |||
2685 | dev_dbg(adapter->dev, "info: num of entries in table = %d\n", | ||
2686 | adapter->num_in_scan_table); | ||
2687 | |||
2688 | /* | ||
2689 | * Loop through the table until the maximum is reached or until a match | ||
2690 | * is found based on the bssid field comparison | ||
2691 | */ | ||
2692 | for (i = 0; | ||
2693 | i < adapter->num_in_scan_table && (!bssid || (bssid && net < 0)); | ||
2694 | i++) { | ||
2695 | if (!mwifiex_ssid_cmp(&adapter->scan_table[i].ssid, ssid) && | ||
2696 | (!bssid | ||
2697 | || !memcmp(adapter->scan_table[i].mac_address, bssid, | ||
2698 | ETH_ALEN)) | ||
2699 | && | ||
2700 | (mwifiex_get_cfp_by_band_and_channel_from_cfg80211 | ||
2701 | (priv, (u8) adapter->scan_table[i].bss_band, | ||
2702 | (u16) adapter->scan_table[i].channel))) { | ||
2703 | switch (mode) { | ||
2704 | case NL80211_IFTYPE_STATION: | ||
2705 | case NL80211_IFTYPE_ADHOC: | ||
2706 | j = mwifiex_is_network_compatible(priv, i, | ||
2707 | mode); | ||
2708 | |||
2709 | if (j >= 0) { | ||
2710 | if (SCAN_RSSI | ||
2711 | (adapter->scan_table[i].rssi) > | ||
2712 | best_rssi) { | ||
2713 | best_rssi = SCAN_RSSI(adapter-> | ||
2714 | scan_table | ||
2715 | [i].rssi); | ||
2716 | net = i; | ||
2717 | } | ||
2718 | } else { | ||
2719 | if (net == -1) | ||
2720 | net = j; | ||
2721 | } | ||
2722 | break; | ||
2723 | case NL80211_IFTYPE_UNSPECIFIED: | ||
2724 | default: | ||
2725 | /* | ||
2726 | * Do not check compatibility if the mode | ||
2727 | * requested is Auto/Unknown. Allows generic | ||
2728 | * find to work without verifying against the | ||
2729 | * Adapter security settings | ||
2730 | */ | ||
2731 | if (SCAN_RSSI(adapter->scan_table[i].rssi) > | ||
2732 | best_rssi) { | ||
2733 | best_rssi = SCAN_RSSI(adapter-> | ||
2734 | scan_table[i].rssi); | ||
2735 | net = i; | ||
2736 | } | ||
2737 | break; | ||
2738 | } | ||
2739 | } | ||
2740 | } | ||
2741 | |||
2742 | return net; | ||
2743 | } | ||
2744 | |||
2745 | /* | ||
2746 | * This function finds a specific compatible BSSID in the scan list. | ||
2747 | * | ||
2748 | * This function loops through the scan table looking for a compatible | ||
2749 | * match. If a BSSID matches, but the BSS is found to be not compatible | ||
2750 | * the function ignores it and continues to search through the rest of | ||
2751 | * the entries in case there is an AP with multiple SSIDs assigned to | ||
2752 | * the same BSSID. | ||
2753 | */ | ||
2754 | s32 | ||
2755 | mwifiex_find_bssid_in_list(struct mwifiex_private *priv, u8 *bssid, | ||
2756 | u32 mode) | ||
2757 | { | ||
2758 | struct mwifiex_adapter *adapter = priv->adapter; | ||
2759 | s32 net = -1; | ||
2760 | u32 i; | ||
2761 | |||
2762 | if (!bssid) | ||
2763 | return -1; | ||
2764 | |||
2765 | dev_dbg(adapter->dev, "info: FindBSSID: Num of BSSIDs = %d\n", | ||
2766 | adapter->num_in_scan_table); | ||
2767 | |||
2768 | /* | ||
2769 | * Look through the scan table for a compatible match. The ret return | ||
2770 | * variable will be equal to the index in the scan table (greater | ||
2771 | * than zero) if the network is compatible. The loop will continue | ||
2772 | * past a matched bssid that is not compatible in case there is an | ||
2773 | * AP with multiple SSIDs assigned to the same BSSID | ||
2774 | */ | ||
2775 | for (i = 0; net < 0 && i < adapter->num_in_scan_table; i++) { | ||
2776 | if (!memcmp | ||
2777 | (adapter->scan_table[i].mac_address, bssid, ETH_ALEN) | ||
2778 | && mwifiex_get_cfp_by_band_and_channel_from_cfg80211 | ||
2779 | (priv, | ||
2780 | (u8) adapter-> | ||
2781 | scan_table[i]. | ||
2782 | bss_band, | ||
2783 | (u16) adapter-> | ||
2784 | scan_table[i]. | ||
2785 | channel)) { | ||
2786 | switch (mode) { | ||
2787 | case NL80211_IFTYPE_STATION: | ||
2788 | case NL80211_IFTYPE_ADHOC: | ||
2789 | net = mwifiex_is_network_compatible(priv, i, | ||
2790 | mode); | ||
2791 | break; | ||
2792 | default: | ||
2793 | net = i; | ||
2794 | break; | ||
2795 | } | ||
2796 | } | ||
2797 | } | ||
2798 | |||
2799 | return net; | ||
2800 | } | ||
2801 | |||
2802 | /* | ||
2803 | * This function inserts scan command node to the scan pending queue. | ||
2804 | */ | ||
2805 | void | ||
2806 | mwifiex_queue_scan_cmd(struct mwifiex_private *priv, | ||
2807 | struct cmd_ctrl_node *cmd_node) | ||
2808 | { | ||
2809 | struct mwifiex_adapter *adapter = priv->adapter; | ||
2810 | unsigned long flags; | ||
2811 | |||
2812 | cmd_node->wait_q_enabled = true; | ||
2813 | spin_lock_irqsave(&adapter->scan_pending_q_lock, flags); | ||
2814 | list_add_tail(&cmd_node->list, &adapter->scan_pending_q); | ||
2815 | spin_unlock_irqrestore(&adapter->scan_pending_q_lock, flags); | ||
2816 | } | ||
2817 | |||
2818 | /* | ||
2819 | * This function finds an AP with specific ssid in the scan list. | ||
2820 | */ | ||
2821 | int mwifiex_find_best_network(struct mwifiex_private *priv, | ||
2822 | struct mwifiex_ssid_bssid *req_ssid_bssid) | ||
2823 | { | ||
2824 | struct mwifiex_adapter *adapter = priv->adapter; | ||
2825 | struct mwifiex_bssdescriptor *req_bss; | ||
2826 | s32 i; | ||
2827 | |||
2828 | memset(req_ssid_bssid, 0, sizeof(struct mwifiex_ssid_bssid)); | ||
2829 | |||
2830 | i = mwifiex_find_best_network_in_list(priv); | ||
2831 | |||
2832 | if (i >= 0) { | ||
2833 | req_bss = &adapter->scan_table[i]; | ||
2834 | memcpy(&req_ssid_bssid->ssid, &req_bss->ssid, | ||
2835 | sizeof(struct mwifiex_802_11_ssid)); | ||
2836 | memcpy((u8 *) &req_ssid_bssid->bssid, | ||
2837 | (u8 *) &req_bss->mac_address, ETH_ALEN); | ||
2838 | |||
2839 | /* Make sure we are in the right mode */ | ||
2840 | if (priv->bss_mode == NL80211_IFTYPE_UNSPECIFIED) | ||
2841 | priv->bss_mode = req_bss->bss_mode; | ||
2842 | } | ||
2843 | |||
2844 | if (!req_ssid_bssid->ssid.ssid_len) | ||
2845 | return -1; | ||
2846 | |||
2847 | dev_dbg(adapter->dev, "info: Best network found = [%s], " | ||
2848 | "[%pM]\n", req_ssid_bssid->ssid.ssid, | ||
2849 | req_ssid_bssid->bssid); | ||
2850 | |||
2851 | return 0; | ||
2852 | } | ||
2853 | |||
2854 | /* | ||
2855 | * This function sends a scan command for all available channels to the | ||
2856 | * firmware, filtered on a specific SSID. | ||
2857 | */ | ||
2858 | static int mwifiex_scan_specific_ssid(struct mwifiex_private *priv, | ||
2859 | struct mwifiex_802_11_ssid *req_ssid) | ||
2860 | { | ||
2861 | struct mwifiex_adapter *adapter = priv->adapter; | ||
2862 | int ret = 0; | ||
2863 | struct mwifiex_user_scan_cfg *scan_cfg; | ||
2864 | |||
2865 | if (!req_ssid) | ||
2866 | return -1; | ||
2867 | |||
2868 | if (adapter->scan_processing) { | ||
2869 | dev_dbg(adapter->dev, "cmd: Scan already in process...\n"); | ||
2870 | return ret; | ||
2871 | } | ||
2872 | |||
2873 | if (priv->scan_block) { | ||
2874 | dev_dbg(adapter->dev, | ||
2875 | "cmd: Scan is blocked during association...\n"); | ||
2876 | return ret; | ||
2877 | } | ||
2878 | |||
2879 | mwifiex_scan_delete_ssid_table_entry(priv, req_ssid); | ||
2880 | |||
2881 | scan_cfg = kzalloc(sizeof(struct mwifiex_user_scan_cfg), GFP_KERNEL); | ||
2882 | if (!scan_cfg) { | ||
2883 | dev_err(adapter->dev, "failed to alloc scan_cfg\n"); | ||
2884 | return -ENOMEM; | ||
2885 | } | ||
2886 | |||
2887 | memcpy(scan_cfg->ssid_list[0].ssid, req_ssid->ssid, | ||
2888 | req_ssid->ssid_len); | ||
2889 | scan_cfg->keep_previous_scan = true; | ||
2890 | |||
2891 | ret = mwifiex_scan_networks(priv, scan_cfg); | ||
2892 | |||
2893 | kfree(scan_cfg); | ||
2894 | return ret; | ||
2895 | } | ||
2896 | |||
2897 | /* | ||
2898 | * Sends IOCTL request to start a scan. | ||
2899 | * | ||
2900 | * This function allocates the IOCTL request buffer, fills it | ||
2901 | * with requisite parameters and calls the IOCTL handler. | ||
2902 | * | ||
2903 | * Scan command can be issued for both normal scan and specific SSID | ||
2904 | * scan, depending upon whether an SSID is provided or not. | ||
2905 | */ | ||
2906 | int mwifiex_request_scan(struct mwifiex_private *priv, | ||
2907 | struct mwifiex_802_11_ssid *req_ssid) | ||
2908 | { | ||
2909 | int ret; | ||
2910 | |||
2911 | if (down_interruptible(&priv->async_sem)) { | ||
2912 | dev_err(priv->adapter->dev, "%s: acquire semaphore\n", | ||
2913 | __func__); | ||
2914 | return -1; | ||
2915 | } | ||
2916 | priv->scan_pending_on_block = true; | ||
2917 | |||
2918 | priv->adapter->cmd_wait_q.condition = false; | ||
2919 | |||
2920 | if (req_ssid && req_ssid->ssid_len != 0) | ||
2921 | /* Specific SSID scan */ | ||
2922 | ret = mwifiex_scan_specific_ssid(priv, req_ssid); | ||
2923 | else | ||
2924 | /* Normal scan */ | ||
2925 | ret = mwifiex_scan_networks(priv, NULL); | ||
2926 | |||
2927 | if (!ret) | ||
2928 | ret = mwifiex_wait_queue_complete(priv->adapter); | ||
2929 | |||
2930 | if (ret == -1) { | ||
2931 | priv->scan_pending_on_block = false; | ||
2932 | up(&priv->async_sem); | ||
2933 | } | ||
2934 | |||
2935 | return ret; | ||
2936 | } | ||
2937 | |||
2938 | /* | ||
2939 | * This function appends the vendor specific IE TLV to a buffer. | ||
2940 | */ | ||
2941 | int | ||
2942 | mwifiex_cmd_append_vsie_tlv(struct mwifiex_private *priv, | ||
2943 | u16 vsie_mask, u8 **buffer) | ||
2944 | { | ||
2945 | int id, ret_len = 0; | ||
2946 | struct mwifiex_ie_types_vendor_param_set *vs_param_set; | ||
2947 | |||
2948 | if (!buffer) | ||
2949 | return 0; | ||
2950 | if (!(*buffer)) | ||
2951 | return 0; | ||
2952 | |||
2953 | /* | ||
2954 | * Traverse through the saved vendor specific IE array and append | ||
2955 | * the selected(scan/assoc/adhoc) IE as TLV to the command | ||
2956 | */ | ||
2957 | for (id = 0; id < MWIFIEX_MAX_VSIE_NUM; id++) { | ||
2958 | if (priv->vs_ie[id].mask & vsie_mask) { | ||
2959 | vs_param_set = | ||
2960 | (struct mwifiex_ie_types_vendor_param_set *) | ||
2961 | *buffer; | ||
2962 | vs_param_set->header.type = | ||
2963 | cpu_to_le16(TLV_TYPE_PASSTHROUGH); | ||
2964 | vs_param_set->header.len = | ||
2965 | cpu_to_le16((((u16) priv->vs_ie[id].ie[1]) | ||
2966 | & 0x00FF) + 2); | ||
2967 | memcpy(vs_param_set->ie, priv->vs_ie[id].ie, | ||
2968 | le16_to_cpu(vs_param_set->header.len)); | ||
2969 | *buffer += le16_to_cpu(vs_param_set->header.len) + | ||
2970 | sizeof(struct mwifiex_ie_types_header); | ||
2971 | ret_len += le16_to_cpu(vs_param_set->header.len) + | ||
2972 | sizeof(struct mwifiex_ie_types_header); | ||
2973 | } | ||
2974 | } | ||
2975 | return ret_len; | ||
2976 | } | ||
2977 | |||
2978 | /* | ||
2979 | * This function saves a beacon buffer of the current BSS descriptor. | ||
2980 | * | ||
2981 | * The current beacon buffer is saved so that it can be restored in the | ||
2982 | * following cases that makes the beacon buffer not to contain the current | ||
2983 | * ssid's beacon buffer. | ||
2984 | * - The current ssid was not found somehow in the last scan. | ||
2985 | * - The current ssid was the last entry of the scan table and overloaded. | ||
2986 | */ | ||
2987 | void | ||
2988 | mwifiex_save_curr_bcn(struct mwifiex_private *priv) | ||
2989 | { | ||
2990 | struct mwifiex_bssdescriptor *curr_bss = | ||
2991 | &priv->curr_bss_params.bss_descriptor; | ||
2992 | |||
2993 | if (!curr_bss->beacon_buf_size) | ||
2994 | return; | ||
2995 | |||
2996 | /* allocate beacon buffer at 1st time; or if it's size has changed */ | ||
2997 | if (!priv->curr_bcn_buf || | ||
2998 | priv->curr_bcn_size != curr_bss->beacon_buf_size) { | ||
2999 | priv->curr_bcn_size = curr_bss->beacon_buf_size; | ||
3000 | |||
3001 | kfree(priv->curr_bcn_buf); | ||
3002 | priv->curr_bcn_buf = kzalloc(curr_bss->beacon_buf_size, | ||
3003 | GFP_KERNEL); | ||
3004 | if (!priv->curr_bcn_buf) { | ||
3005 | dev_err(priv->adapter->dev, | ||
3006 | "failed to alloc curr_bcn_buf\n"); | ||
3007 | return; | ||
3008 | } | ||
3009 | } | ||
3010 | |||
3011 | memcpy(priv->curr_bcn_buf, curr_bss->beacon_buf, | ||
3012 | curr_bss->beacon_buf_size); | ||
3013 | dev_dbg(priv->adapter->dev, "info: current beacon saved %d\n", | ||
3014 | priv->curr_bcn_size); | ||
3015 | } | ||
3016 | |||
3017 | /* | ||
3018 | * This function frees the current BSS descriptor beacon buffer. | ||
3019 | */ | ||
3020 | void | ||
3021 | mwifiex_free_curr_bcn(struct mwifiex_private *priv) | ||
3022 | { | ||
3023 | kfree(priv->curr_bcn_buf); | ||
3024 | priv->curr_bcn_buf = NULL; | ||
3025 | } | ||
diff --git a/drivers/net/wireless/mwifiex/sdio.c b/drivers/net/wireless/mwifiex/sdio.c new file mode 100644 index 000000000000..d425dbd91d19 --- /dev/null +++ b/drivers/net/wireless/mwifiex/sdio.c | |||
@@ -0,0 +1,1754 @@ | |||
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; | ||
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 | kfree(card); | ||
72 | return -EIO; | ||
73 | } | ||
74 | |||
75 | if (mwifiex_add_card(card, &add_remove_card_sem, &sdio_ops)) { | ||
76 | pr_err("%s: add card failed\n", __func__); | ||
77 | kfree(card); | ||
78 | sdio_claim_host(func); | ||
79 | ret = sdio_disable_func(func); | ||
80 | sdio_release_host(func); | ||
81 | ret = -1; | ||
82 | } | ||
83 | |||
84 | return ret; | ||
85 | } | ||
86 | |||
87 | /* | ||
88 | * SDIO remove. | ||
89 | * | ||
90 | * This function removes the interface and frees up the card structure. | ||
91 | */ | ||
92 | static void | ||
93 | mwifiex_sdio_remove(struct sdio_func *func) | ||
94 | { | ||
95 | struct sdio_mmc_card *card; | ||
96 | |||
97 | pr_debug("info: SDIO func num=%d\n", func->num); | ||
98 | |||
99 | if (func) { | ||
100 | card = sdio_get_drvdata(func); | ||
101 | if (card) { | ||
102 | mwifiex_remove_card(card->adapter, | ||
103 | &add_remove_card_sem); | ||
104 | kfree(card); | ||
105 | } | ||
106 | } | ||
107 | } | ||
108 | |||
109 | /* | ||
110 | * SDIO suspend. | ||
111 | * | ||
112 | * Kernel needs to suspend all functions separately. Therefore all | ||
113 | * registered functions must have drivers with suspend and resume | ||
114 | * methods. Failing that the kernel simply removes the whole card. | ||
115 | * | ||
116 | * If already not suspended, this function allocates and sends a host | ||
117 | * sleep activate request to the firmware and turns off the traffic. | ||
118 | */ | ||
119 | static int mwifiex_sdio_suspend(struct device *dev) | ||
120 | { | ||
121 | struct sdio_func *func = dev_to_sdio_func(dev); | ||
122 | struct sdio_mmc_card *card; | ||
123 | struct mwifiex_adapter *adapter; | ||
124 | mmc_pm_flag_t pm_flag = 0; | ||
125 | int hs_actived = 0; | ||
126 | int i; | ||
127 | int ret = 0; | ||
128 | |||
129 | if (func) { | ||
130 | pm_flag = sdio_get_host_pm_caps(func); | ||
131 | pr_debug("cmd: %s: suspend: PM flag = 0x%x\n", | ||
132 | sdio_func_id(func), pm_flag); | ||
133 | if (!(pm_flag & MMC_PM_KEEP_POWER)) { | ||
134 | pr_err("%s: cannot remain alive while host is" | ||
135 | " suspended\n", sdio_func_id(func)); | ||
136 | return -ENOSYS; | ||
137 | } | ||
138 | |||
139 | card = sdio_get_drvdata(func); | ||
140 | if (!card || !card->adapter) { | ||
141 | pr_err("suspend: invalid card or adapter\n"); | ||
142 | return 0; | ||
143 | } | ||
144 | } else { | ||
145 | pr_err("suspend: sdio_func is not specified\n"); | ||
146 | return 0; | ||
147 | } | ||
148 | |||
149 | adapter = card->adapter; | ||
150 | |||
151 | /* Enable the Host Sleep */ | ||
152 | hs_actived = mwifiex_enable_hs(adapter); | ||
153 | if (hs_actived) { | ||
154 | pr_debug("cmd: suspend with MMC_PM_KEEP_POWER\n"); | ||
155 | ret = sdio_set_host_pm_flags(func, MMC_PM_KEEP_POWER); | ||
156 | } | ||
157 | |||
158 | /* Indicate device suspended */ | ||
159 | adapter->is_suspended = true; | ||
160 | |||
161 | for (i = 0; i < adapter->priv_num; i++) | ||
162 | netif_carrier_off(adapter->priv[i]->netdev); | ||
163 | |||
164 | return ret; | ||
165 | } | ||
166 | |||
167 | /* | ||
168 | * SDIO resume. | ||
169 | * | ||
170 | * Kernel needs to suspend all functions separately. Therefore all | ||
171 | * registered functions must have drivers with suspend and resume | ||
172 | * methods. Failing that the kernel simply removes the whole card. | ||
173 | * | ||
174 | * If already not resumed, this function turns on the traffic and | ||
175 | * sends a host sleep cancel request to the firmware. | ||
176 | */ | ||
177 | static int mwifiex_sdio_resume(struct device *dev) | ||
178 | { | ||
179 | struct sdio_func *func = dev_to_sdio_func(dev); | ||
180 | struct sdio_mmc_card *card; | ||
181 | struct mwifiex_adapter *adapter; | ||
182 | mmc_pm_flag_t pm_flag = 0; | ||
183 | int i; | ||
184 | |||
185 | if (func) { | ||
186 | pm_flag = sdio_get_host_pm_caps(func); | ||
187 | card = sdio_get_drvdata(func); | ||
188 | if (!card || !card->adapter) { | ||
189 | pr_err("resume: invalid card or adapter\n"); | ||
190 | return 0; | ||
191 | } | ||
192 | } else { | ||
193 | pr_err("resume: sdio_func is not specified\n"); | ||
194 | return 0; | ||
195 | } | ||
196 | |||
197 | adapter = card->adapter; | ||
198 | |||
199 | if (!adapter->is_suspended) { | ||
200 | dev_warn(adapter->dev, "device already resumed\n"); | ||
201 | return 0; | ||
202 | } | ||
203 | |||
204 | adapter->is_suspended = false; | ||
205 | |||
206 | for (i = 0; i < adapter->priv_num; i++) | ||
207 | if (adapter->priv[i]->media_connected) | ||
208 | netif_carrier_on(adapter->priv[i]->netdev); | ||
209 | |||
210 | /* Disable Host Sleep */ | ||
211 | mwifiex_cancel_hs(mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_STA), | ||
212 | MWIFIEX_ASYNC_CMD); | ||
213 | |||
214 | return 0; | ||
215 | } | ||
216 | |||
217 | /* Device ID for SD8787 */ | ||
218 | #define SDIO_DEVICE_ID_MARVELL_8787 (0x9119) | ||
219 | |||
220 | /* WLAN IDs */ | ||
221 | static const struct sdio_device_id mwifiex_ids[] = { | ||
222 | {SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8787)}, | ||
223 | {}, | ||
224 | }; | ||
225 | |||
226 | MODULE_DEVICE_TABLE(sdio, mwifiex_ids); | ||
227 | |||
228 | static const struct dev_pm_ops mwifiex_sdio_pm_ops = { | ||
229 | .suspend = mwifiex_sdio_suspend, | ||
230 | .resume = mwifiex_sdio_resume, | ||
231 | }; | ||
232 | |||
233 | static struct sdio_driver mwifiex_sdio = { | ||
234 | .name = "mwifiex_sdio", | ||
235 | .id_table = mwifiex_ids, | ||
236 | .probe = mwifiex_sdio_probe, | ||
237 | .remove = mwifiex_sdio_remove, | ||
238 | .drv = { | ||
239 | .owner = THIS_MODULE, | ||
240 | .pm = &mwifiex_sdio_pm_ops, | ||
241 | } | ||
242 | }; | ||
243 | |||
244 | /* | ||
245 | * This function writes data into SDIO card register. | ||
246 | */ | ||
247 | static int | ||
248 | mwifiex_write_reg(struct mwifiex_adapter *adapter, u32 reg, u32 data) | ||
249 | { | ||
250 | struct sdio_mmc_card *card = adapter->card; | ||
251 | int ret = -1; | ||
252 | |||
253 | sdio_claim_host(card->func); | ||
254 | sdio_writeb(card->func, (u8) data, reg, &ret); | ||
255 | sdio_release_host(card->func); | ||
256 | |||
257 | return ret; | ||
258 | } | ||
259 | |||
260 | /* | ||
261 | * This function reads data from SDIO card register. | ||
262 | */ | ||
263 | static int | ||
264 | mwifiex_read_reg(struct mwifiex_adapter *adapter, u32 reg, u32 *data) | ||
265 | { | ||
266 | struct sdio_mmc_card *card = adapter->card; | ||
267 | int ret = -1; | ||
268 | u8 val; | ||
269 | |||
270 | sdio_claim_host(card->func); | ||
271 | val = sdio_readb(card->func, reg, &ret); | ||
272 | sdio_release_host(card->func); | ||
273 | |||
274 | *data = val; | ||
275 | |||
276 | return ret; | ||
277 | } | ||
278 | |||
279 | /* | ||
280 | * This function writes multiple data into SDIO card memory. | ||
281 | * | ||
282 | * This does not work in suspended mode. | ||
283 | */ | ||
284 | static int | ||
285 | mwifiex_write_data_sync(struct mwifiex_adapter *adapter, | ||
286 | u8 *buffer, u32 pkt_len, u32 port) | ||
287 | { | ||
288 | struct sdio_mmc_card *card = adapter->card; | ||
289 | int ret = -1; | ||
290 | u8 blk_mode = | ||
291 | (port & MWIFIEX_SDIO_BYTE_MODE_MASK) ? BYTE_MODE : BLOCK_MODE; | ||
292 | u32 blk_size = (blk_mode == BLOCK_MODE) ? MWIFIEX_SDIO_BLOCK_SIZE : 1; | ||
293 | u32 blk_cnt = | ||
294 | (blk_mode == | ||
295 | BLOCK_MODE) ? (pkt_len / | ||
296 | MWIFIEX_SDIO_BLOCK_SIZE) : pkt_len; | ||
297 | u32 ioport = (port & MWIFIEX_SDIO_IO_PORT_MASK); | ||
298 | |||
299 | if (adapter->is_suspended) { | ||
300 | dev_err(adapter->dev, | ||
301 | "%s: not allowed while suspended\n", __func__); | ||
302 | return -1; | ||
303 | } | ||
304 | |||
305 | sdio_claim_host(card->func); | ||
306 | |||
307 | if (!sdio_writesb(card->func, ioport, buffer, blk_cnt * blk_size)) | ||
308 | ret = 0; | ||
309 | |||
310 | sdio_release_host(card->func); | ||
311 | |||
312 | return ret; | ||
313 | } | ||
314 | |||
315 | /* | ||
316 | * This function reads multiple data from SDIO card memory. | ||
317 | */ | ||
318 | static int mwifiex_read_data_sync(struct mwifiex_adapter *adapter, u8 *buffer, | ||
319 | u32 len, u32 port, 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 | dev_dbg(adapter->dev, "event: wakeup device...\n"); | ||
352 | |||
353 | return mwifiex_write_reg(adapter, CONFIGURATION_REG, HOST_POWER_UP); | ||
354 | } | ||
355 | |||
356 | /* | ||
357 | * This function is called after the card has woken up. | ||
358 | * | ||
359 | * The card configuration register is reset. | ||
360 | */ | ||
361 | static int mwifiex_pm_wakeup_card_complete(struct mwifiex_adapter *adapter) | ||
362 | { | ||
363 | dev_dbg(adapter->dev, "cmd: wakeup device completed\n"); | ||
364 | |||
365 | return mwifiex_write_reg(adapter, CONFIGURATION_REG, 0); | ||
366 | } | ||
367 | |||
368 | /* | ||
369 | * This function initializes the IO ports. | ||
370 | * | ||
371 | * The following operations are performed - | ||
372 | * - Read the IO ports (0, 1 and 2) | ||
373 | * - Set host interrupt Reset-To-Read to clear | ||
374 | * - Set auto re-enable interrupt | ||
375 | */ | ||
376 | static int mwifiex_init_sdio_ioport(struct mwifiex_adapter *adapter) | ||
377 | { | ||
378 | u32 reg; | ||
379 | |||
380 | adapter->ioport = 0; | ||
381 | |||
382 | /* Read the IO port */ | ||
383 | if (!mwifiex_read_reg(adapter, IO_PORT_0_REG, ®)) | ||
384 | adapter->ioport |= (reg & 0xff); | ||
385 | else | ||
386 | return -1; | ||
387 | |||
388 | if (!mwifiex_read_reg(adapter, IO_PORT_1_REG, ®)) | ||
389 | adapter->ioport |= ((reg & 0xff) << 8); | ||
390 | else | ||
391 | return -1; | ||
392 | |||
393 | if (!mwifiex_read_reg(adapter, IO_PORT_2_REG, ®)) | ||
394 | adapter->ioport |= ((reg & 0xff) << 16); | ||
395 | else | ||
396 | return -1; | ||
397 | |||
398 | pr_debug("info: SDIO FUNC1 IO port: %#x\n", adapter->ioport); | ||
399 | |||
400 | /* Set Host interrupt reset to read to clear */ | ||
401 | if (!mwifiex_read_reg(adapter, HOST_INT_RSR_REG, ®)) | ||
402 | mwifiex_write_reg(adapter, HOST_INT_RSR_REG, | ||
403 | reg | SDIO_INT_MASK); | ||
404 | else | ||
405 | return -1; | ||
406 | |||
407 | /* Dnld/Upld ready set to auto reset */ | ||
408 | if (!mwifiex_read_reg(adapter, CARD_MISC_CFG_REG, ®)) | ||
409 | mwifiex_write_reg(adapter, CARD_MISC_CFG_REG, | ||
410 | reg | AUTO_RE_ENABLE_INT); | ||
411 | else | ||
412 | return -1; | ||
413 | |||
414 | return 0; | ||
415 | } | ||
416 | |||
417 | /* | ||
418 | * This function sends data to the card. | ||
419 | */ | ||
420 | static int mwifiex_write_data_to_card(struct mwifiex_adapter *adapter, | ||
421 | u8 *payload, u32 pkt_len, u32 port) | ||
422 | { | ||
423 | u32 i = 0; | ||
424 | int ret; | ||
425 | |||
426 | do { | ||
427 | ret = mwifiex_write_data_sync(adapter, payload, pkt_len, port); | ||
428 | if (ret) { | ||
429 | i++; | ||
430 | dev_err(adapter->dev, "host_to_card, write iomem" | ||
431 | " (%d) failed: %d\n", i, ret); | ||
432 | if (mwifiex_write_reg(adapter, | ||
433 | CONFIGURATION_REG, 0x04)) | ||
434 | dev_err(adapter->dev, "write CFG reg failed\n"); | ||
435 | |||
436 | ret = -1; | ||
437 | if (i > MAX_WRITE_IOMEM_RETRY) | ||
438 | return ret; | ||
439 | } | ||
440 | } while (ret == -1); | ||
441 | |||
442 | return ret; | ||
443 | } | ||
444 | |||
445 | /* | ||
446 | * This function gets the read port. | ||
447 | * | ||
448 | * If control port bit is set in MP read bitmap, the control port | ||
449 | * is returned, otherwise the current read port is returned and | ||
450 | * the value is increased (provided it does not reach the maximum | ||
451 | * limit, in which case it is reset to 1) | ||
452 | */ | ||
453 | static int mwifiex_get_rd_port(struct mwifiex_adapter *adapter, u8 *port) | ||
454 | { | ||
455 | struct sdio_mmc_card *card = adapter->card; | ||
456 | u16 rd_bitmap = card->mp_rd_bitmap; | ||
457 | |||
458 | dev_dbg(adapter->dev, "data: mp_rd_bitmap=0x%04x\n", rd_bitmap); | ||
459 | |||
460 | if (!(rd_bitmap & (CTRL_PORT_MASK | DATA_PORT_MASK))) | ||
461 | return -1; | ||
462 | |||
463 | if (card->mp_rd_bitmap & CTRL_PORT_MASK) { | ||
464 | card->mp_rd_bitmap &= (u16) (~CTRL_PORT_MASK); | ||
465 | *port = CTRL_PORT; | ||
466 | dev_dbg(adapter->dev, "data: port=%d mp_rd_bitmap=0x%04x\n", | ||
467 | *port, card->mp_rd_bitmap); | ||
468 | } else { | ||
469 | if (card->mp_rd_bitmap & (1 << card->curr_rd_port)) { | ||
470 | card->mp_rd_bitmap &= | ||
471 | (u16) (~(1 << card->curr_rd_port)); | ||
472 | *port = card->curr_rd_port; | ||
473 | |||
474 | if (++card->curr_rd_port == MAX_PORT) | ||
475 | card->curr_rd_port = 1; | ||
476 | } else { | ||
477 | return -1; | ||
478 | } | ||
479 | |||
480 | dev_dbg(adapter->dev, | ||
481 | "data: port=%d mp_rd_bitmap=0x%04x -> 0x%04x\n", | ||
482 | *port, rd_bitmap, card->mp_rd_bitmap); | ||
483 | } | ||
484 | return 0; | ||
485 | } | ||
486 | |||
487 | /* | ||
488 | * This function gets the write port for data. | ||
489 | * | ||
490 | * The current write port is returned if available and the value is | ||
491 | * increased (provided it does not reach the maximum limit, in which | ||
492 | * case it is reset to 1) | ||
493 | */ | ||
494 | static int mwifiex_get_wr_port_data(struct mwifiex_adapter *adapter, u8 *port) | ||
495 | { | ||
496 | struct sdio_mmc_card *card = adapter->card; | ||
497 | u16 wr_bitmap = card->mp_wr_bitmap; | ||
498 | |||
499 | dev_dbg(adapter->dev, "data: mp_wr_bitmap=0x%04x\n", wr_bitmap); | ||
500 | |||
501 | if (!(wr_bitmap & card->mp_data_port_mask)) | ||
502 | return -1; | ||
503 | |||
504 | if (card->mp_wr_bitmap & (1 << card->curr_wr_port)) { | ||
505 | card->mp_wr_bitmap &= (u16) (~(1 << card->curr_wr_port)); | ||
506 | *port = card->curr_wr_port; | ||
507 | if (++card->curr_wr_port == card->mp_end_port) | ||
508 | card->curr_wr_port = 1; | ||
509 | } else { | ||
510 | adapter->data_sent = true; | ||
511 | return -EBUSY; | ||
512 | } | ||
513 | |||
514 | if (*port == CTRL_PORT) { | ||
515 | dev_err(adapter->dev, "invalid data port=%d cur port=%d" | ||
516 | " mp_wr_bitmap=0x%04x -> 0x%04x\n", | ||
517 | *port, card->curr_wr_port, wr_bitmap, | ||
518 | card->mp_wr_bitmap); | ||
519 | return -1; | ||
520 | } | ||
521 | |||
522 | dev_dbg(adapter->dev, "data: port=%d mp_wr_bitmap=0x%04x -> 0x%04x\n", | ||
523 | *port, wr_bitmap, card->mp_wr_bitmap); | ||
524 | |||
525 | return 0; | ||
526 | } | ||
527 | |||
528 | /* | ||
529 | * This function polls the card status. | ||
530 | */ | ||
531 | static int | ||
532 | mwifiex_sdio_poll_card_status(struct mwifiex_adapter *adapter, u8 bits) | ||
533 | { | ||
534 | u32 tries; | ||
535 | u32 cs; | ||
536 | |||
537 | for (tries = 0; tries < MAX_POLL_TRIES; tries++) { | ||
538 | if (mwifiex_read_reg(adapter, CARD_STATUS_REG, &cs)) | ||
539 | break; | ||
540 | else if ((cs & bits) == bits) | ||
541 | return 0; | ||
542 | |||
543 | udelay(10); | ||
544 | } | ||
545 | |||
546 | dev_err(adapter->dev, "poll card status failed, tries = %d\n", | ||
547 | tries); | ||
548 | return -1; | ||
549 | } | ||
550 | |||
551 | /* | ||
552 | * This function reads the firmware status. | ||
553 | */ | ||
554 | static int | ||
555 | mwifiex_sdio_read_fw_status(struct mwifiex_adapter *adapter, u16 *dat) | ||
556 | { | ||
557 | u32 fws0, fws1; | ||
558 | |||
559 | if (mwifiex_read_reg(adapter, CARD_FW_STATUS0_REG, &fws0)) | ||
560 | return -1; | ||
561 | |||
562 | if (mwifiex_read_reg(adapter, CARD_FW_STATUS1_REG, &fws1)) | ||
563 | return -1; | ||
564 | |||
565 | *dat = (u16) ((fws1 << 8) | fws0); | ||
566 | |||
567 | return 0; | ||
568 | } | ||
569 | |||
570 | /* | ||
571 | * This function disables the host interrupt. | ||
572 | * | ||
573 | * The host interrupt mask is read, the disable bit is reset and | ||
574 | * written back to the card host interrupt mask register. | ||
575 | */ | ||
576 | static int mwifiex_sdio_disable_host_int(struct mwifiex_adapter *adapter) | ||
577 | { | ||
578 | u32 host_int_mask; | ||
579 | |||
580 | /* Read back the host_int_mask register */ | ||
581 | if (mwifiex_read_reg(adapter, HOST_INT_MASK_REG, &host_int_mask)) | ||
582 | return -1; | ||
583 | |||
584 | /* Update with the mask and write back to the register */ | ||
585 | host_int_mask &= ~HOST_INT_DISABLE; | ||
586 | |||
587 | if (mwifiex_write_reg(adapter, HOST_INT_MASK_REG, host_int_mask)) { | ||
588 | dev_err(adapter->dev, "disable host interrupt failed\n"); | ||
589 | return -1; | ||
590 | } | ||
591 | |||
592 | return 0; | ||
593 | } | ||
594 | |||
595 | /* | ||
596 | * This function enables the host interrupt. | ||
597 | * | ||
598 | * The host interrupt enable mask is written to the card | ||
599 | * host interrupt mask register. | ||
600 | */ | ||
601 | static int mwifiex_sdio_enable_host_int(struct mwifiex_adapter *adapter) | ||
602 | { | ||
603 | /* Simply write the mask to the register */ | ||
604 | if (mwifiex_write_reg(adapter, HOST_INT_MASK_REG, HOST_INT_ENABLE)) { | ||
605 | dev_err(adapter->dev, "enable host interrupt failed\n"); | ||
606 | return -1; | ||
607 | } | ||
608 | return 0; | ||
609 | } | ||
610 | |||
611 | /* | ||
612 | * This function sends a data buffer to the card. | ||
613 | */ | ||
614 | static int mwifiex_sdio_card_to_host(struct mwifiex_adapter *adapter, | ||
615 | u32 *type, u8 *buffer, | ||
616 | u32 npayload, u32 ioport) | ||
617 | { | ||
618 | int ret; | ||
619 | u32 nb; | ||
620 | |||
621 | if (!buffer) { | ||
622 | dev_err(adapter->dev, "%s: buffer is NULL\n", __func__); | ||
623 | return -1; | ||
624 | } | ||
625 | |||
626 | ret = mwifiex_read_data_sync(adapter, buffer, npayload, ioport, 1); | ||
627 | |||
628 | if (ret) { | ||
629 | dev_err(adapter->dev, "%s: read iomem failed: %d\n", __func__, | ||
630 | ret); | ||
631 | return -1; | ||
632 | } | ||
633 | |||
634 | nb = le16_to_cpu(*(__le16 *) (buffer)); | ||
635 | if (nb > npayload) { | ||
636 | dev_err(adapter->dev, "%s: invalid packet, nb=%d, npayload=%d\n", | ||
637 | __func__, nb, npayload); | ||
638 | return -1; | ||
639 | } | ||
640 | |||
641 | *type = le16_to_cpu(*(__le16 *) (buffer + 2)); | ||
642 | |||
643 | return ret; | ||
644 | } | ||
645 | |||
646 | /* | ||
647 | * This function downloads the firmware to the card. | ||
648 | * | ||
649 | * Firmware is downloaded to the card in blocks. Every block download | ||
650 | * is tested for CRC errors, and retried a number of times before | ||
651 | * returning failure. | ||
652 | */ | ||
653 | static int mwifiex_prog_fw_w_helper(struct mwifiex_adapter *adapter, | ||
654 | struct mwifiex_fw_image *fw) | ||
655 | { | ||
656 | int ret; | ||
657 | u8 *firmware = fw->fw_buf; | ||
658 | u32 firmware_len = fw->fw_len; | ||
659 | u32 offset = 0; | ||
660 | u32 base0, base1; | ||
661 | u8 *fwbuf; | ||
662 | u16 len = 0; | ||
663 | u32 txlen, tx_blocks = 0, tries; | ||
664 | u32 i = 0; | ||
665 | |||
666 | if (!firmware_len) { | ||
667 | dev_err(adapter->dev, "firmware image not found!" | ||
668 | " Terminating download\n"); | ||
669 | return -1; | ||
670 | } | ||
671 | |||
672 | dev_dbg(adapter->dev, "info: downloading FW image (%d bytes)\n", | ||
673 | firmware_len); | ||
674 | |||
675 | /* Assume that the allocated buffer is 8-byte aligned */ | ||
676 | fwbuf = kzalloc(MWIFIEX_UPLD_SIZE, GFP_KERNEL); | ||
677 | if (!fwbuf) { | ||
678 | dev_err(adapter->dev, "unable to alloc buffer for firmware." | ||
679 | " Terminating download\n"); | ||
680 | return -ENOMEM; | ||
681 | } | ||
682 | |||
683 | /* Perform firmware data transfer */ | ||
684 | do { | ||
685 | /* The host polls for the DN_LD_CARD_RDY and CARD_IO_READY | ||
686 | bits */ | ||
687 | ret = mwifiex_sdio_poll_card_status(adapter, CARD_IO_READY | | ||
688 | DN_LD_CARD_RDY); | ||
689 | if (ret) { | ||
690 | dev_err(adapter->dev, "FW download with helper:" | ||
691 | " poll status timeout @ %d\n", offset); | ||
692 | goto done; | ||
693 | } | ||
694 | |||
695 | /* More data? */ | ||
696 | if (offset >= firmware_len) | ||
697 | break; | ||
698 | |||
699 | for (tries = 0; tries < MAX_POLL_TRIES; tries++) { | ||
700 | ret = mwifiex_read_reg(adapter, HOST_F1_RD_BASE_0, | ||
701 | &base0); | ||
702 | if (ret) { | ||
703 | dev_err(adapter->dev, "dev BASE0 register read" | ||
704 | " failed: base0=0x%04X(%d). Terminating " | ||
705 | "download\n", base0, base0); | ||
706 | goto done; | ||
707 | } | ||
708 | ret = mwifiex_read_reg(adapter, HOST_F1_RD_BASE_1, | ||
709 | &base1); | ||
710 | if (ret) { | ||
711 | dev_err(adapter->dev, "dev BASE1 register read" | ||
712 | " failed: base1=0x%04X(%d). Terminating " | ||
713 | "download\n", base1, base1); | ||
714 | goto done; | ||
715 | } | ||
716 | len = (u16) (((base1 & 0xff) << 8) | (base0 & 0xff)); | ||
717 | |||
718 | if (len) | ||
719 | break; | ||
720 | |||
721 | udelay(10); | ||
722 | } | ||
723 | |||
724 | if (!len) { | ||
725 | break; | ||
726 | } else if (len > MWIFIEX_UPLD_SIZE) { | ||
727 | dev_err(adapter->dev, "FW download failed @ %d," | ||
728 | " invalid length %d\n", offset, len); | ||
729 | ret = -1; | ||
730 | goto done; | ||
731 | } | ||
732 | |||
733 | txlen = len; | ||
734 | |||
735 | if (len & BIT(0)) { | ||
736 | i++; | ||
737 | if (i > MAX_WRITE_IOMEM_RETRY) { | ||
738 | dev_err(adapter->dev, "FW download failed @" | ||
739 | " %d, over max retry count\n", offset); | ||
740 | ret = -1; | ||
741 | goto done; | ||
742 | } | ||
743 | dev_err(adapter->dev, "CRC indicated by the helper:" | ||
744 | " len = 0x%04X, txlen = %d\n", len, txlen); | ||
745 | len &= ~BIT(0); | ||
746 | /* Setting this to 0 to resend from same offset */ | ||
747 | txlen = 0; | ||
748 | } else { | ||
749 | i = 0; | ||
750 | |||
751 | /* Set blocksize to transfer - checking for last | ||
752 | block */ | ||
753 | if (firmware_len - offset < txlen) | ||
754 | txlen = firmware_len - offset; | ||
755 | |||
756 | tx_blocks = (txlen + MWIFIEX_SDIO_BLOCK_SIZE - | ||
757 | 1) / MWIFIEX_SDIO_BLOCK_SIZE; | ||
758 | |||
759 | /* Copy payload to buffer */ | ||
760 | memmove(fwbuf, &firmware[offset], txlen); | ||
761 | } | ||
762 | |||
763 | ret = mwifiex_write_data_sync(adapter, fwbuf, tx_blocks * | ||
764 | MWIFIEX_SDIO_BLOCK_SIZE, | ||
765 | adapter->ioport); | ||
766 | if (ret) { | ||
767 | dev_err(adapter->dev, "FW download, write iomem (%d)" | ||
768 | " failed @ %d\n", i, offset); | ||
769 | if (mwifiex_write_reg(adapter, CONFIGURATION_REG, 0x04)) | ||
770 | dev_err(adapter->dev, "write CFG reg failed\n"); | ||
771 | |||
772 | ret = -1; | ||
773 | goto done; | ||
774 | } | ||
775 | |||
776 | offset += txlen; | ||
777 | } while (true); | ||
778 | |||
779 | dev_dbg(adapter->dev, "info: FW download over, size %d bytes\n", | ||
780 | offset); | ||
781 | |||
782 | ret = 0; | ||
783 | done: | ||
784 | kfree(fwbuf); | ||
785 | return ret; | ||
786 | } | ||
787 | |||
788 | /* | ||
789 | * This function checks the firmware status in card. | ||
790 | * | ||
791 | * The winner interface is also determined by this function. | ||
792 | */ | ||
793 | static int mwifiex_check_fw_status(struct mwifiex_adapter *adapter, | ||
794 | u32 poll_num, int *winner) | ||
795 | { | ||
796 | int ret = 0; | ||
797 | u16 firmware_stat; | ||
798 | u32 tries; | ||
799 | u32 winner_status; | ||
800 | |||
801 | /* Wait for firmware initialization event */ | ||
802 | for (tries = 0; tries < poll_num; tries++) { | ||
803 | ret = mwifiex_sdio_read_fw_status(adapter, &firmware_stat); | ||
804 | if (ret) | ||
805 | continue; | ||
806 | if (firmware_stat == FIRMWARE_READY) { | ||
807 | ret = 0; | ||
808 | break; | ||
809 | } else { | ||
810 | mdelay(100); | ||
811 | ret = -1; | ||
812 | } | ||
813 | } | ||
814 | |||
815 | if (winner && ret) { | ||
816 | if (mwifiex_read_reg | ||
817 | (adapter, CARD_FW_STATUS0_REG, &winner_status)) | ||
818 | winner_status = 0; | ||
819 | |||
820 | if (winner_status) | ||
821 | *winner = 0; | ||
822 | else | ||
823 | *winner = 1; | ||
824 | } | ||
825 | return ret; | ||
826 | } | ||
827 | |||
828 | /* | ||
829 | * This function reads the interrupt status from card. | ||
830 | */ | ||
831 | static void mwifiex_interrupt_status(struct mwifiex_adapter *adapter) | ||
832 | { | ||
833 | struct sdio_mmc_card *card = adapter->card; | ||
834 | u32 sdio_ireg; | ||
835 | unsigned long flags; | ||
836 | |||
837 | if (mwifiex_read_data_sync(adapter, card->mp_regs, MAX_MP_REGS, | ||
838 | REG_PORT | MWIFIEX_SDIO_BYTE_MODE_MASK, | ||
839 | 0)) { | ||
840 | dev_err(adapter->dev, "read mp_regs failed\n"); | ||
841 | return; | ||
842 | } | ||
843 | |||
844 | sdio_ireg = card->mp_regs[HOST_INTSTATUS_REG]; | ||
845 | if (sdio_ireg) { | ||
846 | /* | ||
847 | * DN_LD_HOST_INT_STATUS and/or UP_LD_HOST_INT_STATUS | ||
848 | * Clear the interrupt status register | ||
849 | */ | ||
850 | dev_dbg(adapter->dev, "int: sdio_ireg = %#x\n", sdio_ireg); | ||
851 | spin_lock_irqsave(&adapter->int_lock, flags); | ||
852 | adapter->int_status |= sdio_ireg; | ||
853 | spin_unlock_irqrestore(&adapter->int_lock, flags); | ||
854 | } | ||
855 | } | ||
856 | |||
857 | /* | ||
858 | * SDIO interrupt handler. | ||
859 | * | ||
860 | * This function reads the interrupt status from firmware and assigns | ||
861 | * the main process in workqueue which will handle the interrupt. | ||
862 | */ | ||
863 | static void | ||
864 | mwifiex_sdio_interrupt(struct sdio_func *func) | ||
865 | { | ||
866 | struct mwifiex_adapter *adapter; | ||
867 | struct sdio_mmc_card *card; | ||
868 | |||
869 | card = sdio_get_drvdata(func); | ||
870 | if (!card || !card->adapter) { | ||
871 | pr_debug("int: func=%p card=%p adapter=%p\n", | ||
872 | func, card, card ? card->adapter : NULL); | ||
873 | return; | ||
874 | } | ||
875 | adapter = card->adapter; | ||
876 | |||
877 | if (adapter->surprise_removed) | ||
878 | return; | ||
879 | |||
880 | if (!adapter->pps_uapsd_mode && adapter->ps_state == PS_STATE_SLEEP) | ||
881 | adapter->ps_state = PS_STATE_AWAKE; | ||
882 | |||
883 | mwifiex_interrupt_status(adapter); | ||
884 | queue_work(adapter->workqueue, &adapter->main_work); | ||
885 | } | ||
886 | |||
887 | /* | ||
888 | * This function decodes a received packet. | ||
889 | * | ||
890 | * Based on the type, the packet is treated as either a data, or | ||
891 | * a command response, or an event, and the correct handler | ||
892 | * function is invoked. | ||
893 | */ | ||
894 | static int mwifiex_decode_rx_packet(struct mwifiex_adapter *adapter, | ||
895 | struct sk_buff *skb, u32 upld_typ) | ||
896 | { | ||
897 | u8 *cmd_buf; | ||
898 | |||
899 | skb_pull(skb, INTF_HEADER_LEN); | ||
900 | |||
901 | switch (upld_typ) { | ||
902 | case MWIFIEX_TYPE_DATA: | ||
903 | dev_dbg(adapter->dev, "info: --- Rx: Data packet ---\n"); | ||
904 | mwifiex_handle_rx_packet(adapter, skb); | ||
905 | break; | ||
906 | |||
907 | case MWIFIEX_TYPE_CMD: | ||
908 | dev_dbg(adapter->dev, "info: --- Rx: Cmd Response ---\n"); | ||
909 | /* take care of curr_cmd = NULL case */ | ||
910 | if (!adapter->curr_cmd) { | ||
911 | cmd_buf = adapter->upld_buf; | ||
912 | |||
913 | if (adapter->ps_state == PS_STATE_SLEEP_CFM) | ||
914 | mwifiex_process_sleep_confirm_resp(adapter, | ||
915 | skb->data, skb->len); | ||
916 | |||
917 | memcpy(cmd_buf, skb->data, min_t(u32, | ||
918 | MWIFIEX_SIZE_OF_CMD_BUFFER, skb->len)); | ||
919 | |||
920 | dev_kfree_skb_any(skb); | ||
921 | } else { | ||
922 | adapter->cmd_resp_received = true; | ||
923 | adapter->curr_cmd->resp_skb = skb; | ||
924 | } | ||
925 | break; | ||
926 | |||
927 | case MWIFIEX_TYPE_EVENT: | ||
928 | dev_dbg(adapter->dev, "info: --- Rx: Event ---\n"); | ||
929 | adapter->event_cause = *(u32 *) skb->data; | ||
930 | |||
931 | skb_pull(skb, MWIFIEX_EVENT_HEADER_LEN); | ||
932 | |||
933 | if ((skb->len > 0) && (skb->len < MAX_EVENT_SIZE)) | ||
934 | memcpy(adapter->event_body, skb->data, skb->len); | ||
935 | |||
936 | /* event cause has been saved to adapter->event_cause */ | ||
937 | adapter->event_received = true; | ||
938 | adapter->event_skb = skb; | ||
939 | |||
940 | break; | ||
941 | |||
942 | default: | ||
943 | dev_err(adapter->dev, "unknown upload type %#x\n", upld_typ); | ||
944 | dev_kfree_skb_any(skb); | ||
945 | break; | ||
946 | } | ||
947 | |||
948 | return 0; | ||
949 | } | ||
950 | |||
951 | /* | ||
952 | * This function transfers received packets from card to driver, performing | ||
953 | * aggregation if required. | ||
954 | * | ||
955 | * For data received on control port, or if aggregation is disabled, the | ||
956 | * received buffers are uploaded as separate packets. However, if aggregation | ||
957 | * is enabled and required, the buffers are copied onto an aggregation buffer, | ||
958 | * provided there is space left, processed and finally uploaded. | ||
959 | */ | ||
960 | static int mwifiex_sdio_card_to_host_mp_aggr(struct mwifiex_adapter *adapter, | ||
961 | struct sk_buff *skb, u8 port) | ||
962 | { | ||
963 | struct sdio_mmc_card *card = adapter->card; | ||
964 | s32 f_do_rx_aggr = 0; | ||
965 | s32 f_do_rx_cur = 0; | ||
966 | s32 f_aggr_cur = 0; | ||
967 | struct sk_buff *skb_deaggr; | ||
968 | u32 pind; | ||
969 | u32 pkt_len, pkt_type = 0; | ||
970 | u8 *curr_ptr; | ||
971 | u32 rx_len = skb->len; | ||
972 | |||
973 | if (port == CTRL_PORT) { | ||
974 | /* Read the command Resp without aggr */ | ||
975 | dev_dbg(adapter->dev, "info: %s: no aggregation for cmd " | ||
976 | "response\n", __func__); | ||
977 | |||
978 | f_do_rx_cur = 1; | ||
979 | goto rx_curr_single; | ||
980 | } | ||
981 | |||
982 | if (!card->mpa_rx.enabled) { | ||
983 | dev_dbg(adapter->dev, "info: %s: rx aggregation disabled\n", | ||
984 | __func__); | ||
985 | |||
986 | f_do_rx_cur = 1; | ||
987 | goto rx_curr_single; | ||
988 | } | ||
989 | |||
990 | if (card->mp_rd_bitmap & (~((u16) CTRL_PORT_MASK))) { | ||
991 | /* Some more data RX pending */ | ||
992 | dev_dbg(adapter->dev, "info: %s: not last packet\n", __func__); | ||
993 | |||
994 | if (MP_RX_AGGR_IN_PROGRESS(card)) { | ||
995 | if (MP_RX_AGGR_BUF_HAS_ROOM(card, skb->len)) { | ||
996 | f_aggr_cur = 1; | ||
997 | } else { | ||
998 | /* No room in Aggr buf, do rx aggr now */ | ||
999 | f_do_rx_aggr = 1; | ||
1000 | f_do_rx_cur = 1; | ||
1001 | } | ||
1002 | } else { | ||
1003 | /* Rx aggr not in progress */ | ||
1004 | f_aggr_cur = 1; | ||
1005 | } | ||
1006 | |||
1007 | } else { | ||
1008 | /* No more data RX pending */ | ||
1009 | dev_dbg(adapter->dev, "info: %s: last packet\n", __func__); | ||
1010 | |||
1011 | if (MP_RX_AGGR_IN_PROGRESS(card)) { | ||
1012 | f_do_rx_aggr = 1; | ||
1013 | if (MP_RX_AGGR_BUF_HAS_ROOM(card, skb->len)) | ||
1014 | f_aggr_cur = 1; | ||
1015 | else | ||
1016 | /* No room in Aggr buf, do rx aggr now */ | ||
1017 | f_do_rx_cur = 1; | ||
1018 | } else { | ||
1019 | f_do_rx_cur = 1; | ||
1020 | } | ||
1021 | } | ||
1022 | |||
1023 | if (f_aggr_cur) { | ||
1024 | dev_dbg(adapter->dev, "info: current packet aggregation\n"); | ||
1025 | /* Curr pkt can be aggregated */ | ||
1026 | MP_RX_AGGR_SETUP(card, skb, port); | ||
1027 | |||
1028 | if (MP_RX_AGGR_PKT_LIMIT_REACHED(card) || | ||
1029 | MP_RX_AGGR_PORT_LIMIT_REACHED(card)) { | ||
1030 | dev_dbg(adapter->dev, "info: %s: aggregated packet " | ||
1031 | "limit reached\n", __func__); | ||
1032 | /* No more pkts allowed in Aggr buf, rx it */ | ||
1033 | f_do_rx_aggr = 1; | ||
1034 | } | ||
1035 | } | ||
1036 | |||
1037 | if (f_do_rx_aggr) { | ||
1038 | /* do aggr RX now */ | ||
1039 | dev_dbg(adapter->dev, "info: do_rx_aggr: num of packets: %d\n", | ||
1040 | card->mpa_rx.pkt_cnt); | ||
1041 | |||
1042 | if (mwifiex_read_data_sync(adapter, card->mpa_rx.buf, | ||
1043 | card->mpa_rx.buf_len, | ||
1044 | (adapter->ioport | 0x1000 | | ||
1045 | (card->mpa_rx.ports << 4)) + | ||
1046 | card->mpa_rx.start_port, 1)) | ||
1047 | return -1; | ||
1048 | |||
1049 | curr_ptr = card->mpa_rx.buf; | ||
1050 | |||
1051 | for (pind = 0; pind < card->mpa_rx.pkt_cnt; pind++) { | ||
1052 | |||
1053 | /* get curr PKT len & type */ | ||
1054 | pkt_len = *(u16 *) &curr_ptr[0]; | ||
1055 | pkt_type = *(u16 *) &curr_ptr[2]; | ||
1056 | |||
1057 | /* copy pkt to deaggr buf */ | ||
1058 | skb_deaggr = card->mpa_rx.skb_arr[pind]; | ||
1059 | |||
1060 | if ((pkt_type == MWIFIEX_TYPE_DATA) && (pkt_len <= | ||
1061 | card->mpa_rx.len_arr[pind])) { | ||
1062 | |||
1063 | memcpy(skb_deaggr->data, curr_ptr, pkt_len); | ||
1064 | |||
1065 | skb_trim(skb_deaggr, pkt_len); | ||
1066 | |||
1067 | /* Process de-aggr packet */ | ||
1068 | mwifiex_decode_rx_packet(adapter, skb_deaggr, | ||
1069 | pkt_type); | ||
1070 | } else { | ||
1071 | dev_err(adapter->dev, "wrong aggr pkt:" | ||
1072 | " type=%d len=%d max_len=%d\n", | ||
1073 | pkt_type, pkt_len, | ||
1074 | card->mpa_rx.len_arr[pind]); | ||
1075 | dev_kfree_skb_any(skb_deaggr); | ||
1076 | } | ||
1077 | curr_ptr += card->mpa_rx.len_arr[pind]; | ||
1078 | } | ||
1079 | MP_RX_AGGR_BUF_RESET(card); | ||
1080 | } | ||
1081 | |||
1082 | rx_curr_single: | ||
1083 | if (f_do_rx_cur) { | ||
1084 | dev_dbg(adapter->dev, "info: RX: port: %d, rx_len: %d\n", | ||
1085 | port, rx_len); | ||
1086 | |||
1087 | if (mwifiex_sdio_card_to_host(adapter, &pkt_type, | ||
1088 | skb->data, skb->len, | ||
1089 | adapter->ioport + port)) | ||
1090 | return -1; | ||
1091 | |||
1092 | mwifiex_decode_rx_packet(adapter, skb, pkt_type); | ||
1093 | } | ||
1094 | |||
1095 | return 0; | ||
1096 | } | ||
1097 | |||
1098 | /* | ||
1099 | * This function checks the current interrupt status. | ||
1100 | * | ||
1101 | * The following interrupts are checked and handled by this function - | ||
1102 | * - Data sent | ||
1103 | * - Command sent | ||
1104 | * - Packets received | ||
1105 | * | ||
1106 | * Since the firmware does not generate download ready interrupt if the | ||
1107 | * port updated is command port only, command sent interrupt checking | ||
1108 | * should be done manually, and for every SDIO interrupt. | ||
1109 | * | ||
1110 | * In case of Rx packets received, the packets are uploaded from card to | ||
1111 | * host and processed accordingly. | ||
1112 | */ | ||
1113 | static int mwifiex_process_int_status(struct mwifiex_adapter *adapter) | ||
1114 | { | ||
1115 | struct sdio_mmc_card *card = adapter->card; | ||
1116 | int ret = 0; | ||
1117 | u8 sdio_ireg; | ||
1118 | struct sk_buff *skb; | ||
1119 | u8 port = CTRL_PORT; | ||
1120 | u32 len_reg_l, len_reg_u; | ||
1121 | u32 rx_blocks; | ||
1122 | u16 rx_len; | ||
1123 | unsigned long flags; | ||
1124 | |||
1125 | spin_lock_irqsave(&adapter->int_lock, flags); | ||
1126 | sdio_ireg = adapter->int_status; | ||
1127 | adapter->int_status = 0; | ||
1128 | spin_unlock_irqrestore(&adapter->int_lock, flags); | ||
1129 | |||
1130 | if (!sdio_ireg) | ||
1131 | return ret; | ||
1132 | |||
1133 | if (sdio_ireg & DN_LD_HOST_INT_STATUS) { | ||
1134 | card->mp_wr_bitmap = ((u16) card->mp_regs[WR_BITMAP_U]) << 8; | ||
1135 | card->mp_wr_bitmap |= (u16) card->mp_regs[WR_BITMAP_L]; | ||
1136 | dev_dbg(adapter->dev, "int: DNLD: wr_bitmap=0x%04x\n", | ||
1137 | card->mp_wr_bitmap); | ||
1138 | if (adapter->data_sent && | ||
1139 | (card->mp_wr_bitmap & card->mp_data_port_mask)) { | ||
1140 | dev_dbg(adapter->dev, | ||
1141 | "info: <--- Tx DONE Interrupt --->\n"); | ||
1142 | adapter->data_sent = false; | ||
1143 | } | ||
1144 | } | ||
1145 | |||
1146 | /* As firmware will not generate download ready interrupt if the port | ||
1147 | updated is command port only, cmd_sent should be done for any SDIO | ||
1148 | interrupt. */ | ||
1149 | if (adapter->cmd_sent) { | ||
1150 | /* Check if firmware has attach buffer at command port and | ||
1151 | update just that in wr_bit_map. */ | ||
1152 | card->mp_wr_bitmap |= | ||
1153 | (u16) card->mp_regs[WR_BITMAP_L] & CTRL_PORT_MASK; | ||
1154 | if (card->mp_wr_bitmap & CTRL_PORT_MASK) | ||
1155 | adapter->cmd_sent = false; | ||
1156 | } | ||
1157 | |||
1158 | dev_dbg(adapter->dev, "info: cmd_sent=%d data_sent=%d\n", | ||
1159 | adapter->cmd_sent, adapter->data_sent); | ||
1160 | if (sdio_ireg & UP_LD_HOST_INT_STATUS) { | ||
1161 | card->mp_rd_bitmap = ((u16) card->mp_regs[RD_BITMAP_U]) << 8; | ||
1162 | card->mp_rd_bitmap |= (u16) card->mp_regs[RD_BITMAP_L]; | ||
1163 | dev_dbg(adapter->dev, "int: UPLD: rd_bitmap=0x%04x\n", | ||
1164 | card->mp_rd_bitmap); | ||
1165 | |||
1166 | while (true) { | ||
1167 | ret = mwifiex_get_rd_port(adapter, &port); | ||
1168 | if (ret) { | ||
1169 | dev_dbg(adapter->dev, | ||
1170 | "info: no more rd_port available\n"); | ||
1171 | break; | ||
1172 | } | ||
1173 | len_reg_l = RD_LEN_P0_L + (port << 1); | ||
1174 | len_reg_u = RD_LEN_P0_U + (port << 1); | ||
1175 | rx_len = ((u16) card->mp_regs[len_reg_u]) << 8; | ||
1176 | rx_len |= (u16) card->mp_regs[len_reg_l]; | ||
1177 | dev_dbg(adapter->dev, "info: RX: port=%d rx_len=%u\n", | ||
1178 | port, rx_len); | ||
1179 | rx_blocks = | ||
1180 | (rx_len + MWIFIEX_SDIO_BLOCK_SIZE - | ||
1181 | 1) / MWIFIEX_SDIO_BLOCK_SIZE; | ||
1182 | if (rx_len <= INTF_HEADER_LEN | ||
1183 | || (rx_blocks * MWIFIEX_SDIO_BLOCK_SIZE) > | ||
1184 | MWIFIEX_RX_DATA_BUF_SIZE) { | ||
1185 | dev_err(adapter->dev, "invalid rx_len=%d\n", | ||
1186 | rx_len); | ||
1187 | return -1; | ||
1188 | } | ||
1189 | rx_len = (u16) (rx_blocks * MWIFIEX_SDIO_BLOCK_SIZE); | ||
1190 | |||
1191 | skb = dev_alloc_skb(rx_len); | ||
1192 | |||
1193 | if (!skb) { | ||
1194 | dev_err(adapter->dev, "%s: failed to alloc skb", | ||
1195 | __func__); | ||
1196 | return -1; | ||
1197 | } | ||
1198 | |||
1199 | skb_put(skb, rx_len); | ||
1200 | |||
1201 | dev_dbg(adapter->dev, "info: rx_len = %d skb->len = %d\n", | ||
1202 | rx_len, skb->len); | ||
1203 | |||
1204 | if (mwifiex_sdio_card_to_host_mp_aggr(adapter, skb, | ||
1205 | port)) { | ||
1206 | u32 cr = 0; | ||
1207 | |||
1208 | dev_err(adapter->dev, "card_to_host_mpa failed:" | ||
1209 | " int status=%#x\n", sdio_ireg); | ||
1210 | if (mwifiex_read_reg(adapter, | ||
1211 | CONFIGURATION_REG, &cr)) | ||
1212 | dev_err(adapter->dev, | ||
1213 | "read CFG reg failed\n"); | ||
1214 | |||
1215 | dev_dbg(adapter->dev, | ||
1216 | "info: CFG reg val = %d\n", cr); | ||
1217 | if (mwifiex_write_reg(adapter, | ||
1218 | CONFIGURATION_REG, | ||
1219 | (cr | 0x04))) | ||
1220 | dev_err(adapter->dev, | ||
1221 | "write CFG reg failed\n"); | ||
1222 | |||
1223 | dev_dbg(adapter->dev, "info: write success\n"); | ||
1224 | if (mwifiex_read_reg(adapter, | ||
1225 | CONFIGURATION_REG, &cr)) | ||
1226 | dev_err(adapter->dev, | ||
1227 | "read CFG reg failed\n"); | ||
1228 | |||
1229 | dev_dbg(adapter->dev, | ||
1230 | "info: CFG reg val =%x\n", cr); | ||
1231 | dev_kfree_skb_any(skb); | ||
1232 | return -1; | ||
1233 | } | ||
1234 | } | ||
1235 | } | ||
1236 | |||
1237 | return 0; | ||
1238 | } | ||
1239 | |||
1240 | /* | ||
1241 | * This function aggregates transmission buffers in driver and downloads | ||
1242 | * the aggregated packet to card. | ||
1243 | * | ||
1244 | * The individual packets are aggregated by copying into an aggregation | ||
1245 | * buffer and then downloaded to the card. Previous unsent packets in the | ||
1246 | * aggregation buffer are pre-copied first before new packets are added. | ||
1247 | * Aggregation is done till there is space left in the aggregation buffer, | ||
1248 | * or till new packets are available. | ||
1249 | * | ||
1250 | * The function will only download the packet to the card when aggregation | ||
1251 | * stops, otherwise it will just aggregate the packet in aggregation buffer | ||
1252 | * and return. | ||
1253 | */ | ||
1254 | static int mwifiex_host_to_card_mp_aggr(struct mwifiex_adapter *adapter, | ||
1255 | u8 *payload, u32 pkt_len, u8 port, | ||
1256 | u32 next_pkt_len) | ||
1257 | { | ||
1258 | struct sdio_mmc_card *card = adapter->card; | ||
1259 | int ret = 0; | ||
1260 | s32 f_send_aggr_buf = 0; | ||
1261 | s32 f_send_cur_buf = 0; | ||
1262 | s32 f_precopy_cur_buf = 0; | ||
1263 | s32 f_postcopy_cur_buf = 0; | ||
1264 | |||
1265 | if ((!card->mpa_tx.enabled) || (port == CTRL_PORT)) { | ||
1266 | dev_dbg(adapter->dev, "info: %s: tx aggregation disabled\n", | ||
1267 | __func__); | ||
1268 | |||
1269 | f_send_cur_buf = 1; | ||
1270 | goto tx_curr_single; | ||
1271 | } | ||
1272 | |||
1273 | if (next_pkt_len) { | ||
1274 | /* More pkt in TX queue */ | ||
1275 | dev_dbg(adapter->dev, "info: %s: more packets in queue.\n", | ||
1276 | __func__); | ||
1277 | |||
1278 | if (MP_TX_AGGR_IN_PROGRESS(card)) { | ||
1279 | if (!MP_TX_AGGR_PORT_LIMIT_REACHED(card) && | ||
1280 | MP_TX_AGGR_BUF_HAS_ROOM(card, pkt_len)) { | ||
1281 | f_precopy_cur_buf = 1; | ||
1282 | |||
1283 | if (!(card->mp_wr_bitmap & | ||
1284 | (1 << card->curr_wr_port)) | ||
1285 | || !MP_TX_AGGR_BUF_HAS_ROOM( | ||
1286 | card, next_pkt_len)) | ||
1287 | f_send_aggr_buf = 1; | ||
1288 | } else { | ||
1289 | /* No room in Aggr buf, send it */ | ||
1290 | f_send_aggr_buf = 1; | ||
1291 | |||
1292 | if (MP_TX_AGGR_PORT_LIMIT_REACHED(card) || | ||
1293 | !(card->mp_wr_bitmap & | ||
1294 | (1 << card->curr_wr_port))) | ||
1295 | f_send_cur_buf = 1; | ||
1296 | else | ||
1297 | f_postcopy_cur_buf = 1; | ||
1298 | } | ||
1299 | } else { | ||
1300 | if (MP_TX_AGGR_BUF_HAS_ROOM(card, pkt_len) | ||
1301 | && (card->mp_wr_bitmap & (1 << card->curr_wr_port))) | ||
1302 | f_precopy_cur_buf = 1; | ||
1303 | else | ||
1304 | f_send_cur_buf = 1; | ||
1305 | } | ||
1306 | } else { | ||
1307 | /* Last pkt in TX queue */ | ||
1308 | dev_dbg(adapter->dev, "info: %s: Last packet in Tx Queue.\n", | ||
1309 | __func__); | ||
1310 | |||
1311 | if (MP_TX_AGGR_IN_PROGRESS(card)) { | ||
1312 | /* some packs in Aggr buf already */ | ||
1313 | f_send_aggr_buf = 1; | ||
1314 | |||
1315 | if (MP_TX_AGGR_BUF_HAS_ROOM(card, pkt_len)) | ||
1316 | f_precopy_cur_buf = 1; | ||
1317 | else | ||
1318 | /* No room in Aggr buf, send it */ | ||
1319 | f_send_cur_buf = 1; | ||
1320 | } else { | ||
1321 | f_send_cur_buf = 1; | ||
1322 | } | ||
1323 | } | ||
1324 | |||
1325 | if (f_precopy_cur_buf) { | ||
1326 | dev_dbg(adapter->dev, "data: %s: precopy current buffer\n", | ||
1327 | __func__); | ||
1328 | MP_TX_AGGR_BUF_PUT(card, payload, pkt_len, port); | ||
1329 | |||
1330 | if (MP_TX_AGGR_PKT_LIMIT_REACHED(card) || | ||
1331 | MP_TX_AGGR_PORT_LIMIT_REACHED(card)) | ||
1332 | /* No more pkts allowed in Aggr buf, send it */ | ||
1333 | f_send_aggr_buf = 1; | ||
1334 | } | ||
1335 | |||
1336 | if (f_send_aggr_buf) { | ||
1337 | dev_dbg(adapter->dev, "data: %s: send aggr buffer: %d %d\n", | ||
1338 | __func__, | ||
1339 | card->mpa_tx.start_port, card->mpa_tx.ports); | ||
1340 | ret = mwifiex_write_data_to_card(adapter, card->mpa_tx.buf, | ||
1341 | card->mpa_tx.buf_len, | ||
1342 | (adapter->ioport | 0x1000 | | ||
1343 | (card->mpa_tx.ports << 4)) + | ||
1344 | card->mpa_tx.start_port); | ||
1345 | |||
1346 | MP_TX_AGGR_BUF_RESET(card); | ||
1347 | } | ||
1348 | |||
1349 | tx_curr_single: | ||
1350 | if (f_send_cur_buf) { | ||
1351 | dev_dbg(adapter->dev, "data: %s: send current buffer %d\n", | ||
1352 | __func__, port); | ||
1353 | ret = mwifiex_write_data_to_card(adapter, payload, pkt_len, | ||
1354 | adapter->ioport + port); | ||
1355 | } | ||
1356 | |||
1357 | if (f_postcopy_cur_buf) { | ||
1358 | dev_dbg(adapter->dev, "data: %s: postcopy current buffer\n", | ||
1359 | __func__); | ||
1360 | MP_TX_AGGR_BUF_PUT(card, payload, pkt_len, port); | ||
1361 | } | ||
1362 | |||
1363 | return ret; | ||
1364 | } | ||
1365 | |||
1366 | /* | ||
1367 | * This function downloads data from driver to card. | ||
1368 | * | ||
1369 | * Both commands and data packets are transferred to the card by this | ||
1370 | * function. | ||
1371 | * | ||
1372 | * This function adds the SDIO specific header to the front of the buffer | ||
1373 | * before transferring. The header contains the length of the packet and | ||
1374 | * the type. The firmware handles the packets based upon this set type. | ||
1375 | */ | ||
1376 | static int mwifiex_sdio_host_to_card(struct mwifiex_adapter *adapter, | ||
1377 | u8 type, u8 *payload, u32 pkt_len, | ||
1378 | struct mwifiex_tx_param *tx_param) | ||
1379 | { | ||
1380 | struct sdio_mmc_card *card = adapter->card; | ||
1381 | int ret; | ||
1382 | u32 buf_block_len; | ||
1383 | u32 blk_size; | ||
1384 | u8 port = CTRL_PORT; | ||
1385 | |||
1386 | /* Allocate buffer and copy payload */ | ||
1387 | blk_size = MWIFIEX_SDIO_BLOCK_SIZE; | ||
1388 | buf_block_len = (pkt_len + blk_size - 1) / blk_size; | ||
1389 | *(u16 *) &payload[0] = (u16) pkt_len; | ||
1390 | *(u16 *) &payload[2] = type; | ||
1391 | |||
1392 | /* | ||
1393 | * This is SDIO specific header | ||
1394 | * u16 length, | ||
1395 | * u16 type (MWIFIEX_TYPE_DATA = 0, MWIFIEX_TYPE_CMD = 1, | ||
1396 | * MWIFIEX_TYPE_EVENT = 3) | ||
1397 | */ | ||
1398 | if (type == MWIFIEX_TYPE_DATA) { | ||
1399 | ret = mwifiex_get_wr_port_data(adapter, &port); | ||
1400 | if (ret) { | ||
1401 | dev_err(adapter->dev, "%s: no wr_port available\n", | ||
1402 | __func__); | ||
1403 | return ret; | ||
1404 | } | ||
1405 | } else { | ||
1406 | adapter->cmd_sent = true; | ||
1407 | /* Type must be MWIFIEX_TYPE_CMD */ | ||
1408 | |||
1409 | if (pkt_len <= INTF_HEADER_LEN || | ||
1410 | pkt_len > MWIFIEX_UPLD_SIZE) | ||
1411 | dev_err(adapter->dev, "%s: payload=%p, nb=%d\n", | ||
1412 | __func__, payload, pkt_len); | ||
1413 | } | ||
1414 | |||
1415 | /* Transfer data to card */ | ||
1416 | pkt_len = buf_block_len * blk_size; | ||
1417 | |||
1418 | if (tx_param) | ||
1419 | ret = mwifiex_host_to_card_mp_aggr(adapter, payload, pkt_len, | ||
1420 | port, tx_param->next_pkt_len); | ||
1421 | else | ||
1422 | ret = mwifiex_host_to_card_mp_aggr(adapter, payload, pkt_len, | ||
1423 | port, 0); | ||
1424 | |||
1425 | if (ret) { | ||
1426 | if (type == MWIFIEX_TYPE_CMD) | ||
1427 | adapter->cmd_sent = false; | ||
1428 | if (type == MWIFIEX_TYPE_DATA) | ||
1429 | adapter->data_sent = false; | ||
1430 | } else { | ||
1431 | if (type == MWIFIEX_TYPE_DATA) { | ||
1432 | if (!(card->mp_wr_bitmap & (1 << card->curr_wr_port))) | ||
1433 | adapter->data_sent = true; | ||
1434 | else | ||
1435 | adapter->data_sent = false; | ||
1436 | } | ||
1437 | } | ||
1438 | |||
1439 | return ret; | ||
1440 | } | ||
1441 | |||
1442 | /* | ||
1443 | * This function allocates the MPA Tx and Rx buffers. | ||
1444 | */ | ||
1445 | static int mwifiex_alloc_sdio_mpa_buffers(struct mwifiex_adapter *adapter, | ||
1446 | u32 mpa_tx_buf_size, u32 mpa_rx_buf_size) | ||
1447 | { | ||
1448 | struct sdio_mmc_card *card = adapter->card; | ||
1449 | int ret = 0; | ||
1450 | |||
1451 | card->mpa_tx.buf = kzalloc(mpa_tx_buf_size, GFP_KERNEL); | ||
1452 | if (!card->mpa_tx.buf) { | ||
1453 | dev_err(adapter->dev, "could not alloc buffer for MP-A TX\n"); | ||
1454 | ret = -1; | ||
1455 | goto error; | ||
1456 | } | ||
1457 | |||
1458 | card->mpa_tx.buf_size = mpa_tx_buf_size; | ||
1459 | |||
1460 | card->mpa_rx.buf = kzalloc(mpa_rx_buf_size, GFP_KERNEL); | ||
1461 | if (!card->mpa_rx.buf) { | ||
1462 | dev_err(adapter->dev, "could not alloc buffer for MP-A RX\n"); | ||
1463 | ret = -1; | ||
1464 | goto error; | ||
1465 | } | ||
1466 | |||
1467 | card->mpa_rx.buf_size = mpa_rx_buf_size; | ||
1468 | |||
1469 | error: | ||
1470 | if (ret) { | ||
1471 | kfree(card->mpa_tx.buf); | ||
1472 | kfree(card->mpa_rx.buf); | ||
1473 | } | ||
1474 | |||
1475 | return ret; | ||
1476 | } | ||
1477 | |||
1478 | /* | ||
1479 | * This function unregisters the SDIO device. | ||
1480 | * | ||
1481 | * The SDIO IRQ is released, the function is disabled and driver | ||
1482 | * data is set to null. | ||
1483 | */ | ||
1484 | static void | ||
1485 | mwifiex_unregister_dev(struct mwifiex_adapter *adapter) | ||
1486 | { | ||
1487 | struct sdio_mmc_card *card = adapter->card; | ||
1488 | |||
1489 | if (adapter->card) { | ||
1490 | /* Release the SDIO IRQ */ | ||
1491 | sdio_claim_host(card->func); | ||
1492 | sdio_release_irq(card->func); | ||
1493 | sdio_disable_func(card->func); | ||
1494 | sdio_release_host(card->func); | ||
1495 | sdio_set_drvdata(card->func, NULL); | ||
1496 | } | ||
1497 | } | ||
1498 | |||
1499 | /* | ||
1500 | * This function registers the SDIO device. | ||
1501 | * | ||
1502 | * SDIO IRQ is claimed, block size is set and driver data is initialized. | ||
1503 | */ | ||
1504 | static int mwifiex_register_dev(struct mwifiex_adapter *adapter) | ||
1505 | { | ||
1506 | int ret = 0; | ||
1507 | struct sdio_mmc_card *card = adapter->card; | ||
1508 | struct sdio_func *func = card->func; | ||
1509 | |||
1510 | /* save adapter pointer in card */ | ||
1511 | card->adapter = adapter; | ||
1512 | |||
1513 | sdio_claim_host(func); | ||
1514 | |||
1515 | /* Request the SDIO IRQ */ | ||
1516 | ret = sdio_claim_irq(func, mwifiex_sdio_interrupt); | ||
1517 | if (ret) { | ||
1518 | pr_err("claim irq failed: ret=%d\n", ret); | ||
1519 | goto disable_func; | ||
1520 | } | ||
1521 | |||
1522 | /* Set block size */ | ||
1523 | ret = sdio_set_block_size(card->func, MWIFIEX_SDIO_BLOCK_SIZE); | ||
1524 | if (ret) { | ||
1525 | pr_err("cannot set SDIO block size\n"); | ||
1526 | ret = -1; | ||
1527 | goto release_irq; | ||
1528 | } | ||
1529 | |||
1530 | sdio_release_host(func); | ||
1531 | sdio_set_drvdata(func, card); | ||
1532 | |||
1533 | adapter->dev = &func->dev; | ||
1534 | |||
1535 | return 0; | ||
1536 | |||
1537 | release_irq: | ||
1538 | sdio_release_irq(func); | ||
1539 | disable_func: | ||
1540 | sdio_disable_func(func); | ||
1541 | sdio_release_host(func); | ||
1542 | adapter->card = NULL; | ||
1543 | |||
1544 | return -1; | ||
1545 | } | ||
1546 | |||
1547 | /* | ||
1548 | * This function initializes the SDIO driver. | ||
1549 | * | ||
1550 | * The following initializations steps are followed - | ||
1551 | * - Read the Host interrupt status register to acknowledge | ||
1552 | * the first interrupt got from bootloader | ||
1553 | * - Disable host interrupt mask register | ||
1554 | * - Get SDIO port | ||
1555 | * - Get revision ID | ||
1556 | * - Initialize SDIO variables in card | ||
1557 | * - Allocate MP registers | ||
1558 | * - Allocate MPA Tx and Rx buffers | ||
1559 | */ | ||
1560 | static int mwifiex_init_sdio(struct mwifiex_adapter *adapter) | ||
1561 | { | ||
1562 | struct sdio_mmc_card *card = adapter->card; | ||
1563 | int ret; | ||
1564 | u32 sdio_ireg; | ||
1565 | |||
1566 | /* | ||
1567 | * Read the HOST_INT_STATUS_REG for ACK the first interrupt got | ||
1568 | * from the bootloader. If we don't do this we get a interrupt | ||
1569 | * as soon as we register the irq. | ||
1570 | */ | ||
1571 | mwifiex_read_reg(adapter, HOST_INTSTATUS_REG, &sdio_ireg); | ||
1572 | |||
1573 | /* Disable host interrupt mask register for SDIO */ | ||
1574 | mwifiex_sdio_disable_host_int(adapter); | ||
1575 | |||
1576 | /* Get SDIO ioport */ | ||
1577 | mwifiex_init_sdio_ioport(adapter); | ||
1578 | |||
1579 | /* Get revision ID */ | ||
1580 | #define REV_ID_REG 0x5c | ||
1581 | mwifiex_read_reg(adapter, REV_ID_REG, &adapter->revision_id); | ||
1582 | |||
1583 | /* Initialize SDIO variables in card */ | ||
1584 | card->mp_rd_bitmap = 0; | ||
1585 | card->mp_wr_bitmap = 0; | ||
1586 | card->curr_rd_port = 1; | ||
1587 | card->curr_wr_port = 1; | ||
1588 | |||
1589 | card->mp_data_port_mask = DATA_PORT_MASK; | ||
1590 | |||
1591 | card->mpa_tx.buf_len = 0; | ||
1592 | card->mpa_tx.pkt_cnt = 0; | ||
1593 | card->mpa_tx.start_port = 0; | ||
1594 | |||
1595 | card->mpa_tx.enabled = 0; | ||
1596 | card->mpa_tx.pkt_aggr_limit = SDIO_MP_AGGR_DEF_PKT_LIMIT; | ||
1597 | |||
1598 | card->mpa_rx.buf_len = 0; | ||
1599 | card->mpa_rx.pkt_cnt = 0; | ||
1600 | card->mpa_rx.start_port = 0; | ||
1601 | |||
1602 | card->mpa_rx.enabled = 0; | ||
1603 | card->mpa_rx.pkt_aggr_limit = SDIO_MP_AGGR_DEF_PKT_LIMIT; | ||
1604 | |||
1605 | /* Allocate buffers for SDIO MP-A */ | ||
1606 | card->mp_regs = kzalloc(MAX_MP_REGS, GFP_KERNEL); | ||
1607 | if (!card->mp_regs) { | ||
1608 | dev_err(adapter->dev, "failed to alloc mp_regs\n"); | ||
1609 | return -ENOMEM; | ||
1610 | } | ||
1611 | |||
1612 | ret = mwifiex_alloc_sdio_mpa_buffers(adapter, | ||
1613 | SDIO_MP_TX_AGGR_DEF_BUF_SIZE, | ||
1614 | SDIO_MP_RX_AGGR_DEF_BUF_SIZE); | ||
1615 | if (ret) { | ||
1616 | dev_err(adapter->dev, "failed to alloc sdio mp-a buffers\n"); | ||
1617 | kfree(card->mp_regs); | ||
1618 | return -1; | ||
1619 | } | ||
1620 | |||
1621 | return ret; | ||
1622 | } | ||
1623 | |||
1624 | /* | ||
1625 | * This function resets the MPA Tx and Rx buffers. | ||
1626 | */ | ||
1627 | static void mwifiex_cleanup_mpa_buf(struct mwifiex_adapter *adapter) | ||
1628 | { | ||
1629 | struct sdio_mmc_card *card = adapter->card; | ||
1630 | |||
1631 | MP_TX_AGGR_BUF_RESET(card); | ||
1632 | MP_RX_AGGR_BUF_RESET(card); | ||
1633 | } | ||
1634 | |||
1635 | /* | ||
1636 | * This function cleans up the allocated card buffers. | ||
1637 | * | ||
1638 | * The following are freed by this function - | ||
1639 | * - MP registers | ||
1640 | * - MPA Tx buffer | ||
1641 | * - MPA Rx buffer | ||
1642 | */ | ||
1643 | static void mwifiex_cleanup_sdio(struct mwifiex_adapter *adapter) | ||
1644 | { | ||
1645 | struct sdio_mmc_card *card = adapter->card; | ||
1646 | |||
1647 | kfree(card->mp_regs); | ||
1648 | kfree(card->mpa_tx.buf); | ||
1649 | kfree(card->mpa_rx.buf); | ||
1650 | } | ||
1651 | |||
1652 | /* | ||
1653 | * This function updates the MP end port in card. | ||
1654 | */ | ||
1655 | static void | ||
1656 | mwifiex_update_mp_end_port(struct mwifiex_adapter *adapter, u16 port) | ||
1657 | { | ||
1658 | struct sdio_mmc_card *card = adapter->card; | ||
1659 | int i; | ||
1660 | |||
1661 | card->mp_end_port = port; | ||
1662 | |||
1663 | card->mp_data_port_mask = DATA_PORT_MASK; | ||
1664 | |||
1665 | for (i = 1; i <= MAX_PORT - card->mp_end_port; i++) | ||
1666 | card->mp_data_port_mask &= ~(1 << (MAX_PORT - i)); | ||
1667 | |||
1668 | card->curr_wr_port = 1; | ||
1669 | |||
1670 | dev_dbg(adapter->dev, "cmd: mp_end_port %d, data port mask 0x%x\n", | ||
1671 | port, card->mp_data_port_mask); | ||
1672 | } | ||
1673 | |||
1674 | static struct mwifiex_if_ops sdio_ops = { | ||
1675 | .init_if = mwifiex_init_sdio, | ||
1676 | .cleanup_if = mwifiex_cleanup_sdio, | ||
1677 | .check_fw_status = mwifiex_check_fw_status, | ||
1678 | .prog_fw = mwifiex_prog_fw_w_helper, | ||
1679 | .register_dev = mwifiex_register_dev, | ||
1680 | .unregister_dev = mwifiex_unregister_dev, | ||
1681 | .enable_int = mwifiex_sdio_enable_host_int, | ||
1682 | .process_int_status = mwifiex_process_int_status, | ||
1683 | .host_to_card = mwifiex_sdio_host_to_card, | ||
1684 | .wakeup = mwifiex_pm_wakeup_card, | ||
1685 | .wakeup_complete = mwifiex_pm_wakeup_card_complete, | ||
1686 | |||
1687 | /* SDIO specific */ | ||
1688 | .update_mp_end_port = mwifiex_update_mp_end_port, | ||
1689 | .cleanup_mpa_buf = mwifiex_cleanup_mpa_buf, | ||
1690 | }; | ||
1691 | |||
1692 | /* | ||
1693 | * This function initializes the SDIO driver. | ||
1694 | * | ||
1695 | * This initiates the semaphore and registers the device with | ||
1696 | * SDIO bus. | ||
1697 | */ | ||
1698 | static int | ||
1699 | mwifiex_sdio_init_module(void) | ||
1700 | { | ||
1701 | sema_init(&add_remove_card_sem, 1); | ||
1702 | |||
1703 | return sdio_register_driver(&mwifiex_sdio); | ||
1704 | } | ||
1705 | |||
1706 | /* | ||
1707 | * This function cleans up the SDIO driver. | ||
1708 | * | ||
1709 | * The following major steps are followed for cleanup - | ||
1710 | * - Resume the device if its suspended | ||
1711 | * - Disconnect the device if connected | ||
1712 | * - Shutdown the firmware | ||
1713 | * - Unregister the device from SDIO bus. | ||
1714 | */ | ||
1715 | static void | ||
1716 | mwifiex_sdio_cleanup_module(void) | ||
1717 | { | ||
1718 | struct mwifiex_adapter *adapter = g_adapter; | ||
1719 | int i; | ||
1720 | |||
1721 | if (down_interruptible(&add_remove_card_sem)) | ||
1722 | goto exit_sem_err; | ||
1723 | |||
1724 | if (!adapter || !adapter->priv_num) | ||
1725 | goto exit; | ||
1726 | |||
1727 | if (adapter->is_suspended) | ||
1728 | mwifiex_sdio_resume(adapter->dev); | ||
1729 | |||
1730 | for (i = 0; i < adapter->priv_num; i++) | ||
1731 | if ((GET_BSS_ROLE(adapter->priv[i]) == MWIFIEX_BSS_ROLE_STA) && | ||
1732 | adapter->priv[i]->media_connected) | ||
1733 | mwifiex_deauthenticate(adapter->priv[i], NULL); | ||
1734 | |||
1735 | if (!adapter->surprise_removed) | ||
1736 | mwifiex_init_shutdown_fw(mwifiex_get_priv(adapter, | ||
1737 | MWIFIEX_BSS_ROLE_ANY), | ||
1738 | MWIFIEX_FUNC_SHUTDOWN); | ||
1739 | |||
1740 | exit: | ||
1741 | up(&add_remove_card_sem); | ||
1742 | |||
1743 | exit_sem_err: | ||
1744 | sdio_unregister_driver(&mwifiex_sdio); | ||
1745 | } | ||
1746 | |||
1747 | module_init(mwifiex_sdio_init_module); | ||
1748 | module_exit(mwifiex_sdio_cleanup_module); | ||
1749 | |||
1750 | MODULE_AUTHOR("Marvell International Ltd."); | ||
1751 | MODULE_DESCRIPTION("Marvell WiFi-Ex SDIO Driver version " SDIO_VERSION); | ||
1752 | MODULE_VERSION(SDIO_VERSION); | ||
1753 | MODULE_LICENSE("GPL v2"); | ||
1754 | 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..4e97e90aa399 --- /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 w/o 4 bytes of interface header */ | ||
171 | #define MWIFIEX_EVENT_HEADER_LEN 4 | ||
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..8af3a78d2723 --- /dev/null +++ b/drivers/net/wireless/mwifiex/sta_cmd.c | |||
@@ -0,0 +1,1219 @@ | |||
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 host_cmd_ds_command *cmd) | ||
194 | { | ||
195 | cmd->command = cpu_to_le16(HostCmd_CMD_802_11_GET_LOG); | ||
196 | cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_802_11_get_log) + | ||
197 | S_DS_GEN); | ||
198 | return 0; | ||
199 | } | ||
200 | |||
201 | /* | ||
202 | * This function prepares command to set/get Tx data rate configuration. | ||
203 | * | ||
204 | * Preparation includes - | ||
205 | * - Setting command ID, action and proper size | ||
206 | * - Setting configuration index, rate scope and rate drop pattern | ||
207 | * parameters (as required) | ||
208 | * - Ensuring correct endian-ness | ||
209 | */ | ||
210 | static int mwifiex_cmd_tx_rate_cfg(struct mwifiex_private *priv, | ||
211 | struct host_cmd_ds_command *cmd, | ||
212 | u16 cmd_action, void *data_buf) | ||
213 | { | ||
214 | struct host_cmd_ds_tx_rate_cfg *rate_cfg = &cmd->params.tx_rate_cfg; | ||
215 | struct mwifiex_rate_scope *rate_scope; | ||
216 | struct mwifiex_rate_drop_pattern *rate_drop; | ||
217 | u16 *pbitmap_rates = (u16 *) data_buf; | ||
218 | |||
219 | u32 i; | ||
220 | |||
221 | cmd->command = cpu_to_le16(HostCmd_CMD_TX_RATE_CFG); | ||
222 | |||
223 | rate_cfg->action = cpu_to_le16(cmd_action); | ||
224 | rate_cfg->cfg_index = 0; | ||
225 | |||
226 | rate_scope = (struct mwifiex_rate_scope *) ((u8 *) rate_cfg + | ||
227 | sizeof(struct host_cmd_ds_tx_rate_cfg)); | ||
228 | rate_scope->type = cpu_to_le16(TLV_TYPE_RATE_SCOPE); | ||
229 | rate_scope->length = cpu_to_le16(sizeof(struct mwifiex_rate_scope) - | ||
230 | sizeof(struct mwifiex_ie_types_header)); | ||
231 | if (pbitmap_rates != NULL) { | ||
232 | rate_scope->hr_dsss_rate_bitmap = cpu_to_le16(pbitmap_rates[0]); | ||
233 | rate_scope->ofdm_rate_bitmap = cpu_to_le16(pbitmap_rates[1]); | ||
234 | for (i = 0; | ||
235 | i < sizeof(rate_scope->ht_mcs_rate_bitmap) / sizeof(u16); | ||
236 | i++) | ||
237 | rate_scope->ht_mcs_rate_bitmap[i] = | ||
238 | cpu_to_le16(pbitmap_rates[2 + i]); | ||
239 | } else { | ||
240 | rate_scope->hr_dsss_rate_bitmap = | ||
241 | cpu_to_le16(priv->bitmap_rates[0]); | ||
242 | rate_scope->ofdm_rate_bitmap = | ||
243 | cpu_to_le16(priv->bitmap_rates[1]); | ||
244 | for (i = 0; | ||
245 | i < sizeof(rate_scope->ht_mcs_rate_bitmap) / sizeof(u16); | ||
246 | i++) | ||
247 | rate_scope->ht_mcs_rate_bitmap[i] = | ||
248 | cpu_to_le16(priv->bitmap_rates[2 + i]); | ||
249 | } | ||
250 | |||
251 | rate_drop = (struct mwifiex_rate_drop_pattern *) ((u8 *) rate_scope + | ||
252 | sizeof(struct mwifiex_rate_scope)); | ||
253 | rate_drop->type = cpu_to_le16(TLV_TYPE_RATE_DROP_CONTROL); | ||
254 | rate_drop->length = cpu_to_le16(sizeof(rate_drop->rate_drop_mode)); | ||
255 | rate_drop->rate_drop_mode = 0; | ||
256 | |||
257 | cmd->size = | ||
258 | cpu_to_le16(S_DS_GEN + sizeof(struct host_cmd_ds_tx_rate_cfg) + | ||
259 | sizeof(struct mwifiex_rate_scope) + | ||
260 | sizeof(struct mwifiex_rate_drop_pattern)); | ||
261 | |||
262 | return 0; | ||
263 | } | ||
264 | |||
265 | /* | ||
266 | * This function prepares command to set/get Tx power configuration. | ||
267 | * | ||
268 | * Preparation includes - | ||
269 | * - Setting command ID, action and proper size | ||
270 | * - Setting Tx power mode, power group TLV | ||
271 | * (as required) | ||
272 | * - Ensuring correct endian-ness | ||
273 | */ | ||
274 | static int mwifiex_cmd_tx_power_cfg(struct host_cmd_ds_command *cmd, | ||
275 | u16 cmd_action, void *data_buf) | ||
276 | { | ||
277 | struct mwifiex_types_power_group *pg_tlv; | ||
278 | struct host_cmd_ds_txpwr_cfg *txp; | ||
279 | struct host_cmd_ds_txpwr_cfg *cmd_txp_cfg = &cmd->params.txp_cfg; | ||
280 | |||
281 | cmd->command = cpu_to_le16(HostCmd_CMD_TXPWR_CFG); | ||
282 | cmd->size = | ||
283 | cpu_to_le16(S_DS_GEN + sizeof(struct host_cmd_ds_txpwr_cfg)); | ||
284 | switch (cmd_action) { | ||
285 | case HostCmd_ACT_GEN_SET: | ||
286 | txp = (struct host_cmd_ds_txpwr_cfg *) data_buf; | ||
287 | if (txp->mode) { | ||
288 | pg_tlv = (struct mwifiex_types_power_group | ||
289 | *) ((unsigned long) data_buf + | ||
290 | sizeof(struct host_cmd_ds_txpwr_cfg)); | ||
291 | memmove(cmd_txp_cfg, data_buf, | ||
292 | sizeof(struct host_cmd_ds_txpwr_cfg) + | ||
293 | sizeof(struct mwifiex_types_power_group) + | ||
294 | pg_tlv->length); | ||
295 | |||
296 | pg_tlv = (struct mwifiex_types_power_group *) ((u8 *) | ||
297 | cmd_txp_cfg + | ||
298 | sizeof(struct host_cmd_ds_txpwr_cfg)); | ||
299 | cmd->size = cpu_to_le16(le16_to_cpu(cmd->size) + | ||
300 | sizeof(struct mwifiex_types_power_group) + | ||
301 | pg_tlv->length); | ||
302 | } else { | ||
303 | memmove(cmd_txp_cfg, data_buf, | ||
304 | sizeof(struct host_cmd_ds_txpwr_cfg)); | ||
305 | } | ||
306 | cmd_txp_cfg->action = cpu_to_le16(cmd_action); | ||
307 | break; | ||
308 | case HostCmd_ACT_GEN_GET: | ||
309 | cmd_txp_cfg->action = cpu_to_le16(cmd_action); | ||
310 | break; | ||
311 | } | ||
312 | |||
313 | return 0; | ||
314 | } | ||
315 | |||
316 | /* | ||
317 | * This function prepares command to set Host Sleep configuration. | ||
318 | * | ||
319 | * Preparation includes - | ||
320 | * - Setting command ID and proper size | ||
321 | * - Setting Host Sleep action, conditions, ARP filters | ||
322 | * (as required) | ||
323 | * - Ensuring correct endian-ness | ||
324 | */ | ||
325 | static int mwifiex_cmd_802_11_hs_cfg(struct mwifiex_private *priv, | ||
326 | struct host_cmd_ds_command *cmd, | ||
327 | u16 cmd_action, | ||
328 | struct mwifiex_hs_config_param *data_buf) | ||
329 | { | ||
330 | struct mwifiex_adapter *adapter = priv->adapter; | ||
331 | struct host_cmd_ds_802_11_hs_cfg_enh *hs_cfg = &cmd->params.opt_hs_cfg; | ||
332 | u16 hs_activate = false; | ||
333 | |||
334 | if (data_buf == NULL) | ||
335 | /* New Activate command */ | ||
336 | hs_activate = true; | ||
337 | cmd->command = cpu_to_le16(HostCmd_CMD_802_11_HS_CFG_ENH); | ||
338 | |||
339 | if (!hs_activate && | ||
340 | (data_buf->conditions | ||
341 | != cpu_to_le32(HOST_SLEEP_CFG_CANCEL)) | ||
342 | && ((adapter->arp_filter_size > 0) | ||
343 | && (adapter->arp_filter_size <= ARP_FILTER_MAX_BUF_SIZE))) { | ||
344 | dev_dbg(adapter->dev, | ||
345 | "cmd: Attach %d bytes ArpFilter to HSCfg cmd\n", | ||
346 | adapter->arp_filter_size); | ||
347 | memcpy(((u8 *) hs_cfg) + | ||
348 | sizeof(struct host_cmd_ds_802_11_hs_cfg_enh), | ||
349 | adapter->arp_filter, adapter->arp_filter_size); | ||
350 | cmd->size = cpu_to_le16(adapter->arp_filter_size + | ||
351 | sizeof(struct host_cmd_ds_802_11_hs_cfg_enh) | ||
352 | + S_DS_GEN); | ||
353 | } else { | ||
354 | cmd->size = cpu_to_le16(S_DS_GEN + sizeof(struct | ||
355 | host_cmd_ds_802_11_hs_cfg_enh)); | ||
356 | } | ||
357 | if (hs_activate) { | ||
358 | hs_cfg->action = cpu_to_le16(HS_ACTIVATE); | ||
359 | hs_cfg->params.hs_activate.resp_ctrl = RESP_NEEDED; | ||
360 | } else { | ||
361 | hs_cfg->action = cpu_to_le16(HS_CONFIGURE); | ||
362 | hs_cfg->params.hs_config.conditions = data_buf->conditions; | ||
363 | hs_cfg->params.hs_config.gpio = data_buf->gpio; | ||
364 | hs_cfg->params.hs_config.gap = data_buf->gap; | ||
365 | dev_dbg(adapter->dev, | ||
366 | "cmd: HS_CFG_CMD: condition:0x%x gpio:0x%x gap:0x%x\n", | ||
367 | hs_cfg->params.hs_config.conditions, | ||
368 | hs_cfg->params.hs_config.gpio, | ||
369 | hs_cfg->params.hs_config.gap); | ||
370 | } | ||
371 | |||
372 | return 0; | ||
373 | } | ||
374 | |||
375 | /* | ||
376 | * This function prepares command to set/get MAC address. | ||
377 | * | ||
378 | * Preparation includes - | ||
379 | * - Setting command ID, action and proper size | ||
380 | * - Setting MAC address (for SET only) | ||
381 | * - Ensuring correct endian-ness | ||
382 | */ | ||
383 | static int mwifiex_cmd_802_11_mac_address(struct mwifiex_private *priv, | ||
384 | struct host_cmd_ds_command *cmd, | ||
385 | u16 cmd_action) | ||
386 | { | ||
387 | cmd->command = cpu_to_le16(HostCmd_CMD_802_11_MAC_ADDRESS); | ||
388 | cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_802_11_mac_address) + | ||
389 | S_DS_GEN); | ||
390 | cmd->result = 0; | ||
391 | |||
392 | cmd->params.mac_addr.action = cpu_to_le16(cmd_action); | ||
393 | |||
394 | if (cmd_action == HostCmd_ACT_GEN_SET) | ||
395 | memcpy(cmd->params.mac_addr.mac_addr, priv->curr_addr, | ||
396 | ETH_ALEN); | ||
397 | return 0; | ||
398 | } | ||
399 | |||
400 | /* | ||
401 | * This function prepares command to set MAC multicast address. | ||
402 | * | ||
403 | * Preparation includes - | ||
404 | * - Setting command ID, action and proper size | ||
405 | * - Setting MAC multicast address | ||
406 | * - Ensuring correct endian-ness | ||
407 | */ | ||
408 | static int mwifiex_cmd_mac_multicast_adr(struct host_cmd_ds_command *cmd, | ||
409 | u16 cmd_action, void *data_buf) | ||
410 | { | ||
411 | struct mwifiex_multicast_list *mcast_list = | ||
412 | (struct mwifiex_multicast_list *) data_buf; | ||
413 | struct host_cmd_ds_mac_multicast_adr *mcast_addr = &cmd->params.mc_addr; | ||
414 | |||
415 | cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_mac_multicast_adr) + | ||
416 | S_DS_GEN); | ||
417 | cmd->command = cpu_to_le16(HostCmd_CMD_MAC_MULTICAST_ADR); | ||
418 | |||
419 | mcast_addr->action = cpu_to_le16(cmd_action); | ||
420 | mcast_addr->num_of_adrs = | ||
421 | cpu_to_le16((u16) mcast_list->num_multicast_addr); | ||
422 | memcpy(mcast_addr->mac_list, mcast_list->mac_list, | ||
423 | mcast_list->num_multicast_addr * ETH_ALEN); | ||
424 | |||
425 | return 0; | ||
426 | } | ||
427 | |||
428 | /* | ||
429 | * This function prepares command to deauthenticate. | ||
430 | * | ||
431 | * Preparation includes - | ||
432 | * - Setting command ID and proper size | ||
433 | * - Setting AP MAC address and reason code | ||
434 | * - Ensuring correct endian-ness | ||
435 | */ | ||
436 | static int mwifiex_cmd_802_11_deauthenticate(struct mwifiex_private *priv, | ||
437 | struct host_cmd_ds_command *cmd, | ||
438 | void *data_buf) | ||
439 | { | ||
440 | struct host_cmd_ds_802_11_deauthenticate *deauth = &cmd->params.deauth; | ||
441 | |||
442 | cmd->command = cpu_to_le16(HostCmd_CMD_802_11_DEAUTHENTICATE); | ||
443 | cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_802_11_deauthenticate) | ||
444 | + S_DS_GEN); | ||
445 | |||
446 | /* Set AP MAC address */ | ||
447 | memcpy(deauth->mac_addr, (u8 *) data_buf, ETH_ALEN); | ||
448 | |||
449 | dev_dbg(priv->adapter->dev, "cmd: Deauth: %pM\n", deauth->mac_addr); | ||
450 | |||
451 | deauth->reason_code = cpu_to_le16(WLAN_REASON_DEAUTH_LEAVING); | ||
452 | |||
453 | return 0; | ||
454 | } | ||
455 | |||
456 | /* | ||
457 | * This function prepares command to stop Ad-Hoc network. | ||
458 | * | ||
459 | * Preparation includes - | ||
460 | * - Setting command ID and proper size | ||
461 | * - Ensuring correct endian-ness | ||
462 | */ | ||
463 | static int mwifiex_cmd_802_11_ad_hoc_stop(struct host_cmd_ds_command *cmd) | ||
464 | { | ||
465 | cmd->command = cpu_to_le16(HostCmd_CMD_802_11_AD_HOC_STOP); | ||
466 | cmd->size = cpu_to_le16(S_DS_GEN); | ||
467 | return 0; | ||
468 | } | ||
469 | |||
470 | /* | ||
471 | * This function sets WEP key(s) to key parameter TLV(s). | ||
472 | * | ||
473 | * Multi-key parameter TLVs are supported, so we can send multiple | ||
474 | * WEP keys in a single buffer. | ||
475 | */ | ||
476 | static int | ||
477 | mwifiex_set_keyparamset_wep(struct mwifiex_private *priv, | ||
478 | struct mwifiex_ie_type_key_param_set *key_param_set, | ||
479 | u16 *key_param_len) | ||
480 | { | ||
481 | int cur_key_param_len; | ||
482 | u8 i; | ||
483 | |||
484 | /* Multi-key_param_set TLV is supported */ | ||
485 | for (i = 0; i < NUM_WEP_KEYS; i++) { | ||
486 | if ((priv->wep_key[i].key_length == WLAN_KEY_LEN_WEP40) || | ||
487 | (priv->wep_key[i].key_length == WLAN_KEY_LEN_WEP104)) { | ||
488 | key_param_set->type = | ||
489 | cpu_to_le16(TLV_TYPE_KEY_MATERIAL); | ||
490 | /* Key_param_set WEP fixed length */ | ||
491 | #define KEYPARAMSET_WEP_FIXED_LEN 8 | ||
492 | key_param_set->length = cpu_to_le16((u16) | ||
493 | (priv->wep_key[i]. | ||
494 | key_length + | ||
495 | KEYPARAMSET_WEP_FIXED_LEN)); | ||
496 | key_param_set->key_type_id = | ||
497 | cpu_to_le16(KEY_TYPE_ID_WEP); | ||
498 | key_param_set->key_info = | ||
499 | cpu_to_le16(KEY_ENABLED | KEY_UNICAST | | ||
500 | KEY_MCAST); | ||
501 | key_param_set->key_len = | ||
502 | cpu_to_le16(priv->wep_key[i].key_length); | ||
503 | /* Set WEP key index */ | ||
504 | key_param_set->key[0] = i; | ||
505 | /* Set default Tx key flag */ | ||
506 | if (i == | ||
507 | (priv-> | ||
508 | wep_key_curr_index & HostCmd_WEP_KEY_INDEX_MASK)) | ||
509 | key_param_set->key[1] = 1; | ||
510 | else | ||
511 | key_param_set->key[1] = 0; | ||
512 | memmove(&key_param_set->key[2], | ||
513 | priv->wep_key[i].key_material, | ||
514 | priv->wep_key[i].key_length); | ||
515 | |||
516 | cur_key_param_len = priv->wep_key[i].key_length + | ||
517 | KEYPARAMSET_WEP_FIXED_LEN + | ||
518 | sizeof(struct mwifiex_ie_types_header); | ||
519 | *key_param_len += (u16) cur_key_param_len; | ||
520 | key_param_set = | ||
521 | (struct mwifiex_ie_type_key_param_set *) | ||
522 | ((u8 *)key_param_set + | ||
523 | cur_key_param_len); | ||
524 | } else if (!priv->wep_key[i].key_length) { | ||
525 | continue; | ||
526 | } else { | ||
527 | dev_err(priv->adapter->dev, | ||
528 | "key%d Length = %d is incorrect\n", | ||
529 | (i + 1), priv->wep_key[i].key_length); | ||
530 | return -1; | ||
531 | } | ||
532 | } | ||
533 | |||
534 | return 0; | ||
535 | } | ||
536 | |||
537 | /* | ||
538 | * This function prepares command to set/get/reset network key(s). | ||
539 | * | ||
540 | * Preparation includes - | ||
541 | * - Setting command ID, action and proper size | ||
542 | * - Setting WEP keys, WAPI keys or WPA keys along with required | ||
543 | * encryption (TKIP, AES) (as required) | ||
544 | * - Ensuring correct endian-ness | ||
545 | */ | ||
546 | static int mwifiex_cmd_802_11_key_material(struct mwifiex_private *priv, | ||
547 | struct host_cmd_ds_command *cmd, | ||
548 | u16 cmd_action, | ||
549 | u32 cmd_oid, void *data_buf) | ||
550 | { | ||
551 | struct host_cmd_ds_802_11_key_material *key_material = | ||
552 | &cmd->params.key_material; | ||
553 | struct mwifiex_ds_encrypt_key *enc_key = | ||
554 | (struct mwifiex_ds_encrypt_key *) data_buf; | ||
555 | u16 key_param_len = 0; | ||
556 | int ret = 0; | ||
557 | const u8 bc_mac[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; | ||
558 | |||
559 | cmd->command = cpu_to_le16(HostCmd_CMD_802_11_KEY_MATERIAL); | ||
560 | key_material->action = cpu_to_le16(cmd_action); | ||
561 | |||
562 | if (cmd_action == HostCmd_ACT_GEN_GET) { | ||
563 | cmd->size = | ||
564 | cpu_to_le16(sizeof(key_material->action) + S_DS_GEN); | ||
565 | return ret; | ||
566 | } | ||
567 | |||
568 | if (!enc_key) { | ||
569 | memset(&key_material->key_param_set, 0, | ||
570 | (NUM_WEP_KEYS * | ||
571 | sizeof(struct mwifiex_ie_type_key_param_set))); | ||
572 | ret = mwifiex_set_keyparamset_wep(priv, | ||
573 | &key_material->key_param_set, | ||
574 | &key_param_len); | ||
575 | cmd->size = cpu_to_le16(key_param_len + | ||
576 | sizeof(key_material->action) + S_DS_GEN); | ||
577 | return ret; | ||
578 | } else | ||
579 | memset(&key_material->key_param_set, 0, | ||
580 | sizeof(struct mwifiex_ie_type_key_param_set)); | ||
581 | if (enc_key->is_wapi_key) { | ||
582 | dev_dbg(priv->adapter->dev, "info: Set WAPI Key\n"); | ||
583 | key_material->key_param_set.key_type_id = | ||
584 | cpu_to_le16(KEY_TYPE_ID_WAPI); | ||
585 | if (cmd_oid == KEY_INFO_ENABLED) | ||
586 | key_material->key_param_set.key_info = | ||
587 | cpu_to_le16(KEY_ENABLED); | ||
588 | else | ||
589 | key_material->key_param_set.key_info = | ||
590 | cpu_to_le16(!KEY_ENABLED); | ||
591 | |||
592 | key_material->key_param_set.key[0] = enc_key->key_index; | ||
593 | if (!priv->sec_info.wapi_key_on) | ||
594 | key_material->key_param_set.key[1] = 1; | ||
595 | else | ||
596 | /* set 0 when re-key */ | ||
597 | key_material->key_param_set.key[1] = 0; | ||
598 | |||
599 | if (0 != memcmp(enc_key->mac_addr, bc_mac, sizeof(bc_mac))) { | ||
600 | /* WAPI pairwise key: unicast */ | ||
601 | key_material->key_param_set.key_info |= | ||
602 | cpu_to_le16(KEY_UNICAST); | ||
603 | } else { /* WAPI group key: multicast */ | ||
604 | key_material->key_param_set.key_info |= | ||
605 | cpu_to_le16(KEY_MCAST); | ||
606 | priv->sec_info.wapi_key_on = true; | ||
607 | } | ||
608 | |||
609 | key_material->key_param_set.type = | ||
610 | cpu_to_le16(TLV_TYPE_KEY_MATERIAL); | ||
611 | key_material->key_param_set.key_len = | ||
612 | cpu_to_le16(WAPI_KEY_LEN); | ||
613 | memcpy(&key_material->key_param_set.key[2], | ||
614 | enc_key->key_material, enc_key->key_len); | ||
615 | memcpy(&key_material->key_param_set.key[2 + enc_key->key_len], | ||
616 | enc_key->wapi_rxpn, WAPI_RXPN_LEN); | ||
617 | key_material->key_param_set.length = | ||
618 | cpu_to_le16(WAPI_KEY_LEN + KEYPARAMSET_FIXED_LEN); | ||
619 | |||
620 | key_param_len = (WAPI_KEY_LEN + KEYPARAMSET_FIXED_LEN) + | ||
621 | sizeof(struct mwifiex_ie_types_header); | ||
622 | cmd->size = cpu_to_le16(key_param_len + | ||
623 | sizeof(key_material->action) + S_DS_GEN); | ||
624 | return ret; | ||
625 | } | ||
626 | if (enc_key->key_len == WLAN_KEY_LEN_CCMP) { | ||
627 | dev_dbg(priv->adapter->dev, "cmd: WPA_AES\n"); | ||
628 | key_material->key_param_set.key_type_id = | ||
629 | cpu_to_le16(KEY_TYPE_ID_AES); | ||
630 | if (cmd_oid == KEY_INFO_ENABLED) | ||
631 | key_material->key_param_set.key_info = | ||
632 | cpu_to_le16(KEY_ENABLED); | ||
633 | else | ||
634 | key_material->key_param_set.key_info = | ||
635 | cpu_to_le16(!KEY_ENABLED); | ||
636 | |||
637 | if (enc_key->key_index & MWIFIEX_KEY_INDEX_UNICAST) | ||
638 | /* AES pairwise key: unicast */ | ||
639 | key_material->key_param_set.key_info |= | ||
640 | cpu_to_le16(KEY_UNICAST); | ||
641 | else /* AES group key: multicast */ | ||
642 | key_material->key_param_set.key_info |= | ||
643 | cpu_to_le16(KEY_MCAST); | ||
644 | } else if (enc_key->key_len == WLAN_KEY_LEN_TKIP) { | ||
645 | dev_dbg(priv->adapter->dev, "cmd: WPA_TKIP\n"); | ||
646 | key_material->key_param_set.key_type_id = | ||
647 | cpu_to_le16(KEY_TYPE_ID_TKIP); | ||
648 | key_material->key_param_set.key_info = | ||
649 | cpu_to_le16(KEY_ENABLED); | ||
650 | |||
651 | if (enc_key->key_index & MWIFIEX_KEY_INDEX_UNICAST) | ||
652 | /* TKIP pairwise key: unicast */ | ||
653 | key_material->key_param_set.key_info |= | ||
654 | cpu_to_le16(KEY_UNICAST); | ||
655 | else /* TKIP group key: multicast */ | ||
656 | key_material->key_param_set.key_info |= | ||
657 | cpu_to_le16(KEY_MCAST); | ||
658 | } | ||
659 | |||
660 | if (key_material->key_param_set.key_type_id) { | ||
661 | key_material->key_param_set.type = | ||
662 | cpu_to_le16(TLV_TYPE_KEY_MATERIAL); | ||
663 | key_material->key_param_set.key_len = | ||
664 | cpu_to_le16((u16) enc_key->key_len); | ||
665 | memcpy(key_material->key_param_set.key, enc_key->key_material, | ||
666 | enc_key->key_len); | ||
667 | key_material->key_param_set.length = | ||
668 | cpu_to_le16((u16) enc_key->key_len + | ||
669 | KEYPARAMSET_FIXED_LEN); | ||
670 | |||
671 | key_param_len = (u16) (enc_key->key_len + KEYPARAMSET_FIXED_LEN) | ||
672 | + sizeof(struct mwifiex_ie_types_header); | ||
673 | |||
674 | cmd->size = cpu_to_le16(key_param_len + | ||
675 | sizeof(key_material->action) + S_DS_GEN); | ||
676 | } | ||
677 | |||
678 | return ret; | ||
679 | } | ||
680 | |||
681 | /* | ||
682 | * This function prepares command to set/get 11d domain information. | ||
683 | * | ||
684 | * Preparation includes - | ||
685 | * - Setting command ID, action and proper size | ||
686 | * - Setting domain information fields (for SET only) | ||
687 | * - Ensuring correct endian-ness | ||
688 | */ | ||
689 | static int mwifiex_cmd_802_11d_domain_info(struct mwifiex_private *priv, | ||
690 | struct host_cmd_ds_command *cmd, | ||
691 | u16 cmd_action) | ||
692 | { | ||
693 | struct mwifiex_adapter *adapter = priv->adapter; | ||
694 | struct host_cmd_ds_802_11d_domain_info *domain_info = | ||
695 | &cmd->params.domain_info; | ||
696 | struct mwifiex_ietypes_domain_param_set *domain = | ||
697 | &domain_info->domain; | ||
698 | u8 no_of_triplet = adapter->domain_reg.no_of_triplet; | ||
699 | |||
700 | dev_dbg(adapter->dev, "info: 11D: no_of_triplet=0x%x\n", no_of_triplet); | ||
701 | |||
702 | cmd->command = cpu_to_le16(HostCmd_CMD_802_11D_DOMAIN_INFO); | ||
703 | domain_info->action = cpu_to_le16(cmd_action); | ||
704 | if (cmd_action == HostCmd_ACT_GEN_GET) { | ||
705 | cmd->size = cpu_to_le16(sizeof(domain_info->action) + S_DS_GEN); | ||
706 | return 0; | ||
707 | } | ||
708 | |||
709 | /* Set domain info fields */ | ||
710 | domain->header.type = cpu_to_le16(WLAN_EID_COUNTRY); | ||
711 | memcpy(domain->country_code, adapter->domain_reg.country_code, | ||
712 | sizeof(domain->country_code)); | ||
713 | |||
714 | domain->header.len = cpu_to_le16((no_of_triplet * | ||
715 | sizeof(struct ieee80211_country_ie_triplet)) + | ||
716 | sizeof(domain->country_code)); | ||
717 | |||
718 | if (no_of_triplet) { | ||
719 | memcpy(domain->triplet, adapter->domain_reg.triplet, | ||
720 | no_of_triplet * | ||
721 | sizeof(struct ieee80211_country_ie_triplet)); | ||
722 | |||
723 | cmd->size = cpu_to_le16(sizeof(domain_info->action) + | ||
724 | le16_to_cpu(domain->header.len) + | ||
725 | sizeof(struct mwifiex_ie_types_header) | ||
726 | + S_DS_GEN); | ||
727 | } else { | ||
728 | cmd->size = cpu_to_le16(sizeof(domain_info->action) + S_DS_GEN); | ||
729 | } | ||
730 | |||
731 | return 0; | ||
732 | } | ||
733 | |||
734 | /* | ||
735 | * This function prepares command to set/get RF channel. | ||
736 | * | ||
737 | * Preparation includes - | ||
738 | * - Setting command ID, action and proper size | ||
739 | * - Setting RF type and current RF channel (for SET only) | ||
740 | * - Ensuring correct endian-ness | ||
741 | */ | ||
742 | static int mwifiex_cmd_802_11_rf_channel(struct mwifiex_private *priv, | ||
743 | struct host_cmd_ds_command *cmd, | ||
744 | u16 cmd_action, void *data_buf) | ||
745 | { | ||
746 | struct host_cmd_ds_802_11_rf_channel *rf_chan = | ||
747 | &cmd->params.rf_channel; | ||
748 | uint16_t rf_type = le16_to_cpu(rf_chan->rf_type); | ||
749 | |||
750 | cmd->command = cpu_to_le16(HostCmd_CMD_802_11_RF_CHANNEL); | ||
751 | cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_802_11_rf_channel) | ||
752 | + S_DS_GEN); | ||
753 | |||
754 | if (cmd_action == HostCmd_ACT_GEN_SET) { | ||
755 | if ((priv->adapter->adhoc_start_band & BAND_A) | ||
756 | || (priv->adapter->adhoc_start_band & BAND_AN)) | ||
757 | rf_chan->rf_type = | ||
758 | cpu_to_le16(HostCmd_SCAN_RADIO_TYPE_A); | ||
759 | |||
760 | rf_type = le16_to_cpu(rf_chan->rf_type); | ||
761 | SET_SECONDARYCHAN(rf_type, priv->adapter->chan_offset); | ||
762 | rf_chan->current_channel = cpu_to_le16(*((u16 *) data_buf)); | ||
763 | } | ||
764 | rf_chan->action = cpu_to_le16(cmd_action); | ||
765 | return 0; | ||
766 | } | ||
767 | |||
768 | /* | ||
769 | * This function prepares command to set/get IBSS coalescing status. | ||
770 | * | ||
771 | * Preparation includes - | ||
772 | * - Setting command ID, action and proper size | ||
773 | * - Setting status to enable or disable (for SET only) | ||
774 | * - Ensuring correct endian-ness | ||
775 | */ | ||
776 | static int mwifiex_cmd_ibss_coalescing_status(struct host_cmd_ds_command *cmd, | ||
777 | u16 cmd_action, void *data_buf) | ||
778 | { | ||
779 | struct host_cmd_ds_802_11_ibss_status *ibss_coal = | ||
780 | &(cmd->params.ibss_coalescing); | ||
781 | u16 enable = 0; | ||
782 | |||
783 | cmd->command = cpu_to_le16(HostCmd_CMD_802_11_IBSS_COALESCING_STATUS); | ||
784 | cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_802_11_ibss_status) + | ||
785 | S_DS_GEN); | ||
786 | cmd->result = 0; | ||
787 | ibss_coal->action = cpu_to_le16(cmd_action); | ||
788 | |||
789 | switch (cmd_action) { | ||
790 | case HostCmd_ACT_GEN_SET: | ||
791 | if (data_buf != NULL) | ||
792 | enable = *(u16 *) data_buf; | ||
793 | ibss_coal->enable = cpu_to_le16(enable); | ||
794 | break; | ||
795 | |||
796 | /* In other case.. Nothing to do */ | ||
797 | case HostCmd_ACT_GEN_GET: | ||
798 | default: | ||
799 | break; | ||
800 | } | ||
801 | |||
802 | return 0; | ||
803 | } | ||
804 | |||
805 | /* | ||
806 | * This function prepares command to set/get register value. | ||
807 | * | ||
808 | * Preparation includes - | ||
809 | * - Setting command ID, action and proper size | ||
810 | * - Setting register offset (for both GET and SET) and | ||
811 | * register value (for SET only) | ||
812 | * - Ensuring correct endian-ness | ||
813 | * | ||
814 | * The following type of registers can be accessed with this function - | ||
815 | * - MAC register | ||
816 | * - BBP register | ||
817 | * - RF register | ||
818 | * - PMIC register | ||
819 | * - CAU register | ||
820 | * - EEPROM | ||
821 | */ | ||
822 | static int mwifiex_cmd_reg_access(struct host_cmd_ds_command *cmd, | ||
823 | u16 cmd_action, void *data_buf) | ||
824 | { | ||
825 | struct mwifiex_ds_reg_rw *reg_rw; | ||
826 | |||
827 | reg_rw = (struct mwifiex_ds_reg_rw *) data_buf; | ||
828 | switch (le16_to_cpu(cmd->command)) { | ||
829 | case HostCmd_CMD_MAC_REG_ACCESS: | ||
830 | { | ||
831 | struct host_cmd_ds_mac_reg_access *mac_reg; | ||
832 | |||
833 | cmd->size = cpu_to_le16(sizeof(*mac_reg) + S_DS_GEN); | ||
834 | mac_reg = (struct host_cmd_ds_mac_reg_access *) &cmd-> | ||
835 | params.mac_reg; | ||
836 | mac_reg->action = cpu_to_le16(cmd_action); | ||
837 | mac_reg->offset = | ||
838 | cpu_to_le16((u16) le32_to_cpu(reg_rw->offset)); | ||
839 | mac_reg->value = reg_rw->value; | ||
840 | break; | ||
841 | } | ||
842 | case HostCmd_CMD_BBP_REG_ACCESS: | ||
843 | { | ||
844 | struct host_cmd_ds_bbp_reg_access *bbp_reg; | ||
845 | |||
846 | cmd->size = cpu_to_le16(sizeof(*bbp_reg) + S_DS_GEN); | ||
847 | bbp_reg = (struct host_cmd_ds_bbp_reg_access *) &cmd-> | ||
848 | params.bbp_reg; | ||
849 | bbp_reg->action = cpu_to_le16(cmd_action); | ||
850 | bbp_reg->offset = | ||
851 | cpu_to_le16((u16) le32_to_cpu(reg_rw->offset)); | ||
852 | bbp_reg->value = (u8) le32_to_cpu(reg_rw->value); | ||
853 | break; | ||
854 | } | ||
855 | case HostCmd_CMD_RF_REG_ACCESS: | ||
856 | { | ||
857 | struct host_cmd_ds_rf_reg_access *rf_reg; | ||
858 | |||
859 | cmd->size = cpu_to_le16(sizeof(*rf_reg) + S_DS_GEN); | ||
860 | rf_reg = (struct host_cmd_ds_rf_reg_access *) &cmd-> | ||
861 | params.rf_reg; | ||
862 | rf_reg->action = cpu_to_le16(cmd_action); | ||
863 | rf_reg->offset = | ||
864 | cpu_to_le16((u16) le32_to_cpu(reg_rw->offset)); | ||
865 | rf_reg->value = (u8) le32_to_cpu(reg_rw->value); | ||
866 | break; | ||
867 | } | ||
868 | case HostCmd_CMD_PMIC_REG_ACCESS: | ||
869 | { | ||
870 | struct host_cmd_ds_pmic_reg_access *pmic_reg; | ||
871 | |||
872 | cmd->size = cpu_to_le16(sizeof(*pmic_reg) + S_DS_GEN); | ||
873 | pmic_reg = (struct host_cmd_ds_pmic_reg_access *) &cmd-> | ||
874 | params.pmic_reg; | ||
875 | pmic_reg->action = cpu_to_le16(cmd_action); | ||
876 | pmic_reg->offset = | ||
877 | cpu_to_le16((u16) le32_to_cpu(reg_rw->offset)); | ||
878 | pmic_reg->value = (u8) le32_to_cpu(reg_rw->value); | ||
879 | break; | ||
880 | } | ||
881 | case HostCmd_CMD_CAU_REG_ACCESS: | ||
882 | { | ||
883 | struct host_cmd_ds_rf_reg_access *cau_reg; | ||
884 | |||
885 | cmd->size = cpu_to_le16(sizeof(*cau_reg) + S_DS_GEN); | ||
886 | cau_reg = (struct host_cmd_ds_rf_reg_access *) &cmd-> | ||
887 | params.rf_reg; | ||
888 | cau_reg->action = cpu_to_le16(cmd_action); | ||
889 | cau_reg->offset = | ||
890 | cpu_to_le16((u16) le32_to_cpu(reg_rw->offset)); | ||
891 | cau_reg->value = (u8) le32_to_cpu(reg_rw->value); | ||
892 | break; | ||
893 | } | ||
894 | case HostCmd_CMD_802_11_EEPROM_ACCESS: | ||
895 | { | ||
896 | struct mwifiex_ds_read_eeprom *rd_eeprom = | ||
897 | (struct mwifiex_ds_read_eeprom *) data_buf; | ||
898 | struct host_cmd_ds_802_11_eeprom_access *cmd_eeprom = | ||
899 | (struct host_cmd_ds_802_11_eeprom_access *) | ||
900 | &cmd->params.eeprom; | ||
901 | |||
902 | cmd->size = cpu_to_le16(sizeof(*cmd_eeprom) + S_DS_GEN); | ||
903 | cmd_eeprom->action = cpu_to_le16(cmd_action); | ||
904 | cmd_eeprom->offset = rd_eeprom->offset; | ||
905 | cmd_eeprom->byte_count = rd_eeprom->byte_count; | ||
906 | cmd_eeprom->value = 0; | ||
907 | break; | ||
908 | } | ||
909 | default: | ||
910 | return -1; | ||
911 | } | ||
912 | |||
913 | return 0; | ||
914 | } | ||
915 | |||
916 | /* | ||
917 | * This function prepares the commands before sending them to the firmware. | ||
918 | * | ||
919 | * This is a generic function which calls specific command preparation | ||
920 | * routines based upon the command number. | ||
921 | */ | ||
922 | int mwifiex_sta_prepare_cmd(struct mwifiex_private *priv, uint16_t cmd_no, | ||
923 | u16 cmd_action, u32 cmd_oid, | ||
924 | void *data_buf, void *cmd_buf) | ||
925 | { | ||
926 | struct host_cmd_ds_command *cmd_ptr = | ||
927 | (struct host_cmd_ds_command *) cmd_buf; | ||
928 | int ret = 0; | ||
929 | |||
930 | /* Prepare command */ | ||
931 | switch (cmd_no) { | ||
932 | case HostCmd_CMD_GET_HW_SPEC: | ||
933 | ret = mwifiex_cmd_get_hw_spec(priv, cmd_ptr); | ||
934 | break; | ||
935 | case HostCmd_CMD_MAC_CONTROL: | ||
936 | ret = mwifiex_cmd_mac_control(priv, cmd_ptr, cmd_action, | ||
937 | data_buf); | ||
938 | break; | ||
939 | case HostCmd_CMD_802_11_MAC_ADDRESS: | ||
940 | ret = mwifiex_cmd_802_11_mac_address(priv, cmd_ptr, | ||
941 | cmd_action); | ||
942 | break; | ||
943 | case HostCmd_CMD_MAC_MULTICAST_ADR: | ||
944 | ret = mwifiex_cmd_mac_multicast_adr(cmd_ptr, cmd_action, | ||
945 | data_buf); | ||
946 | break; | ||
947 | case HostCmd_CMD_TX_RATE_CFG: | ||
948 | ret = mwifiex_cmd_tx_rate_cfg(priv, cmd_ptr, cmd_action, | ||
949 | data_buf); | ||
950 | break; | ||
951 | case HostCmd_CMD_TXPWR_CFG: | ||
952 | ret = mwifiex_cmd_tx_power_cfg(cmd_ptr, cmd_action, | ||
953 | data_buf); | ||
954 | break; | ||
955 | case HostCmd_CMD_802_11_PS_MODE_ENH: | ||
956 | ret = mwifiex_cmd_enh_power_mode(priv, cmd_ptr, cmd_action, | ||
957 | (uint16_t)cmd_oid, data_buf); | ||
958 | break; | ||
959 | case HostCmd_CMD_802_11_HS_CFG_ENH: | ||
960 | ret = mwifiex_cmd_802_11_hs_cfg(priv, cmd_ptr, cmd_action, | ||
961 | (struct mwifiex_hs_config_param *) data_buf); | ||
962 | break; | ||
963 | case HostCmd_CMD_802_11_SCAN: | ||
964 | ret = mwifiex_cmd_802_11_scan(cmd_ptr, data_buf); | ||
965 | break; | ||
966 | case HostCmd_CMD_802_11_BG_SCAN_QUERY: | ||
967 | ret = mwifiex_cmd_802_11_bg_scan_query(cmd_ptr); | ||
968 | break; | ||
969 | case HostCmd_CMD_802_11_ASSOCIATE: | ||
970 | ret = mwifiex_cmd_802_11_associate(priv, cmd_ptr, data_buf); | ||
971 | break; | ||
972 | case HostCmd_CMD_802_11_DEAUTHENTICATE: | ||
973 | ret = mwifiex_cmd_802_11_deauthenticate(priv, cmd_ptr, | ||
974 | data_buf); | ||
975 | break; | ||
976 | case HostCmd_CMD_802_11_AD_HOC_START: | ||
977 | ret = mwifiex_cmd_802_11_ad_hoc_start(priv, cmd_ptr, | ||
978 | data_buf); | ||
979 | break; | ||
980 | case HostCmd_CMD_802_11_GET_LOG: | ||
981 | ret = mwifiex_cmd_802_11_get_log(cmd_ptr); | ||
982 | break; | ||
983 | case HostCmd_CMD_802_11_AD_HOC_JOIN: | ||
984 | ret = mwifiex_cmd_802_11_ad_hoc_join(priv, cmd_ptr, | ||
985 | data_buf); | ||
986 | break; | ||
987 | case HostCmd_CMD_802_11_AD_HOC_STOP: | ||
988 | ret = mwifiex_cmd_802_11_ad_hoc_stop(cmd_ptr); | ||
989 | break; | ||
990 | case HostCmd_CMD_RSSI_INFO: | ||
991 | ret = mwifiex_cmd_802_11_rssi_info(priv, cmd_ptr, cmd_action); | ||
992 | break; | ||
993 | case HostCmd_CMD_802_11_SNMP_MIB: | ||
994 | ret = mwifiex_cmd_802_11_snmp_mib(priv, cmd_ptr, cmd_action, | ||
995 | cmd_oid, data_buf); | ||
996 | break; | ||
997 | case HostCmd_CMD_802_11_TX_RATE_QUERY: | ||
998 | cmd_ptr->command = | ||
999 | cpu_to_le16(HostCmd_CMD_802_11_TX_RATE_QUERY); | ||
1000 | cmd_ptr->size = | ||
1001 | cpu_to_le16(sizeof(struct host_cmd_ds_tx_rate_query) + | ||
1002 | S_DS_GEN); | ||
1003 | priv->tx_rate = 0; | ||
1004 | ret = 0; | ||
1005 | break; | ||
1006 | case HostCmd_CMD_VERSION_EXT: | ||
1007 | cmd_ptr->command = cpu_to_le16(cmd_no); | ||
1008 | cmd_ptr->params.verext.version_str_sel = | ||
1009 | (u8) (*((u32 *) data_buf)); | ||
1010 | memcpy(&cmd_ptr->params, data_buf, | ||
1011 | sizeof(struct host_cmd_ds_version_ext)); | ||
1012 | cmd_ptr->size = | ||
1013 | cpu_to_le16(sizeof(struct host_cmd_ds_version_ext) + | ||
1014 | S_DS_GEN); | ||
1015 | ret = 0; | ||
1016 | break; | ||
1017 | case HostCmd_CMD_802_11_RF_CHANNEL: | ||
1018 | ret = mwifiex_cmd_802_11_rf_channel(priv, cmd_ptr, cmd_action, | ||
1019 | data_buf); | ||
1020 | break; | ||
1021 | case HostCmd_CMD_FUNC_INIT: | ||
1022 | if (priv->adapter->hw_status == MWIFIEX_HW_STATUS_RESET) | ||
1023 | priv->adapter->hw_status = MWIFIEX_HW_STATUS_READY; | ||
1024 | cmd_ptr->command = cpu_to_le16(cmd_no); | ||
1025 | cmd_ptr->size = cpu_to_le16(S_DS_GEN); | ||
1026 | break; | ||
1027 | case HostCmd_CMD_FUNC_SHUTDOWN: | ||
1028 | priv->adapter->hw_status = MWIFIEX_HW_STATUS_RESET; | ||
1029 | cmd_ptr->command = cpu_to_le16(cmd_no); | ||
1030 | cmd_ptr->size = cpu_to_le16(S_DS_GEN); | ||
1031 | break; | ||
1032 | case HostCmd_CMD_11N_ADDBA_REQ: | ||
1033 | ret = mwifiex_cmd_11n_addba_req(cmd_ptr, data_buf); | ||
1034 | break; | ||
1035 | case HostCmd_CMD_11N_DELBA: | ||
1036 | ret = mwifiex_cmd_11n_delba(cmd_ptr, data_buf); | ||
1037 | break; | ||
1038 | case HostCmd_CMD_11N_ADDBA_RSP: | ||
1039 | ret = mwifiex_cmd_11n_addba_rsp_gen(priv, cmd_ptr, data_buf); | ||
1040 | break; | ||
1041 | case HostCmd_CMD_802_11_KEY_MATERIAL: | ||
1042 | ret = mwifiex_cmd_802_11_key_material(priv, cmd_ptr, | ||
1043 | cmd_action, cmd_oid, | ||
1044 | data_buf); | ||
1045 | break; | ||
1046 | case HostCmd_CMD_802_11D_DOMAIN_INFO: | ||
1047 | ret = mwifiex_cmd_802_11d_domain_info(priv, cmd_ptr, | ||
1048 | cmd_action); | ||
1049 | break; | ||
1050 | case HostCmd_CMD_RECONFIGURE_TX_BUFF: | ||
1051 | ret = mwifiex_cmd_recfg_tx_buf(priv, cmd_ptr, cmd_action, | ||
1052 | data_buf); | ||
1053 | break; | ||
1054 | case HostCmd_CMD_AMSDU_AGGR_CTRL: | ||
1055 | ret = mwifiex_cmd_amsdu_aggr_ctrl(cmd_ptr, cmd_action, | ||
1056 | data_buf); | ||
1057 | break; | ||
1058 | case HostCmd_CMD_11N_CFG: | ||
1059 | ret = mwifiex_cmd_11n_cfg(cmd_ptr, cmd_action, | ||
1060 | data_buf); | ||
1061 | break; | ||
1062 | case HostCmd_CMD_WMM_GET_STATUS: | ||
1063 | dev_dbg(priv->adapter->dev, | ||
1064 | "cmd: WMM: WMM_GET_STATUS cmd sent\n"); | ||
1065 | cmd_ptr->command = cpu_to_le16(HostCmd_CMD_WMM_GET_STATUS); | ||
1066 | cmd_ptr->size = | ||
1067 | cpu_to_le16(sizeof(struct host_cmd_ds_wmm_get_status) + | ||
1068 | S_DS_GEN); | ||
1069 | ret = 0; | ||
1070 | break; | ||
1071 | case HostCmd_CMD_802_11_IBSS_COALESCING_STATUS: | ||
1072 | ret = mwifiex_cmd_ibss_coalescing_status(cmd_ptr, cmd_action, | ||
1073 | data_buf); | ||
1074 | break; | ||
1075 | case HostCmd_CMD_MAC_REG_ACCESS: | ||
1076 | case HostCmd_CMD_BBP_REG_ACCESS: | ||
1077 | case HostCmd_CMD_RF_REG_ACCESS: | ||
1078 | case HostCmd_CMD_PMIC_REG_ACCESS: | ||
1079 | case HostCmd_CMD_CAU_REG_ACCESS: | ||
1080 | case HostCmd_CMD_802_11_EEPROM_ACCESS: | ||
1081 | ret = mwifiex_cmd_reg_access(cmd_ptr, cmd_action, data_buf); | ||
1082 | break; | ||
1083 | case HostCmd_CMD_SET_BSS_MODE: | ||
1084 | cmd_ptr->command = cpu_to_le16(cmd_no); | ||
1085 | if (priv->bss_mode == NL80211_IFTYPE_ADHOC) | ||
1086 | cmd_ptr->params.bss_mode.con_type = | ||
1087 | CONNECTION_TYPE_ADHOC; | ||
1088 | else if (priv->bss_mode == NL80211_IFTYPE_STATION) | ||
1089 | cmd_ptr->params.bss_mode.con_type = | ||
1090 | CONNECTION_TYPE_INFRA; | ||
1091 | cmd_ptr->size = cpu_to_le16(sizeof(struct | ||
1092 | host_cmd_ds_set_bss_mode) + S_DS_GEN); | ||
1093 | ret = 0; | ||
1094 | break; | ||
1095 | default: | ||
1096 | dev_err(priv->adapter->dev, | ||
1097 | "PREP_CMD: unknown cmd- %#x\n", cmd_no); | ||
1098 | ret = -1; | ||
1099 | break; | ||
1100 | } | ||
1101 | return ret; | ||
1102 | } | ||
1103 | |||
1104 | /* | ||
1105 | * This function issues commands to initialize firmware. | ||
1106 | * | ||
1107 | * This is called after firmware download to bring the card to | ||
1108 | * working state. | ||
1109 | * | ||
1110 | * The following commands are issued sequentially - | ||
1111 | * - Function init (for first interface only) | ||
1112 | * - Read MAC address (for first interface only) | ||
1113 | * - Reconfigure Tx buffer size (for first interface only) | ||
1114 | * - Enable auto deep sleep (for first interface only) | ||
1115 | * - Get Tx rate | ||
1116 | * - Get Tx power | ||
1117 | * - Set IBSS coalescing status | ||
1118 | * - Set AMSDU aggregation control | ||
1119 | * - Set 11d control | ||
1120 | * - Set MAC control (this must be the last command to initialize firmware) | ||
1121 | */ | ||
1122 | int mwifiex_sta_init_cmd(struct mwifiex_private *priv, u8 first_sta) | ||
1123 | { | ||
1124 | int ret; | ||
1125 | u16 enable = true; | ||
1126 | struct mwifiex_ds_11n_amsdu_aggr_ctrl amsdu_aggr_ctrl; | ||
1127 | struct mwifiex_ds_auto_ds auto_ds; | ||
1128 | enum state_11d_t state_11d; | ||
1129 | |||
1130 | if (first_sta) { | ||
1131 | |||
1132 | ret = mwifiex_send_cmd_async(priv, HostCmd_CMD_FUNC_INIT, | ||
1133 | HostCmd_ACT_GEN_SET, 0, NULL); | ||
1134 | if (ret) | ||
1135 | return -1; | ||
1136 | /* Read MAC address from HW */ | ||
1137 | ret = mwifiex_send_cmd_async(priv, HostCmd_CMD_GET_HW_SPEC, | ||
1138 | HostCmd_ACT_GEN_GET, 0, NULL); | ||
1139 | if (ret) | ||
1140 | return -1; | ||
1141 | |||
1142 | /* Reconfigure tx buf size */ | ||
1143 | ret = mwifiex_send_cmd_async(priv, | ||
1144 | HostCmd_CMD_RECONFIGURE_TX_BUFF, | ||
1145 | HostCmd_ACT_GEN_SET, 0, | ||
1146 | &priv->adapter->tx_buf_size); | ||
1147 | if (ret) | ||
1148 | return -1; | ||
1149 | |||
1150 | /* Enable IEEE PS by default */ | ||
1151 | priv->adapter->ps_mode = MWIFIEX_802_11_POWER_MODE_PSP; | ||
1152 | ret = mwifiex_send_cmd_async(priv, | ||
1153 | HostCmd_CMD_802_11_PS_MODE_ENH, | ||
1154 | EN_AUTO_PS, BITMAP_STA_PS, NULL); | ||
1155 | if (ret) | ||
1156 | return -1; | ||
1157 | } | ||
1158 | |||
1159 | /* get tx rate */ | ||
1160 | ret = mwifiex_send_cmd_async(priv, HostCmd_CMD_TX_RATE_CFG, | ||
1161 | HostCmd_ACT_GEN_GET, 0, NULL); | ||
1162 | if (ret) | ||
1163 | return -1; | ||
1164 | priv->data_rate = 0; | ||
1165 | |||
1166 | /* get tx power */ | ||
1167 | ret = mwifiex_send_cmd_async(priv, HostCmd_CMD_TXPWR_CFG, | ||
1168 | HostCmd_ACT_GEN_GET, 0, NULL); | ||
1169 | if (ret) | ||
1170 | return -1; | ||
1171 | |||
1172 | /* set ibss coalescing_status */ | ||
1173 | ret = mwifiex_send_cmd_async(priv, | ||
1174 | HostCmd_CMD_802_11_IBSS_COALESCING_STATUS, | ||
1175 | HostCmd_ACT_GEN_SET, 0, &enable); | ||
1176 | if (ret) | ||
1177 | return -1; | ||
1178 | |||
1179 | memset(&amsdu_aggr_ctrl, 0, sizeof(amsdu_aggr_ctrl)); | ||
1180 | amsdu_aggr_ctrl.enable = true; | ||
1181 | /* Send request to firmware */ | ||
1182 | ret = mwifiex_send_cmd_async(priv, HostCmd_CMD_AMSDU_AGGR_CTRL, | ||
1183 | HostCmd_ACT_GEN_SET, 0, | ||
1184 | (void *) &amsdu_aggr_ctrl); | ||
1185 | if (ret) | ||
1186 | return -1; | ||
1187 | /* MAC Control must be the last command in init_fw */ | ||
1188 | /* set MAC Control */ | ||
1189 | ret = mwifiex_send_cmd_async(priv, HostCmd_CMD_MAC_CONTROL, | ||
1190 | HostCmd_ACT_GEN_SET, 0, | ||
1191 | &priv->curr_pkt_filter); | ||
1192 | if (ret) | ||
1193 | return -1; | ||
1194 | |||
1195 | if (first_sta) { | ||
1196 | /* Enable auto deep sleep */ | ||
1197 | auto_ds.auto_ds = DEEP_SLEEP_ON; | ||
1198 | auto_ds.idle_time = DEEP_SLEEP_IDLE_TIME; | ||
1199 | ret = mwifiex_send_cmd_async(priv, | ||
1200 | HostCmd_CMD_802_11_PS_MODE_ENH, | ||
1201 | EN_AUTO_PS, BITMAP_AUTO_DS, | ||
1202 | &auto_ds); | ||
1203 | if (ret) | ||
1204 | return -1; | ||
1205 | } | ||
1206 | |||
1207 | /* Send cmd to FW to enable/disable 11D function */ | ||
1208 | state_11d = ENABLE_11D; | ||
1209 | ret = mwifiex_send_cmd_async(priv, HostCmd_CMD_802_11_SNMP_MIB, | ||
1210 | HostCmd_ACT_GEN_SET, DOT11D_I, &state_11d); | ||
1211 | if (ret) | ||
1212 | dev_err(priv->adapter->dev, "11D: failed to enable 11D\n"); | ||
1213 | |||
1214 | /* set last_init_cmd */ | ||
1215 | priv->adapter->last_init_cmd = HostCmd_CMD_802_11_SNMP_MIB; | ||
1216 | ret = -EINPROGRESS; | ||
1217 | |||
1218 | return ret; | ||
1219 | } | ||
diff --git a/drivers/net/wireless/mwifiex/sta_cmdresp.c b/drivers/net/wireless/mwifiex/sta_cmdresp.c new file mode 100644 index 000000000000..d08f76429a0a --- /dev/null +++ b/drivers/net/wireless/mwifiex/sta_cmdresp.c | |||
@@ -0,0 +1,972 @@ | |||
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 | { | ||
46 | struct cmd_ctrl_node *cmd_node = NULL, *tmp_node; | ||
47 | struct mwifiex_adapter *adapter = priv->adapter; | ||
48 | struct host_cmd_ds_802_11_ps_mode_enh *pm; | ||
49 | unsigned long flags; | ||
50 | |||
51 | dev_err(adapter->dev, "CMD_RESP: cmd %#x error, result=%#x\n", | ||
52 | resp->command, resp->result); | ||
53 | |||
54 | if (adapter->curr_cmd->wait_q_enabled) | ||
55 | adapter->cmd_wait_q.status = -1; | ||
56 | |||
57 | switch (le16_to_cpu(resp->command)) { | ||
58 | case HostCmd_CMD_802_11_PS_MODE_ENH: | ||
59 | pm = &resp->params.psmode_enh; | ||
60 | dev_err(adapter->dev, "PS_MODE_ENH cmd failed: " | ||
61 | "result=0x%x action=0x%X\n", | ||
62 | resp->result, le16_to_cpu(pm->action)); | ||
63 | /* We do not re-try enter-ps command in ad-hoc mode. */ | ||
64 | if (le16_to_cpu(pm->action) == EN_AUTO_PS && | ||
65 | (le16_to_cpu(pm->params.ps_bitmap) & BITMAP_STA_PS) && | ||
66 | priv->bss_mode == NL80211_IFTYPE_ADHOC) | ||
67 | adapter->ps_mode = MWIFIEX_802_11_POWER_MODE_CAM; | ||
68 | |||
69 | break; | ||
70 | case HostCmd_CMD_802_11_SCAN: | ||
71 | /* Cancel all pending scan command */ | ||
72 | spin_lock_irqsave(&adapter->scan_pending_q_lock, flags); | ||
73 | list_for_each_entry_safe(cmd_node, tmp_node, | ||
74 | &adapter->scan_pending_q, list) { | ||
75 | list_del(&cmd_node->list); | ||
76 | spin_unlock_irqrestore(&adapter->scan_pending_q_lock, | ||
77 | flags); | ||
78 | mwifiex_insert_cmd_to_free_q(adapter, cmd_node); | ||
79 | spin_lock_irqsave(&adapter->scan_pending_q_lock, flags); | ||
80 | } | ||
81 | spin_unlock_irqrestore(&adapter->scan_pending_q_lock, flags); | ||
82 | |||
83 | spin_lock_irqsave(&adapter->mwifiex_cmd_lock, flags); | ||
84 | adapter->scan_processing = false; | ||
85 | spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, flags); | ||
86 | if (priv->report_scan_result) | ||
87 | priv->report_scan_result = false; | ||
88 | if (priv->scan_pending_on_block) { | ||
89 | priv->scan_pending_on_block = false; | ||
90 | up(&priv->async_sem); | ||
91 | } | ||
92 | break; | ||
93 | |||
94 | case HostCmd_CMD_MAC_CONTROL: | ||
95 | break; | ||
96 | |||
97 | default: | ||
98 | break; | ||
99 | } | ||
100 | /* Handling errors here */ | ||
101 | mwifiex_insert_cmd_to_free_q(adapter, adapter->curr_cmd); | ||
102 | |||
103 | spin_lock_irqsave(&adapter->mwifiex_cmd_lock, flags); | ||
104 | adapter->curr_cmd = NULL; | ||
105 | spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, flags); | ||
106 | } | ||
107 | |||
108 | /* | ||
109 | * This function handles the command response of get RSSI info. | ||
110 | * | ||
111 | * Handling includes changing the header fields into CPU format | ||
112 | * and saving the following parameters in driver - | ||
113 | * - Last data and beacon RSSI value | ||
114 | * - Average data and beacon RSSI value | ||
115 | * - Last data and beacon NF value | ||
116 | * - Average data and beacon NF value | ||
117 | * | ||
118 | * The parameters are send to the application as well, along with | ||
119 | * calculated SNR values. | ||
120 | */ | ||
121 | static int mwifiex_ret_802_11_rssi_info(struct mwifiex_private *priv, | ||
122 | struct host_cmd_ds_command *resp, | ||
123 | void *data_buf) | ||
124 | { | ||
125 | struct host_cmd_ds_802_11_rssi_info_rsp *rssi_info_rsp = | ||
126 | &resp->params.rssi_info_rsp; | ||
127 | struct mwifiex_ds_get_signal *signal; | ||
128 | |||
129 | priv->data_rssi_last = le16_to_cpu(rssi_info_rsp->data_rssi_last); | ||
130 | priv->data_nf_last = le16_to_cpu(rssi_info_rsp->data_nf_last); | ||
131 | |||
132 | priv->data_rssi_avg = le16_to_cpu(rssi_info_rsp->data_rssi_avg); | ||
133 | priv->data_nf_avg = le16_to_cpu(rssi_info_rsp->data_nf_avg); | ||
134 | |||
135 | priv->bcn_rssi_last = le16_to_cpu(rssi_info_rsp->bcn_rssi_last); | ||
136 | priv->bcn_nf_last = le16_to_cpu(rssi_info_rsp->bcn_nf_last); | ||
137 | |||
138 | priv->bcn_rssi_avg = le16_to_cpu(rssi_info_rsp->bcn_rssi_avg); | ||
139 | priv->bcn_nf_avg = le16_to_cpu(rssi_info_rsp->bcn_nf_avg); | ||
140 | |||
141 | /* Need to indicate IOCTL complete */ | ||
142 | if (data_buf) { | ||
143 | signal = (struct mwifiex_ds_get_signal *) data_buf; | ||
144 | memset(signal, 0, sizeof(struct mwifiex_ds_get_signal)); | ||
145 | |||
146 | signal->selector = ALL_RSSI_INFO_MASK; | ||
147 | |||
148 | /* RSSI */ | ||
149 | signal->bcn_rssi_last = priv->bcn_rssi_last; | ||
150 | signal->bcn_rssi_avg = priv->bcn_rssi_avg; | ||
151 | signal->data_rssi_last = priv->data_rssi_last; | ||
152 | signal->data_rssi_avg = priv->data_rssi_avg; | ||
153 | |||
154 | /* SNR */ | ||
155 | signal->bcn_snr_last = | ||
156 | CAL_SNR(priv->bcn_rssi_last, priv->bcn_nf_last); | ||
157 | signal->bcn_snr_avg = | ||
158 | CAL_SNR(priv->bcn_rssi_avg, priv->bcn_nf_avg); | ||
159 | signal->data_snr_last = | ||
160 | CAL_SNR(priv->data_rssi_last, priv->data_nf_last); | ||
161 | signal->data_snr_avg = | ||
162 | CAL_SNR(priv->data_rssi_avg, priv->data_nf_avg); | ||
163 | |||
164 | /* NF */ | ||
165 | signal->bcn_nf_last = priv->bcn_nf_last; | ||
166 | signal->bcn_nf_avg = priv->bcn_nf_avg; | ||
167 | signal->data_nf_last = priv->data_nf_last; | ||
168 | signal->data_nf_avg = priv->data_nf_avg; | ||
169 | } | ||
170 | |||
171 | return 0; | ||
172 | } | ||
173 | |||
174 | /* | ||
175 | * This function handles the command response of set/get SNMP | ||
176 | * MIB parameters. | ||
177 | * | ||
178 | * Handling includes changing the header fields into CPU format | ||
179 | * and saving the parameter in driver. | ||
180 | * | ||
181 | * The following parameters are supported - | ||
182 | * - Fragmentation threshold | ||
183 | * - RTS threshold | ||
184 | * - Short retry limit | ||
185 | */ | ||
186 | static int mwifiex_ret_802_11_snmp_mib(struct mwifiex_private *priv, | ||
187 | struct host_cmd_ds_command *resp, | ||
188 | void *data_buf) | ||
189 | { | ||
190 | struct host_cmd_ds_802_11_snmp_mib *smib = &resp->params.smib; | ||
191 | u16 oid = le16_to_cpu(smib->oid); | ||
192 | u16 query_type = le16_to_cpu(smib->query_type); | ||
193 | u32 ul_temp; | ||
194 | |||
195 | dev_dbg(priv->adapter->dev, "info: SNMP_RESP: oid value = %#x," | ||
196 | " query_type = %#x, buf size = %#x\n", | ||
197 | oid, query_type, le16_to_cpu(smib->buf_size)); | ||
198 | if (query_type == HostCmd_ACT_GEN_GET) { | ||
199 | ul_temp = le16_to_cpu(*((__le16 *) (smib->value))); | ||
200 | if (data_buf) | ||
201 | *(u32 *)data_buf = ul_temp; | ||
202 | switch (oid) { | ||
203 | case FRAG_THRESH_I: | ||
204 | dev_dbg(priv->adapter->dev, | ||
205 | "info: SNMP_RESP: FragThsd =%u\n", ul_temp); | ||
206 | break; | ||
207 | case RTS_THRESH_I: | ||
208 | dev_dbg(priv->adapter->dev, | ||
209 | "info: SNMP_RESP: RTSThsd =%u\n", ul_temp); | ||
210 | break; | ||
211 | case SHORT_RETRY_LIM_I: | ||
212 | dev_dbg(priv->adapter->dev, | ||
213 | "info: SNMP_RESP: TxRetryCount=%u\n", ul_temp); | ||
214 | break; | ||
215 | default: | ||
216 | break; | ||
217 | } | ||
218 | } | ||
219 | |||
220 | return 0; | ||
221 | } | ||
222 | |||
223 | /* | ||
224 | * This function handles the command response of get log request | ||
225 | * | ||
226 | * Handling includes changing the header fields into CPU format | ||
227 | * and sending the received parameters to application. | ||
228 | */ | ||
229 | static int mwifiex_ret_get_log(struct mwifiex_private *priv, | ||
230 | struct host_cmd_ds_command *resp, | ||
231 | void *data_buf) | ||
232 | { | ||
233 | struct host_cmd_ds_802_11_get_log *get_log = | ||
234 | (struct host_cmd_ds_802_11_get_log *) &resp->params.get_log; | ||
235 | struct mwifiex_ds_get_stats *stats; | ||
236 | |||
237 | if (data_buf) { | ||
238 | stats = (struct mwifiex_ds_get_stats *) data_buf; | ||
239 | stats->mcast_tx_frame = le32_to_cpu(get_log->mcast_tx_frame); | ||
240 | stats->failed = le32_to_cpu(get_log->failed); | ||
241 | stats->retry = le32_to_cpu(get_log->retry); | ||
242 | stats->multi_retry = le32_to_cpu(get_log->multi_retry); | ||
243 | stats->frame_dup = le32_to_cpu(get_log->frame_dup); | ||
244 | stats->rts_success = le32_to_cpu(get_log->rts_success); | ||
245 | stats->rts_failure = le32_to_cpu(get_log->rts_failure); | ||
246 | stats->ack_failure = le32_to_cpu(get_log->ack_failure); | ||
247 | stats->rx_frag = le32_to_cpu(get_log->rx_frag); | ||
248 | stats->mcast_rx_frame = le32_to_cpu(get_log->mcast_rx_frame); | ||
249 | stats->fcs_error = le32_to_cpu(get_log->fcs_error); | ||
250 | stats->tx_frame = le32_to_cpu(get_log->tx_frame); | ||
251 | stats->wep_icv_error[0] = | ||
252 | le32_to_cpu(get_log->wep_icv_err_cnt[0]); | ||
253 | stats->wep_icv_error[1] = | ||
254 | le32_to_cpu(get_log->wep_icv_err_cnt[1]); | ||
255 | stats->wep_icv_error[2] = | ||
256 | le32_to_cpu(get_log->wep_icv_err_cnt[2]); | ||
257 | stats->wep_icv_error[3] = | ||
258 | le32_to_cpu(get_log->wep_icv_err_cnt[3]); | ||
259 | } | ||
260 | |||
261 | return 0; | ||
262 | } | ||
263 | |||
264 | /* | ||
265 | * This function handles the command response of set/get Tx rate | ||
266 | * configurations. | ||
267 | * | ||
268 | * Handling includes changing the header fields into CPU format | ||
269 | * and saving the following parameters in driver - | ||
270 | * - DSSS rate bitmap | ||
271 | * - OFDM rate bitmap | ||
272 | * - HT MCS rate bitmaps | ||
273 | * | ||
274 | * Based on the new rate bitmaps, the function re-evaluates if | ||
275 | * auto data rate has been activated. If not, it sends another | ||
276 | * query to the firmware to get the current Tx data rate and updates | ||
277 | * the driver value. | ||
278 | */ | ||
279 | static int mwifiex_ret_tx_rate_cfg(struct mwifiex_private *priv, | ||
280 | struct host_cmd_ds_command *resp, | ||
281 | void *data_buf) | ||
282 | { | ||
283 | struct mwifiex_rate_cfg *ds_rate; | ||
284 | struct host_cmd_ds_tx_rate_cfg *rate_cfg = &resp->params.tx_rate_cfg; | ||
285 | struct mwifiex_rate_scope *rate_scope; | ||
286 | struct mwifiex_ie_types_header *head; | ||
287 | u16 tlv, tlv_buf_len; | ||
288 | u8 *tlv_buf; | ||
289 | u32 i; | ||
290 | int ret = 0; | ||
291 | |||
292 | tlv_buf = (u8 *) ((u8 *) rate_cfg) + | ||
293 | sizeof(struct host_cmd_ds_tx_rate_cfg); | ||
294 | tlv_buf_len = *(u16 *) (tlv_buf + sizeof(u16)); | ||
295 | |||
296 | while (tlv_buf && tlv_buf_len > 0) { | ||
297 | tlv = (*tlv_buf); | ||
298 | tlv = tlv | (*(tlv_buf + 1) << 8); | ||
299 | |||
300 | switch (tlv) { | ||
301 | case TLV_TYPE_RATE_SCOPE: | ||
302 | rate_scope = (struct mwifiex_rate_scope *) tlv_buf; | ||
303 | priv->bitmap_rates[0] = | ||
304 | le16_to_cpu(rate_scope->hr_dsss_rate_bitmap); | ||
305 | priv->bitmap_rates[1] = | ||
306 | le16_to_cpu(rate_scope->ofdm_rate_bitmap); | ||
307 | for (i = 0; | ||
308 | i < | ||
309 | sizeof(rate_scope->ht_mcs_rate_bitmap) / | ||
310 | sizeof(u16); i++) | ||
311 | priv->bitmap_rates[2 + i] = | ||
312 | le16_to_cpu(rate_scope-> | ||
313 | ht_mcs_rate_bitmap[i]); | ||
314 | break; | ||
315 | /* Add RATE_DROP tlv here */ | ||
316 | } | ||
317 | |||
318 | head = (struct mwifiex_ie_types_header *) tlv_buf; | ||
319 | tlv_buf += le16_to_cpu(head->len) + sizeof(*head); | ||
320 | tlv_buf_len -= le16_to_cpu(head->len); | ||
321 | } | ||
322 | |||
323 | priv->is_data_rate_auto = mwifiex_is_rate_auto(priv); | ||
324 | |||
325 | if (priv->is_data_rate_auto) | ||
326 | priv->data_rate = 0; | ||
327 | else | ||
328 | ret = mwifiex_send_cmd_async(priv, | ||
329 | HostCmd_CMD_802_11_TX_RATE_QUERY, | ||
330 | HostCmd_ACT_GEN_GET, 0, NULL); | ||
331 | |||
332 | if (data_buf) { | ||
333 | ds_rate = (struct mwifiex_rate_cfg *) data_buf; | ||
334 | if (le16_to_cpu(rate_cfg->action) == HostCmd_ACT_GEN_GET) { | ||
335 | if (priv->is_data_rate_auto) { | ||
336 | ds_rate->is_rate_auto = 1; | ||
337 | } else { | ||
338 | ds_rate->rate = mwifiex_get_rate_index(priv-> | ||
339 | bitmap_rates, | ||
340 | sizeof(priv-> | ||
341 | bitmap_rates)); | ||
342 | if (ds_rate->rate >= | ||
343 | MWIFIEX_RATE_BITMAP_OFDM0 | ||
344 | && ds_rate->rate <= | ||
345 | MWIFIEX_RATE_BITMAP_OFDM7) | ||
346 | ds_rate->rate -= | ||
347 | (MWIFIEX_RATE_BITMAP_OFDM0 - | ||
348 | MWIFIEX_RATE_INDEX_OFDM0); | ||
349 | if (ds_rate->rate >= | ||
350 | MWIFIEX_RATE_BITMAP_MCS0 | ||
351 | && ds_rate->rate <= | ||
352 | MWIFIEX_RATE_BITMAP_MCS127) | ||
353 | ds_rate->rate -= | ||
354 | (MWIFIEX_RATE_BITMAP_MCS0 - | ||
355 | MWIFIEX_RATE_INDEX_MCS0); | ||
356 | } | ||
357 | } | ||
358 | } | ||
359 | |||
360 | return ret; | ||
361 | } | ||
362 | |||
363 | /* | ||
364 | * This function handles the command response of get Tx power level. | ||
365 | * | ||
366 | * Handling includes saving the maximum and minimum Tx power levels | ||
367 | * in driver, as well as sending the values to user. | ||
368 | */ | ||
369 | static int mwifiex_get_power_level(struct mwifiex_private *priv, void *data_buf) | ||
370 | { | ||
371 | int length, max_power = -1, min_power = -1; | ||
372 | struct mwifiex_types_power_group *pg_tlv_hdr; | ||
373 | struct mwifiex_power_group *pg; | ||
374 | |||
375 | if (data_buf) { | ||
376 | pg_tlv_hdr = | ||
377 | (struct mwifiex_types_power_group *) ((u8 *) data_buf | ||
378 | + sizeof(struct host_cmd_ds_txpwr_cfg)); | ||
379 | pg = (struct mwifiex_power_group *) ((u8 *) pg_tlv_hdr + | ||
380 | sizeof(struct mwifiex_types_power_group)); | ||
381 | length = pg_tlv_hdr->length; | ||
382 | if (length > 0) { | ||
383 | max_power = pg->power_max; | ||
384 | min_power = pg->power_min; | ||
385 | length -= sizeof(struct mwifiex_power_group); | ||
386 | } | ||
387 | while (length) { | ||
388 | pg++; | ||
389 | if (max_power < pg->power_max) | ||
390 | max_power = pg->power_max; | ||
391 | |||
392 | if (min_power > pg->power_min) | ||
393 | min_power = pg->power_min; | ||
394 | |||
395 | length -= sizeof(struct mwifiex_power_group); | ||
396 | } | ||
397 | if (pg_tlv_hdr->length > 0) { | ||
398 | priv->min_tx_power_level = (u8) min_power; | ||
399 | priv->max_tx_power_level = (u8) max_power; | ||
400 | } | ||
401 | } else { | ||
402 | return -1; | ||
403 | } | ||
404 | |||
405 | return 0; | ||
406 | } | ||
407 | |||
408 | /* | ||
409 | * This function handles the command response of set/get Tx power | ||
410 | * configurations. | ||
411 | * | ||
412 | * Handling includes changing the header fields into CPU format | ||
413 | * and saving the current Tx power level in driver. | ||
414 | */ | ||
415 | static int mwifiex_ret_tx_power_cfg(struct mwifiex_private *priv, | ||
416 | struct host_cmd_ds_command *resp, | ||
417 | void *data_buf) | ||
418 | { | ||
419 | struct mwifiex_adapter *adapter = priv->adapter; | ||
420 | struct host_cmd_ds_txpwr_cfg *txp_cfg = &resp->params.txp_cfg; | ||
421 | struct mwifiex_types_power_group *pg_tlv_hdr; | ||
422 | struct mwifiex_power_group *pg; | ||
423 | u16 action = le16_to_cpu(txp_cfg->action); | ||
424 | |||
425 | switch (action) { | ||
426 | case HostCmd_ACT_GEN_GET: | ||
427 | { | ||
428 | pg_tlv_hdr = | ||
429 | (struct mwifiex_types_power_group *) ((u8 *) | ||
430 | txp_cfg + | ||
431 | sizeof | ||
432 | (struct | ||
433 | host_cmd_ds_txpwr_cfg)); | ||
434 | pg = (struct mwifiex_power_group *) ((u8 *) | ||
435 | pg_tlv_hdr + | ||
436 | sizeof(struct | ||
437 | mwifiex_types_power_group)); | ||
438 | if (adapter->hw_status == | ||
439 | MWIFIEX_HW_STATUS_INITIALIZING) | ||
440 | mwifiex_get_power_level(priv, txp_cfg); | ||
441 | priv->tx_power_level = (u16) pg->power_min; | ||
442 | break; | ||
443 | } | ||
444 | case HostCmd_ACT_GEN_SET: | ||
445 | if (le32_to_cpu(txp_cfg->mode)) { | ||
446 | pg_tlv_hdr = | ||
447 | (struct mwifiex_types_power_group *) ((u8 *) | ||
448 | txp_cfg + | ||
449 | sizeof | ||
450 | (struct | ||
451 | host_cmd_ds_txpwr_cfg)); | ||
452 | pg = (struct mwifiex_power_group *) ((u8 *) pg_tlv_hdr | ||
453 | + | ||
454 | sizeof(struct | ||
455 | mwifiex_types_power_group)); | ||
456 | if (pg->power_max == pg->power_min) | ||
457 | priv->tx_power_level = (u16) pg->power_min; | ||
458 | } | ||
459 | break; | ||
460 | default: | ||
461 | dev_err(adapter->dev, "CMD_RESP: unknown cmd action %d\n", | ||
462 | action); | ||
463 | return 0; | ||
464 | } | ||
465 | dev_dbg(adapter->dev, | ||
466 | "info: Current TxPower Level = %d, Max Power=%d, Min Power=%d\n", | ||
467 | priv->tx_power_level, priv->max_tx_power_level, | ||
468 | priv->min_tx_power_level); | ||
469 | |||
470 | return 0; | ||
471 | } | ||
472 | |||
473 | /* | ||
474 | * This function handles the command response of set/get MAC address. | ||
475 | * | ||
476 | * Handling includes saving the MAC address in driver. | ||
477 | */ | ||
478 | static int mwifiex_ret_802_11_mac_address(struct mwifiex_private *priv, | ||
479 | struct host_cmd_ds_command *resp) | ||
480 | { | ||
481 | struct host_cmd_ds_802_11_mac_address *cmd_mac_addr = | ||
482 | &resp->params.mac_addr; | ||
483 | |||
484 | memcpy(priv->curr_addr, cmd_mac_addr->mac_addr, ETH_ALEN); | ||
485 | |||
486 | dev_dbg(priv->adapter->dev, | ||
487 | "info: set mac address: %pM\n", priv->curr_addr); | ||
488 | |||
489 | return 0; | ||
490 | } | ||
491 | |||
492 | /* | ||
493 | * This function handles the command response of set/get MAC multicast | ||
494 | * address. | ||
495 | */ | ||
496 | static int mwifiex_ret_mac_multicast_adr(struct mwifiex_private *priv, | ||
497 | struct host_cmd_ds_command *resp) | ||
498 | { | ||
499 | return 0; | ||
500 | } | ||
501 | |||
502 | /* | ||
503 | * This function handles the command response of get Tx rate query. | ||
504 | * | ||
505 | * Handling includes changing the header fields into CPU format | ||
506 | * and saving the Tx rate and HT information parameters in driver. | ||
507 | * | ||
508 | * Both rate configuration and current data rate can be retrieved | ||
509 | * with this request. | ||
510 | */ | ||
511 | static int mwifiex_ret_802_11_tx_rate_query(struct mwifiex_private *priv, | ||
512 | struct host_cmd_ds_command *resp) | ||
513 | { | ||
514 | priv->tx_rate = resp->params.tx_rate.tx_rate; | ||
515 | priv->tx_htinfo = resp->params.tx_rate.ht_info; | ||
516 | if (!priv->is_data_rate_auto) | ||
517 | priv->data_rate = | ||
518 | mwifiex_index_to_data_rate(priv->tx_rate, | ||
519 | priv->tx_htinfo); | ||
520 | |||
521 | return 0; | ||
522 | } | ||
523 | |||
524 | /* | ||
525 | * This function handles the command response of a deauthenticate | ||
526 | * command. | ||
527 | * | ||
528 | * If the deauthenticated MAC matches the current BSS MAC, the connection | ||
529 | * state is reset. | ||
530 | */ | ||
531 | static int mwifiex_ret_802_11_deauthenticate(struct mwifiex_private *priv, | ||
532 | struct host_cmd_ds_command *resp) | ||
533 | { | ||
534 | struct mwifiex_adapter *adapter = priv->adapter; | ||
535 | |||
536 | adapter->dbg.num_cmd_deauth++; | ||
537 | if (!memcmp(resp->params.deauth.mac_addr, | ||
538 | &priv->curr_bss_params.bss_descriptor.mac_address, | ||
539 | sizeof(resp->params.deauth.mac_addr))) | ||
540 | mwifiex_reset_connect_state(priv); | ||
541 | |||
542 | return 0; | ||
543 | } | ||
544 | |||
545 | /* | ||
546 | * This function handles the command response of ad-hoc stop. | ||
547 | * | ||
548 | * The function resets the connection state in driver. | ||
549 | */ | ||
550 | static int mwifiex_ret_802_11_ad_hoc_stop(struct mwifiex_private *priv, | ||
551 | struct host_cmd_ds_command *resp) | ||
552 | { | ||
553 | mwifiex_reset_connect_state(priv); | ||
554 | return 0; | ||
555 | } | ||
556 | |||
557 | /* | ||
558 | * This function handles the command response of set/get key material. | ||
559 | * | ||
560 | * Handling includes updating the driver parameters to reflect the | ||
561 | * changes. | ||
562 | */ | ||
563 | static int mwifiex_ret_802_11_key_material(struct mwifiex_private *priv, | ||
564 | struct host_cmd_ds_command *resp) | ||
565 | { | ||
566 | struct host_cmd_ds_802_11_key_material *key = | ||
567 | &resp->params.key_material; | ||
568 | |||
569 | if (le16_to_cpu(key->action) == HostCmd_ACT_GEN_SET) { | ||
570 | if ((le16_to_cpu(key->key_param_set.key_info) & KEY_MCAST)) { | ||
571 | dev_dbg(priv->adapter->dev, "info: key: GTK is set\n"); | ||
572 | priv->wpa_is_gtk_set = true; | ||
573 | priv->scan_block = false; | ||
574 | } | ||
575 | } | ||
576 | |||
577 | memset(priv->aes_key.key_param_set.key, 0, | ||
578 | sizeof(key->key_param_set.key)); | ||
579 | priv->aes_key.key_param_set.key_len = key->key_param_set.key_len; | ||
580 | memcpy(priv->aes_key.key_param_set.key, key->key_param_set.key, | ||
581 | le16_to_cpu(priv->aes_key.key_param_set.key_len)); | ||
582 | |||
583 | return 0; | ||
584 | } | ||
585 | |||
586 | /* | ||
587 | * This function handles the command response of get 11d domain information. | ||
588 | */ | ||
589 | static int mwifiex_ret_802_11d_domain_info(struct mwifiex_private *priv, | ||
590 | struct host_cmd_ds_command *resp) | ||
591 | { | ||
592 | struct host_cmd_ds_802_11d_domain_info_rsp *domain_info = | ||
593 | &resp->params.domain_info_resp; | ||
594 | struct mwifiex_ietypes_domain_param_set *domain = &domain_info->domain; | ||
595 | u16 action = le16_to_cpu(domain_info->action); | ||
596 | u8 no_of_triplet; | ||
597 | |||
598 | no_of_triplet = (u8) ((le16_to_cpu(domain->header.len) - | ||
599 | IEEE80211_COUNTRY_STRING_LEN) / | ||
600 | sizeof(struct ieee80211_country_ie_triplet)); | ||
601 | |||
602 | dev_dbg(priv->adapter->dev, "info: 11D Domain Info Resp:" | ||
603 | " no_of_triplet=%d\n", no_of_triplet); | ||
604 | |||
605 | if (no_of_triplet > MWIFIEX_MAX_TRIPLET_802_11D) { | ||
606 | dev_warn(priv->adapter->dev, | ||
607 | "11D: invalid number of triplets %d " | ||
608 | "returned!!\n", no_of_triplet); | ||
609 | return -1; | ||
610 | } | ||
611 | |||
612 | switch (action) { | ||
613 | case HostCmd_ACT_GEN_SET: /* Proc Set Action */ | ||
614 | break; | ||
615 | case HostCmd_ACT_GEN_GET: | ||
616 | break; | ||
617 | default: | ||
618 | dev_err(priv->adapter->dev, | ||
619 | "11D: invalid action:%d\n", domain_info->action); | ||
620 | return -1; | ||
621 | } | ||
622 | |||
623 | return 0; | ||
624 | } | ||
625 | |||
626 | /* | ||
627 | * This function handles the command response of get RF channel. | ||
628 | * | ||
629 | * Handling includes changing the header fields into CPU format | ||
630 | * and saving the new channel in driver. | ||
631 | */ | ||
632 | static int mwifiex_ret_802_11_rf_channel(struct mwifiex_private *priv, | ||
633 | struct host_cmd_ds_command *resp, | ||
634 | void *data_buf) | ||
635 | { | ||
636 | struct host_cmd_ds_802_11_rf_channel *rf_channel = | ||
637 | &resp->params.rf_channel; | ||
638 | u16 new_channel = le16_to_cpu(rf_channel->current_channel); | ||
639 | |||
640 | if (priv->curr_bss_params.bss_descriptor.channel != new_channel) { | ||
641 | dev_dbg(priv->adapter->dev, "cmd: Channel Switch: %d to %d\n", | ||
642 | priv->curr_bss_params.bss_descriptor.channel, | ||
643 | new_channel); | ||
644 | /* Update the channel again */ | ||
645 | priv->curr_bss_params.bss_descriptor.channel = new_channel; | ||
646 | } | ||
647 | if (data_buf) | ||
648 | *((u16 *)data_buf) = new_channel; | ||
649 | |||
650 | return 0; | ||
651 | } | ||
652 | |||
653 | /* | ||
654 | * This function handles the command response of get extended version. | ||
655 | * | ||
656 | * Handling includes forming the extended version string and sending it | ||
657 | * to application. | ||
658 | */ | ||
659 | static int mwifiex_ret_ver_ext(struct mwifiex_private *priv, | ||
660 | struct host_cmd_ds_command *resp, | ||
661 | void *data_buf) | ||
662 | { | ||
663 | struct host_cmd_ds_version_ext *ver_ext = &resp->params.verext; | ||
664 | struct host_cmd_ds_version_ext *version_ext; | ||
665 | |||
666 | if (data_buf) { | ||
667 | version_ext = (struct host_cmd_ds_version_ext *)data_buf; | ||
668 | version_ext->version_str_sel = ver_ext->version_str_sel; | ||
669 | memcpy(version_ext->version_str, ver_ext->version_str, | ||
670 | sizeof(char) * 128); | ||
671 | memcpy(priv->version_str, ver_ext->version_str, 128); | ||
672 | } | ||
673 | return 0; | ||
674 | } | ||
675 | |||
676 | /* | ||
677 | * This function handles the command response of register access. | ||
678 | * | ||
679 | * The register value and offset are returned to the user. For EEPROM | ||
680 | * access, the byte count is also returned. | ||
681 | */ | ||
682 | static int mwifiex_ret_reg_access(u16 type, struct host_cmd_ds_command *resp, | ||
683 | void *data_buf) | ||
684 | { | ||
685 | struct mwifiex_ds_reg_rw *reg_rw; | ||
686 | struct mwifiex_ds_read_eeprom *eeprom; | ||
687 | |||
688 | if (data_buf) { | ||
689 | reg_rw = (struct mwifiex_ds_reg_rw *) data_buf; | ||
690 | eeprom = (struct mwifiex_ds_read_eeprom *) data_buf; | ||
691 | switch (type) { | ||
692 | case HostCmd_CMD_MAC_REG_ACCESS: | ||
693 | { | ||
694 | struct host_cmd_ds_mac_reg_access *reg; | ||
695 | reg = (struct host_cmd_ds_mac_reg_access *) | ||
696 | &resp->params.mac_reg; | ||
697 | reg_rw->offset = cpu_to_le32( | ||
698 | (u32) le16_to_cpu(reg->offset)); | ||
699 | reg_rw->value = reg->value; | ||
700 | break; | ||
701 | } | ||
702 | case HostCmd_CMD_BBP_REG_ACCESS: | ||
703 | { | ||
704 | struct host_cmd_ds_bbp_reg_access *reg; | ||
705 | reg = (struct host_cmd_ds_bbp_reg_access *) | ||
706 | &resp->params.bbp_reg; | ||
707 | reg_rw->offset = cpu_to_le32( | ||
708 | (u32) le16_to_cpu(reg->offset)); | ||
709 | reg_rw->value = cpu_to_le32((u32) reg->value); | ||
710 | break; | ||
711 | } | ||
712 | |||
713 | case HostCmd_CMD_RF_REG_ACCESS: | ||
714 | { | ||
715 | struct host_cmd_ds_rf_reg_access *reg; | ||
716 | reg = (struct host_cmd_ds_rf_reg_access *) | ||
717 | &resp->params.rf_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 | case HostCmd_CMD_PMIC_REG_ACCESS: | ||
724 | { | ||
725 | struct host_cmd_ds_pmic_reg_access *reg; | ||
726 | reg = (struct host_cmd_ds_pmic_reg_access *) | ||
727 | &resp->params.pmic_reg; | ||
728 | reg_rw->offset = cpu_to_le32( | ||
729 | (u32) le16_to_cpu(reg->offset)); | ||
730 | reg_rw->value = cpu_to_le32((u32) reg->value); | ||
731 | break; | ||
732 | } | ||
733 | case HostCmd_CMD_CAU_REG_ACCESS: | ||
734 | { | ||
735 | struct host_cmd_ds_rf_reg_access *reg; | ||
736 | reg = (struct host_cmd_ds_rf_reg_access *) | ||
737 | &resp->params.rf_reg; | ||
738 | reg_rw->offset = cpu_to_le32( | ||
739 | (u32) le16_to_cpu(reg->offset)); | ||
740 | reg_rw->value = cpu_to_le32((u32) reg->value); | ||
741 | break; | ||
742 | } | ||
743 | case HostCmd_CMD_802_11_EEPROM_ACCESS: | ||
744 | { | ||
745 | struct host_cmd_ds_802_11_eeprom_access | ||
746 | *cmd_eeprom = | ||
747 | (struct host_cmd_ds_802_11_eeprom_access | ||
748 | *) &resp->params.eeprom; | ||
749 | pr_debug("info: EEPROM read len=%x\n", | ||
750 | cmd_eeprom->byte_count); | ||
751 | if (le16_to_cpu(eeprom->byte_count) < | ||
752 | le16_to_cpu( | ||
753 | cmd_eeprom->byte_count)) { | ||
754 | eeprom->byte_count = cpu_to_le16(0); | ||
755 | pr_debug("info: EEPROM read " | ||
756 | "length is too big\n"); | ||
757 | return -1; | ||
758 | } | ||
759 | eeprom->offset = cmd_eeprom->offset; | ||
760 | eeprom->byte_count = cmd_eeprom->byte_count; | ||
761 | if (le16_to_cpu(eeprom->byte_count) > 0) | ||
762 | memcpy(&eeprom->value, | ||
763 | &cmd_eeprom->value, | ||
764 | le16_to_cpu(eeprom->byte_count)); | ||
765 | |||
766 | break; | ||
767 | } | ||
768 | default: | ||
769 | return -1; | ||
770 | } | ||
771 | } | ||
772 | return 0; | ||
773 | } | ||
774 | |||
775 | /* | ||
776 | * This function handles the command response of get IBSS coalescing status. | ||
777 | * | ||
778 | * If the received BSSID is different than the current one, the current BSSID, | ||
779 | * beacon interval, ATIM window and ERP information are updated, along with | ||
780 | * changing the ad-hoc state accordingly. | ||
781 | */ | ||
782 | static int mwifiex_ret_ibss_coalescing_status(struct mwifiex_private *priv, | ||
783 | struct host_cmd_ds_command *resp) | ||
784 | { | ||
785 | struct host_cmd_ds_802_11_ibss_status *ibss_coal_resp = | ||
786 | &(resp->params.ibss_coalescing); | ||
787 | u8 zero_mac[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 }; | ||
788 | |||
789 | if (le16_to_cpu(ibss_coal_resp->action) == HostCmd_ACT_GEN_SET) | ||
790 | return 0; | ||
791 | |||
792 | dev_dbg(priv->adapter->dev, | ||
793 | "info: new BSSID %pM\n", ibss_coal_resp->bssid); | ||
794 | |||
795 | /* If rsp has NULL BSSID, Just return..... No Action */ | ||
796 | if (!memcmp(ibss_coal_resp->bssid, zero_mac, ETH_ALEN)) { | ||
797 | dev_warn(priv->adapter->dev, "new BSSID is NULL\n"); | ||
798 | return 0; | ||
799 | } | ||
800 | |||
801 | /* If BSSID is diff, modify current BSS parameters */ | ||
802 | if (memcmp(priv->curr_bss_params.bss_descriptor.mac_address, | ||
803 | ibss_coal_resp->bssid, ETH_ALEN)) { | ||
804 | /* BSSID */ | ||
805 | memcpy(priv->curr_bss_params.bss_descriptor.mac_address, | ||
806 | ibss_coal_resp->bssid, ETH_ALEN); | ||
807 | |||
808 | /* Beacon Interval */ | ||
809 | priv->curr_bss_params.bss_descriptor.beacon_period | ||
810 | = le16_to_cpu(ibss_coal_resp->beacon_interval); | ||
811 | |||
812 | /* ERP Information */ | ||
813 | priv->curr_bss_params.bss_descriptor.erp_flags = | ||
814 | (u8) le16_to_cpu(ibss_coal_resp->use_g_rate_protect); | ||
815 | |||
816 | priv->adhoc_state = ADHOC_COALESCED; | ||
817 | } | ||
818 | |||
819 | return 0; | ||
820 | } | ||
821 | |||
822 | /* | ||
823 | * This function handles the command responses. | ||
824 | * | ||
825 | * This is a generic function, which calls command specific | ||
826 | * response handlers based on the command ID. | ||
827 | */ | ||
828 | int mwifiex_process_sta_cmdresp(struct mwifiex_private *priv, | ||
829 | u16 cmdresp_no, void *cmd_buf) | ||
830 | { | ||
831 | int ret = 0; | ||
832 | struct mwifiex_adapter *adapter = priv->adapter; | ||
833 | struct host_cmd_ds_command *resp = | ||
834 | (struct host_cmd_ds_command *) cmd_buf; | ||
835 | void *data_buf = adapter->curr_cmd->data_buf; | ||
836 | |||
837 | /* If the command is not successful, cleanup and return failure */ | ||
838 | if (resp->result != HostCmd_RESULT_OK) { | ||
839 | mwifiex_process_cmdresp_error(priv, resp); | ||
840 | return -1; | ||
841 | } | ||
842 | /* Command successful, handle response */ | ||
843 | switch (cmdresp_no) { | ||
844 | case HostCmd_CMD_GET_HW_SPEC: | ||
845 | ret = mwifiex_ret_get_hw_spec(priv, resp); | ||
846 | break; | ||
847 | case HostCmd_CMD_MAC_CONTROL: | ||
848 | break; | ||
849 | case HostCmd_CMD_802_11_MAC_ADDRESS: | ||
850 | ret = mwifiex_ret_802_11_mac_address(priv, resp); | ||
851 | break; | ||
852 | case HostCmd_CMD_MAC_MULTICAST_ADR: | ||
853 | ret = mwifiex_ret_mac_multicast_adr(priv, resp); | ||
854 | break; | ||
855 | case HostCmd_CMD_TX_RATE_CFG: | ||
856 | ret = mwifiex_ret_tx_rate_cfg(priv, resp, data_buf); | ||
857 | break; | ||
858 | case HostCmd_CMD_802_11_SCAN: | ||
859 | ret = mwifiex_ret_802_11_scan(priv, resp); | ||
860 | adapter->curr_cmd->wait_q_enabled = false; | ||
861 | break; | ||
862 | case HostCmd_CMD_802_11_BG_SCAN_QUERY: | ||
863 | ret = mwifiex_ret_802_11_scan(priv, resp); | ||
864 | dev_dbg(adapter->dev, | ||
865 | "info: CMD_RESP: BG_SCAN result is ready!\n"); | ||
866 | break; | ||
867 | case HostCmd_CMD_TXPWR_CFG: | ||
868 | ret = mwifiex_ret_tx_power_cfg(priv, resp, data_buf); | ||
869 | break; | ||
870 | case HostCmd_CMD_802_11_PS_MODE_ENH: | ||
871 | ret = mwifiex_ret_enh_power_mode(priv, resp, data_buf); | ||
872 | break; | ||
873 | case HostCmd_CMD_802_11_HS_CFG_ENH: | ||
874 | ret = mwifiex_ret_802_11_hs_cfg(priv, resp); | ||
875 | break; | ||
876 | case HostCmd_CMD_802_11_ASSOCIATE: | ||
877 | ret = mwifiex_ret_802_11_associate(priv, resp); | ||
878 | break; | ||
879 | case HostCmd_CMD_802_11_DEAUTHENTICATE: | ||
880 | ret = mwifiex_ret_802_11_deauthenticate(priv, resp); | ||
881 | break; | ||
882 | case HostCmd_CMD_802_11_AD_HOC_START: | ||
883 | case HostCmd_CMD_802_11_AD_HOC_JOIN: | ||
884 | ret = mwifiex_ret_802_11_ad_hoc(priv, resp); | ||
885 | break; | ||
886 | case HostCmd_CMD_802_11_AD_HOC_STOP: | ||
887 | ret = mwifiex_ret_802_11_ad_hoc_stop(priv, resp); | ||
888 | break; | ||
889 | case HostCmd_CMD_802_11_GET_LOG: | ||
890 | ret = mwifiex_ret_get_log(priv, resp, data_buf); | ||
891 | break; | ||
892 | case HostCmd_CMD_RSSI_INFO: | ||
893 | ret = mwifiex_ret_802_11_rssi_info(priv, resp, data_buf); | ||
894 | break; | ||
895 | case HostCmd_CMD_802_11_SNMP_MIB: | ||
896 | ret = mwifiex_ret_802_11_snmp_mib(priv, resp, data_buf); | ||
897 | break; | ||
898 | case HostCmd_CMD_802_11_TX_RATE_QUERY: | ||
899 | ret = mwifiex_ret_802_11_tx_rate_query(priv, resp); | ||
900 | break; | ||
901 | case HostCmd_CMD_802_11_RF_CHANNEL: | ||
902 | ret = mwifiex_ret_802_11_rf_channel(priv, resp, data_buf); | ||
903 | break; | ||
904 | case HostCmd_CMD_VERSION_EXT: | ||
905 | ret = mwifiex_ret_ver_ext(priv, resp, data_buf); | ||
906 | break; | ||
907 | case HostCmd_CMD_FUNC_INIT: | ||
908 | case HostCmd_CMD_FUNC_SHUTDOWN: | ||
909 | break; | ||
910 | case HostCmd_CMD_802_11_KEY_MATERIAL: | ||
911 | ret = mwifiex_ret_802_11_key_material(priv, resp); | ||
912 | break; | ||
913 | case HostCmd_CMD_802_11D_DOMAIN_INFO: | ||
914 | ret = mwifiex_ret_802_11d_domain_info(priv, resp); | ||
915 | break; | ||
916 | case HostCmd_CMD_11N_ADDBA_REQ: | ||
917 | ret = mwifiex_ret_11n_addba_req(priv, resp); | ||
918 | break; | ||
919 | case HostCmd_CMD_11N_DELBA: | ||
920 | ret = mwifiex_ret_11n_delba(priv, resp); | ||
921 | break; | ||
922 | case HostCmd_CMD_11N_ADDBA_RSP: | ||
923 | ret = mwifiex_ret_11n_addba_resp(priv, resp); | ||
924 | break; | ||
925 | case HostCmd_CMD_RECONFIGURE_TX_BUFF: | ||
926 | adapter->tx_buf_size = (u16) le16_to_cpu(resp->params. | ||
927 | tx_buf.buff_size); | ||
928 | adapter->tx_buf_size = (adapter->tx_buf_size / | ||
929 | MWIFIEX_SDIO_BLOCK_SIZE) * | ||
930 | MWIFIEX_SDIO_BLOCK_SIZE; | ||
931 | adapter->curr_tx_buf_size = adapter->tx_buf_size; | ||
932 | dev_dbg(adapter->dev, | ||
933 | "cmd: max_tx_buf_size=%d, tx_buf_size=%d\n", | ||
934 | adapter->max_tx_buf_size, adapter->tx_buf_size); | ||
935 | |||
936 | if (adapter->if_ops.update_mp_end_port) | ||
937 | adapter->if_ops.update_mp_end_port(adapter, | ||
938 | le16_to_cpu(resp-> | ||
939 | params. | ||
940 | tx_buf. | ||
941 | mp_end_port)); | ||
942 | break; | ||
943 | case HostCmd_CMD_AMSDU_AGGR_CTRL: | ||
944 | ret = mwifiex_ret_amsdu_aggr_ctrl(resp, data_buf); | ||
945 | break; | ||
946 | case HostCmd_CMD_WMM_GET_STATUS: | ||
947 | ret = mwifiex_ret_wmm_get_status(priv, resp); | ||
948 | break; | ||
949 | case HostCmd_CMD_802_11_IBSS_COALESCING_STATUS: | ||
950 | ret = mwifiex_ret_ibss_coalescing_status(priv, resp); | ||
951 | break; | ||
952 | case HostCmd_CMD_MAC_REG_ACCESS: | ||
953 | case HostCmd_CMD_BBP_REG_ACCESS: | ||
954 | case HostCmd_CMD_RF_REG_ACCESS: | ||
955 | case HostCmd_CMD_PMIC_REG_ACCESS: | ||
956 | case HostCmd_CMD_CAU_REG_ACCESS: | ||
957 | case HostCmd_CMD_802_11_EEPROM_ACCESS: | ||
958 | ret = mwifiex_ret_reg_access(cmdresp_no, resp, data_buf); | ||
959 | break; | ||
960 | case HostCmd_CMD_SET_BSS_MODE: | ||
961 | break; | ||
962 | case HostCmd_CMD_11N_CFG: | ||
963 | ret = mwifiex_ret_11n_cfg(resp, data_buf); | ||
964 | break; | ||
965 | default: | ||
966 | dev_err(adapter->dev, "CMD_RESP: unknown cmd response %#x\n", | ||
967 | resp->command); | ||
968 | break; | ||
969 | } | ||
970 | |||
971 | return ret; | ||
972 | } | ||
diff --git a/drivers/net/wireless/mwifiex/sta_event.c b/drivers/net/wireless/mwifiex/sta_event.c new file mode 100644 index 000000000000..fc265cab0907 --- /dev/null +++ b/drivers/net/wireless/mwifiex/sta_event.c | |||
@@ -0,0 +1,406 @@ | |||
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 = 0; | ||
80 | |||
81 | /* Enable auto data rate */ | ||
82 | priv->is_data_rate_auto = true; | ||
83 | priv->data_rate = 0; | ||
84 | |||
85 | if (priv->bss_mode == NL80211_IFTYPE_ADHOC) { | ||
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_send_cmd_async(priv, | ||
275 | HostCmd_CMD_802_11_HS_CFG_ENH, | ||
276 | 0, 0, NULL); | ||
277 | break; | ||
278 | |||
279 | case EVENT_MIC_ERR_UNICAST: | ||
280 | dev_dbg(adapter->dev, "event: UNICAST MIC ERROR\n"); | ||
281 | break; | ||
282 | |||
283 | case EVENT_MIC_ERR_MULTICAST: | ||
284 | dev_dbg(adapter->dev, "event: MULTICAST MIC ERROR\n"); | ||
285 | break; | ||
286 | case EVENT_MIB_CHANGED: | ||
287 | case EVENT_INIT_DONE: | ||
288 | break; | ||
289 | |||
290 | case EVENT_ADHOC_BCN_LOST: | ||
291 | dev_dbg(adapter->dev, "event: ADHOC_BCN_LOST\n"); | ||
292 | priv->adhoc_is_link_sensed = false; | ||
293 | mwifiex_clean_txrx(priv); | ||
294 | if (!netif_queue_stopped(priv->netdev)) | ||
295 | netif_stop_queue(priv->netdev); | ||
296 | if (netif_carrier_ok(priv->netdev)) | ||
297 | netif_carrier_off(priv->netdev); | ||
298 | break; | ||
299 | |||
300 | case EVENT_BG_SCAN_REPORT: | ||
301 | dev_dbg(adapter->dev, "event: BGS_REPORT\n"); | ||
302 | /* Clear the previous scan result */ | ||
303 | memset(adapter->scan_table, 0x00, | ||
304 | sizeof(struct mwifiex_bssdescriptor) * IW_MAX_AP); | ||
305 | adapter->num_in_scan_table = 0; | ||
306 | adapter->bcn_buf_end = adapter->bcn_buf; | ||
307 | ret = mwifiex_send_cmd_async(priv, | ||
308 | HostCmd_CMD_802_11_BG_SCAN_QUERY, | ||
309 | HostCmd_ACT_GEN_GET, 0, NULL); | ||
310 | break; | ||
311 | |||
312 | case EVENT_PORT_RELEASE: | ||
313 | dev_dbg(adapter->dev, "event: PORT RELEASE\n"); | ||
314 | break; | ||
315 | |||
316 | case EVENT_WMM_STATUS_CHANGE: | ||
317 | dev_dbg(adapter->dev, "event: WMM status changed\n"); | ||
318 | ret = mwifiex_send_cmd_async(priv, HostCmd_CMD_WMM_GET_STATUS, | ||
319 | 0, 0, NULL); | ||
320 | break; | ||
321 | |||
322 | case EVENT_RSSI_LOW: | ||
323 | dev_dbg(adapter->dev, "event: Beacon RSSI_LOW\n"); | ||
324 | break; | ||
325 | case EVENT_SNR_LOW: | ||
326 | dev_dbg(adapter->dev, "event: Beacon SNR_LOW\n"); | ||
327 | break; | ||
328 | case EVENT_MAX_FAIL: | ||
329 | dev_dbg(adapter->dev, "event: MAX_FAIL\n"); | ||
330 | break; | ||
331 | case EVENT_RSSI_HIGH: | ||
332 | dev_dbg(adapter->dev, "event: Beacon RSSI_HIGH\n"); | ||
333 | break; | ||
334 | case EVENT_SNR_HIGH: | ||
335 | dev_dbg(adapter->dev, "event: Beacon SNR_HIGH\n"); | ||
336 | break; | ||
337 | case EVENT_DATA_RSSI_LOW: | ||
338 | dev_dbg(adapter->dev, "event: Data RSSI_LOW\n"); | ||
339 | break; | ||
340 | case EVENT_DATA_SNR_LOW: | ||
341 | dev_dbg(adapter->dev, "event: Data SNR_LOW\n"); | ||
342 | break; | ||
343 | case EVENT_DATA_RSSI_HIGH: | ||
344 | dev_dbg(adapter->dev, "event: Data RSSI_HIGH\n"); | ||
345 | break; | ||
346 | case EVENT_DATA_SNR_HIGH: | ||
347 | dev_dbg(adapter->dev, "event: Data SNR_HIGH\n"); | ||
348 | break; | ||
349 | case EVENT_LINK_QUALITY: | ||
350 | dev_dbg(adapter->dev, "event: Link Quality\n"); | ||
351 | break; | ||
352 | case EVENT_PRE_BEACON_LOST: | ||
353 | dev_dbg(adapter->dev, "event: Pre-Beacon Lost\n"); | ||
354 | break; | ||
355 | case EVENT_IBSS_COALESCED: | ||
356 | dev_dbg(adapter->dev, "event: IBSS_COALESCED\n"); | ||
357 | ret = mwifiex_send_cmd_async(priv, | ||
358 | HostCmd_CMD_802_11_IBSS_COALESCING_STATUS, | ||
359 | HostCmd_ACT_GEN_GET, 0, NULL); | ||
360 | break; | ||
361 | case EVENT_ADDBA: | ||
362 | dev_dbg(adapter->dev, "event: ADDBA Request\n"); | ||
363 | mwifiex_send_cmd_async(priv, HostCmd_CMD_11N_ADDBA_RSP, | ||
364 | HostCmd_ACT_GEN_SET, 0, | ||
365 | adapter->event_body); | ||
366 | break; | ||
367 | case EVENT_DELBA: | ||
368 | dev_dbg(adapter->dev, "event: DELBA Request\n"); | ||
369 | mwifiex_11n_delete_ba_stream(priv, adapter->event_body); | ||
370 | break; | ||
371 | case EVENT_BA_STREAM_TIEMOUT: | ||
372 | dev_dbg(adapter->dev, "event: BA Stream timeout\n"); | ||
373 | mwifiex_11n_ba_stream_timeout(priv, | ||
374 | (struct host_cmd_ds_11n_batimeout | ||
375 | *) | ||
376 | adapter->event_body); | ||
377 | break; | ||
378 | case EVENT_AMSDU_AGGR_CTRL: | ||
379 | dev_dbg(adapter->dev, "event: AMSDU_AGGR_CTRL %d\n", | ||
380 | *(u16 *) adapter->event_body); | ||
381 | adapter->tx_buf_size = | ||
382 | min(adapter->curr_tx_buf_size, | ||
383 | le16_to_cpu(*(__le16 *) adapter->event_body)); | ||
384 | dev_dbg(adapter->dev, "event: tx_buf_size %d\n", | ||
385 | adapter->tx_buf_size); | ||
386 | break; | ||
387 | |||
388 | case EVENT_WEP_ICV_ERR: | ||
389 | dev_dbg(adapter->dev, "event: WEP ICV error\n"); | ||
390 | break; | ||
391 | |||
392 | case EVENT_BW_CHANGE: | ||
393 | dev_dbg(adapter->dev, "event: BW Change\n"); | ||
394 | break; | ||
395 | |||
396 | case EVENT_HOSTWAKE_STAIE: | ||
397 | dev_dbg(adapter->dev, "event: HOSTWAKE_STAIE %d\n", eventcause); | ||
398 | break; | ||
399 | default: | ||
400 | dev_dbg(adapter->dev, "event: unknown event id: %#x\n", | ||
401 | eventcause); | ||
402 | break; | ||
403 | } | ||
404 | |||
405 | return ret; | ||
406 | } | ||
diff --git a/drivers/net/wireless/mwifiex/sta_ioctl.c b/drivers/net/wireless/mwifiex/sta_ioctl.c new file mode 100644 index 000000000000..d05907d05039 --- /dev/null +++ b/drivers/net/wireless/mwifiex/sta_ioctl.c | |||
@@ -0,0 +1,1593 @@ | |||
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 | int mwifiex_copy_mcast_addr(struct mwifiex_multicast_list *mlist, | ||
37 | struct net_device *dev) | ||
38 | { | ||
39 | int i = 0; | ||
40 | struct netdev_hw_addr *ha; | ||
41 | |||
42 | netdev_for_each_mc_addr(ha, dev) | ||
43 | memcpy(&mlist->mac_list[i++], ha->addr, ETH_ALEN); | ||
44 | |||
45 | return i; | ||
46 | } | ||
47 | |||
48 | /* | ||
49 | * Wait queue completion handler. | ||
50 | * | ||
51 | * This function waits on a cmd wait queue. It also cancels the pending | ||
52 | * request after waking up, in case of errors. | ||
53 | */ | ||
54 | int mwifiex_wait_queue_complete(struct mwifiex_adapter *adapter) | ||
55 | { | ||
56 | bool cancel_flag = false; | ||
57 | int status = adapter->cmd_wait_q.status; | ||
58 | |||
59 | dev_dbg(adapter->dev, "cmd pending\n"); | ||
60 | atomic_inc(&adapter->cmd_pending); | ||
61 | |||
62 | /* Status pending, wake up main process */ | ||
63 | queue_work(adapter->workqueue, &adapter->main_work); | ||
64 | |||
65 | /* Wait for completion */ | ||
66 | wait_event_interruptible(adapter->cmd_wait_q.wait, | ||
67 | adapter->cmd_wait_q.condition); | ||
68 | if (!adapter->cmd_wait_q.condition) | ||
69 | cancel_flag = true; | ||
70 | |||
71 | if (cancel_flag) { | ||
72 | mwifiex_cancel_pending_ioctl(adapter); | ||
73 | dev_dbg(adapter->dev, "cmd cancel\n"); | ||
74 | } | ||
75 | adapter->cmd_wait_q.status = 0; | ||
76 | |||
77 | return status; | ||
78 | } | ||
79 | |||
80 | /* | ||
81 | * This function prepares the correct firmware command and | ||
82 | * issues it to set the multicast list. | ||
83 | * | ||
84 | * This function can be used to enable promiscuous mode, or enable all | ||
85 | * multicast packets, or to enable selective multicast. | ||
86 | */ | ||
87 | int mwifiex_request_set_multicast_list(struct mwifiex_private *priv, | ||
88 | struct mwifiex_multicast_list *mcast_list) | ||
89 | { | ||
90 | int ret = 0; | ||
91 | u16 old_pkt_filter; | ||
92 | |||
93 | old_pkt_filter = priv->curr_pkt_filter; | ||
94 | |||
95 | if (mcast_list->mode == MWIFIEX_PROMISC_MODE) { | ||
96 | dev_dbg(priv->adapter->dev, "info: Enable Promiscuous mode\n"); | ||
97 | priv->curr_pkt_filter |= HostCmd_ACT_MAC_PROMISCUOUS_ENABLE; | ||
98 | priv->curr_pkt_filter &= | ||
99 | ~HostCmd_ACT_MAC_ALL_MULTICAST_ENABLE; | ||
100 | } else { | ||
101 | /* Multicast */ | ||
102 | priv->curr_pkt_filter &= ~HostCmd_ACT_MAC_PROMISCUOUS_ENABLE; | ||
103 | if (mcast_list->mode == MWIFIEX_MULTICAST_MODE) { | ||
104 | dev_dbg(priv->adapter->dev, | ||
105 | "info: Enabling All Multicast!\n"); | ||
106 | priv->curr_pkt_filter |= | ||
107 | HostCmd_ACT_MAC_ALL_MULTICAST_ENABLE; | ||
108 | } else { | ||
109 | priv->curr_pkt_filter &= | ||
110 | ~HostCmd_ACT_MAC_ALL_MULTICAST_ENABLE; | ||
111 | if (mcast_list->num_multicast_addr) { | ||
112 | dev_dbg(priv->adapter->dev, | ||
113 | "info: Set multicast list=%d\n", | ||
114 | mcast_list->num_multicast_addr); | ||
115 | /* Set multicast addresses to firmware */ | ||
116 | if (old_pkt_filter == priv->curr_pkt_filter) { | ||
117 | /* Send request to firmware */ | ||
118 | ret = mwifiex_send_cmd_async(priv, | ||
119 | HostCmd_CMD_MAC_MULTICAST_ADR, | ||
120 | HostCmd_ACT_GEN_SET, 0, | ||
121 | mcast_list); | ||
122 | } else { | ||
123 | /* Send request to firmware */ | ||
124 | ret = mwifiex_send_cmd_async(priv, | ||
125 | HostCmd_CMD_MAC_MULTICAST_ADR, | ||
126 | HostCmd_ACT_GEN_SET, 0, | ||
127 | mcast_list); | ||
128 | } | ||
129 | } | ||
130 | } | ||
131 | } | ||
132 | dev_dbg(priv->adapter->dev, | ||
133 | "info: old_pkt_filter=%#x, curr_pkt_filter=%#x\n", | ||
134 | old_pkt_filter, priv->curr_pkt_filter); | ||
135 | if (old_pkt_filter != priv->curr_pkt_filter) { | ||
136 | ret = mwifiex_send_cmd_async(priv, HostCmd_CMD_MAC_CONTROL, | ||
137 | HostCmd_ACT_GEN_SET, | ||
138 | 0, &priv->curr_pkt_filter); | ||
139 | } | ||
140 | |||
141 | return ret; | ||
142 | } | ||
143 | |||
144 | /* | ||
145 | * In Ad-Hoc mode, the IBSS is created if not found in scan list. | ||
146 | * In both Ad-Hoc and infra mode, an deauthentication is performed | ||
147 | * first. | ||
148 | */ | ||
149 | int mwifiex_bss_start(struct mwifiex_private *priv, | ||
150 | struct mwifiex_ssid_bssid *ssid_bssid) | ||
151 | { | ||
152 | int ret; | ||
153 | struct mwifiex_adapter *adapter = priv->adapter; | ||
154 | s32 i = -1; | ||
155 | |||
156 | priv->scan_block = false; | ||
157 | if (!ssid_bssid) | ||
158 | return -1; | ||
159 | |||
160 | if (priv->bss_mode == NL80211_IFTYPE_STATION) { | ||
161 | /* Infra mode */ | ||
162 | ret = mwifiex_deauthenticate(priv, NULL); | ||
163 | if (ret) | ||
164 | return ret; | ||
165 | |||
166 | /* Search for the requested SSID in the scan table */ | ||
167 | if (ssid_bssid->ssid.ssid_len) | ||
168 | i = mwifiex_find_ssid_in_list(priv, &ssid_bssid->ssid, | ||
169 | NULL, NL80211_IFTYPE_STATION); | ||
170 | else | ||
171 | i = mwifiex_find_bssid_in_list(priv, | ||
172 | (u8 *) &ssid_bssid->bssid, | ||
173 | NL80211_IFTYPE_STATION); | ||
174 | if (i < 0) | ||
175 | return -1; | ||
176 | |||
177 | dev_dbg(adapter->dev, | ||
178 | "info: SSID found in scan list ... associating...\n"); | ||
179 | |||
180 | /* Clear any past association response stored for | ||
181 | * application retrieval */ | ||
182 | priv->assoc_rsp_size = 0; | ||
183 | ret = mwifiex_associate(priv, &adapter->scan_table[i]); | ||
184 | if (ret) | ||
185 | return ret; | ||
186 | } else { | ||
187 | /* Adhoc mode */ | ||
188 | /* If the requested SSID matches current SSID, return */ | ||
189 | if (ssid_bssid->ssid.ssid_len && | ||
190 | (!mwifiex_ssid_cmp | ||
191 | (&priv->curr_bss_params.bss_descriptor.ssid, | ||
192 | &ssid_bssid->ssid))) | ||
193 | return 0; | ||
194 | |||
195 | /* Exit Adhoc mode first */ | ||
196 | dev_dbg(adapter->dev, "info: Sending Adhoc Stop\n"); | ||
197 | ret = mwifiex_deauthenticate(priv, NULL); | ||
198 | if (ret) | ||
199 | return ret; | ||
200 | |||
201 | priv->adhoc_is_link_sensed = false; | ||
202 | |||
203 | /* Search for the requested network in the scan table */ | ||
204 | if (ssid_bssid->ssid.ssid_len) | ||
205 | i = mwifiex_find_ssid_in_list(priv, | ||
206 | &ssid_bssid->ssid, NULL, | ||
207 | NL80211_IFTYPE_ADHOC); | ||
208 | else | ||
209 | i = mwifiex_find_bssid_in_list(priv, | ||
210 | (u8 *)&ssid_bssid->bssid, | ||
211 | NL80211_IFTYPE_ADHOC); | ||
212 | |||
213 | if (i >= 0) { | ||
214 | dev_dbg(adapter->dev, "info: network found in scan" | ||
215 | " list. Joining...\n"); | ||
216 | ret = mwifiex_adhoc_join(priv, &adapter->scan_table[i]); | ||
217 | if (ret) | ||
218 | return ret; | ||
219 | } else { | ||
220 | dev_dbg(adapter->dev, "info: Network not found in " | ||
221 | "the list, creating adhoc with ssid = %s\n", | ||
222 | ssid_bssid->ssid.ssid); | ||
223 | ret = mwifiex_adhoc_start(priv, &ssid_bssid->ssid); | ||
224 | if (ret) | ||
225 | return ret; | ||
226 | } | ||
227 | } | ||
228 | |||
229 | return ret; | ||
230 | } | ||
231 | |||
232 | /* | ||
233 | * IOCTL request handler to set host sleep configuration. | ||
234 | * | ||
235 | * This function prepares the correct firmware command and | ||
236 | * issues it. | ||
237 | */ | ||
238 | int mwifiex_set_hs_params(struct mwifiex_private *priv, u16 action, | ||
239 | int cmd_type, struct mwifiex_ds_hs_cfg *hs_cfg) | ||
240 | |||
241 | { | ||
242 | struct mwifiex_adapter *adapter = priv->adapter; | ||
243 | int status = 0; | ||
244 | u32 prev_cond = 0; | ||
245 | |||
246 | if (!hs_cfg) | ||
247 | return -ENOMEM; | ||
248 | |||
249 | switch (action) { | ||
250 | case HostCmd_ACT_GEN_SET: | ||
251 | if (adapter->pps_uapsd_mode) { | ||
252 | dev_dbg(adapter->dev, "info: Host Sleep IOCTL" | ||
253 | " is blocked in UAPSD/PPS mode\n"); | ||
254 | status = -1; | ||
255 | break; | ||
256 | } | ||
257 | if (hs_cfg->is_invoke_hostcmd) { | ||
258 | if (hs_cfg->conditions == HOST_SLEEP_CFG_CANCEL) { | ||
259 | if (!adapter->is_hs_configured) | ||
260 | /* Already cancelled */ | ||
261 | break; | ||
262 | /* Save previous condition */ | ||
263 | prev_cond = le32_to_cpu(adapter->hs_cfg | ||
264 | .conditions); | ||
265 | adapter->hs_cfg.conditions = | ||
266 | cpu_to_le32(hs_cfg->conditions); | ||
267 | } else if (hs_cfg->conditions) { | ||
268 | adapter->hs_cfg.conditions = | ||
269 | cpu_to_le32(hs_cfg->conditions); | ||
270 | adapter->hs_cfg.gpio = (u8)hs_cfg->gpio; | ||
271 | if (hs_cfg->gap) | ||
272 | adapter->hs_cfg.gap = (u8)hs_cfg->gap; | ||
273 | } else if (adapter->hs_cfg.conditions == | ||
274 | cpu_to_le32( | ||
275 | HOST_SLEEP_CFG_CANCEL)) { | ||
276 | /* Return failure if no parameters for HS | ||
277 | enable */ | ||
278 | status = -1; | ||
279 | break; | ||
280 | } | ||
281 | if (cmd_type == MWIFIEX_SYNC_CMD) | ||
282 | status = mwifiex_send_cmd_sync(priv, | ||
283 | HostCmd_CMD_802_11_HS_CFG_ENH, | ||
284 | HostCmd_ACT_GEN_SET, 0, | ||
285 | &adapter->hs_cfg); | ||
286 | else | ||
287 | status = mwifiex_send_cmd_async(priv, | ||
288 | HostCmd_CMD_802_11_HS_CFG_ENH, | ||
289 | HostCmd_ACT_GEN_SET, 0, | ||
290 | &adapter->hs_cfg); | ||
291 | if (hs_cfg->conditions == HOST_SLEEP_CFG_CANCEL) | ||
292 | /* Restore previous condition */ | ||
293 | adapter->hs_cfg.conditions = | ||
294 | cpu_to_le32(prev_cond); | ||
295 | } else { | ||
296 | adapter->hs_cfg.conditions = | ||
297 | cpu_to_le32(hs_cfg->conditions); | ||
298 | adapter->hs_cfg.gpio = (u8)hs_cfg->gpio; | ||
299 | adapter->hs_cfg.gap = (u8)hs_cfg->gap; | ||
300 | } | ||
301 | break; | ||
302 | case HostCmd_ACT_GEN_GET: | ||
303 | hs_cfg->conditions = le32_to_cpu(adapter->hs_cfg.conditions); | ||
304 | hs_cfg->gpio = adapter->hs_cfg.gpio; | ||
305 | hs_cfg->gap = adapter->hs_cfg.gap; | ||
306 | break; | ||
307 | default: | ||
308 | status = -1; | ||
309 | break; | ||
310 | } | ||
311 | |||
312 | return status; | ||
313 | } | ||
314 | |||
315 | /* | ||
316 | * Sends IOCTL request to cancel the existing Host Sleep configuration. | ||
317 | * | ||
318 | * This function allocates the IOCTL request buffer, fills it | ||
319 | * with requisite parameters and calls the IOCTL handler. | ||
320 | */ | ||
321 | int mwifiex_cancel_hs(struct mwifiex_private *priv, int cmd_type) | ||
322 | { | ||
323 | struct mwifiex_ds_hs_cfg hscfg; | ||
324 | |||
325 | hscfg.conditions = HOST_SLEEP_CFG_CANCEL; | ||
326 | hscfg.is_invoke_hostcmd = true; | ||
327 | |||
328 | return mwifiex_set_hs_params(priv, HostCmd_ACT_GEN_SET, | ||
329 | cmd_type, &hscfg); | ||
330 | } | ||
331 | EXPORT_SYMBOL_GPL(mwifiex_cancel_hs); | ||
332 | |||
333 | /* | ||
334 | * Sends IOCTL request to cancel the existing Host Sleep configuration. | ||
335 | * | ||
336 | * This function allocates the IOCTL request buffer, fills it | ||
337 | * with requisite parameters and calls the IOCTL handler. | ||
338 | */ | ||
339 | int mwifiex_enable_hs(struct mwifiex_adapter *adapter) | ||
340 | { | ||
341 | struct mwifiex_ds_hs_cfg hscfg; | ||
342 | |||
343 | if (adapter->hs_activated) { | ||
344 | dev_dbg(adapter->dev, "cmd: HS Already actived\n"); | ||
345 | return true; | ||
346 | } | ||
347 | |||
348 | adapter->hs_activate_wait_q_woken = false; | ||
349 | |||
350 | memset(&hscfg, 0, sizeof(struct mwifiex_hs_config_param)); | ||
351 | hscfg.is_invoke_hostcmd = true; | ||
352 | |||
353 | if (mwifiex_set_hs_params(mwifiex_get_priv(adapter, | ||
354 | MWIFIEX_BSS_ROLE_STA), | ||
355 | HostCmd_ACT_GEN_SET, MWIFIEX_SYNC_CMD, | ||
356 | &hscfg)) { | ||
357 | dev_err(adapter->dev, "IOCTL request HS enable failed\n"); | ||
358 | return false; | ||
359 | } | ||
360 | |||
361 | wait_event_interruptible(adapter->hs_activate_wait_q, | ||
362 | adapter->hs_activate_wait_q_woken); | ||
363 | |||
364 | return true; | ||
365 | } | ||
366 | EXPORT_SYMBOL_GPL(mwifiex_enable_hs); | ||
367 | |||
368 | /* | ||
369 | * IOCTL request handler to get BSS information. | ||
370 | * | ||
371 | * This function collates the information from different driver structures | ||
372 | * to send to the user. | ||
373 | */ | ||
374 | int mwifiex_get_bss_info(struct mwifiex_private *priv, | ||
375 | struct mwifiex_bss_info *info) | ||
376 | { | ||
377 | struct mwifiex_adapter *adapter = priv->adapter; | ||
378 | struct mwifiex_bssdescriptor *bss_desc; | ||
379 | s32 tbl_idx; | ||
380 | |||
381 | if (!info) | ||
382 | return -1; | ||
383 | |||
384 | bss_desc = &priv->curr_bss_params.bss_descriptor; | ||
385 | |||
386 | info->bss_mode = priv->bss_mode; | ||
387 | |||
388 | memcpy(&info->ssid, &bss_desc->ssid, | ||
389 | sizeof(struct mwifiex_802_11_ssid)); | ||
390 | |||
391 | memcpy(&info->bssid, &bss_desc->mac_address, ETH_ALEN); | ||
392 | |||
393 | info->bss_chan = bss_desc->channel; | ||
394 | |||
395 | info->region_code = adapter->region_code; | ||
396 | |||
397 | /* Scan table index if connected */ | ||
398 | info->scan_table_idx = 0; | ||
399 | if (priv->media_connected) { | ||
400 | tbl_idx = | ||
401 | mwifiex_find_ssid_in_list(priv, &bss_desc->ssid, | ||
402 | bss_desc->mac_address, | ||
403 | priv->bss_mode); | ||
404 | if (tbl_idx >= 0) | ||
405 | info->scan_table_idx = tbl_idx; | ||
406 | } | ||
407 | |||
408 | info->media_connected = priv->media_connected; | ||
409 | |||
410 | info->max_power_level = priv->max_tx_power_level; | ||
411 | info->min_power_level = priv->min_tx_power_level; | ||
412 | |||
413 | info->adhoc_state = priv->adhoc_state; | ||
414 | |||
415 | info->bcn_nf_last = priv->bcn_nf_last; | ||
416 | |||
417 | if (priv->sec_info.wep_status == MWIFIEX_802_11_WEP_ENABLED) | ||
418 | info->wep_status = true; | ||
419 | else | ||
420 | info->wep_status = false; | ||
421 | |||
422 | info->is_hs_configured = adapter->is_hs_configured; | ||
423 | info->is_deep_sleep = adapter->is_deep_sleep; | ||
424 | |||
425 | return 0; | ||
426 | } | ||
427 | |||
428 | /* | ||
429 | * The function sets band configurations. | ||
430 | * | ||
431 | * it performs extra checks to make sure the Ad-Hoc | ||
432 | * band and channel are compatible. Otherwise it returns an error. | ||
433 | * | ||
434 | */ | ||
435 | int mwifiex_set_radio_band_cfg(struct mwifiex_private *priv, | ||
436 | struct mwifiex_ds_band_cfg *radio_cfg) | ||
437 | { | ||
438 | struct mwifiex_adapter *adapter = priv->adapter; | ||
439 | u8 infra_band, adhoc_band; | ||
440 | u32 adhoc_channel; | ||
441 | |||
442 | infra_band = (u8) radio_cfg->config_bands; | ||
443 | adhoc_band = (u8) radio_cfg->adhoc_start_band; | ||
444 | adhoc_channel = radio_cfg->adhoc_channel; | ||
445 | |||
446 | /* SET Infra band */ | ||
447 | if ((infra_band | adapter->fw_bands) & ~adapter->fw_bands) | ||
448 | return -1; | ||
449 | |||
450 | adapter->config_bands = infra_band; | ||
451 | |||
452 | /* SET Ad-hoc Band */ | ||
453 | if ((adhoc_band | adapter->fw_bands) & ~adapter->fw_bands) | ||
454 | return -1; | ||
455 | |||
456 | if (adhoc_band) | ||
457 | adapter->adhoc_start_band = adhoc_band; | ||
458 | adapter->chan_offset = (u8) radio_cfg->sec_chan_offset; | ||
459 | /* | ||
460 | * If no adhoc_channel is supplied verify if the existing adhoc | ||
461 | * channel compiles with new adhoc_band | ||
462 | */ | ||
463 | if (!adhoc_channel) { | ||
464 | if (!mwifiex_get_cfp_by_band_and_channel_from_cfg80211 | ||
465 | (priv, adapter->adhoc_start_band, | ||
466 | priv->adhoc_channel)) { | ||
467 | /* Pass back the default channel */ | ||
468 | radio_cfg->adhoc_channel = DEFAULT_AD_HOC_CHANNEL; | ||
469 | if ((adapter->adhoc_start_band & BAND_A) | ||
470 | || (adapter->adhoc_start_band & BAND_AN)) | ||
471 | radio_cfg->adhoc_channel = | ||
472 | DEFAULT_AD_HOC_CHANNEL_A; | ||
473 | } | ||
474 | } else { /* Retrurn error if adhoc_band and | ||
475 | adhoc_channel combination is invalid */ | ||
476 | if (!mwifiex_get_cfp_by_band_and_channel_from_cfg80211 | ||
477 | (priv, adapter->adhoc_start_band, (u16) adhoc_channel)) | ||
478 | return -1; | ||
479 | priv->adhoc_channel = (u8) adhoc_channel; | ||
480 | } | ||
481 | if ((adhoc_band & BAND_GN) || (adhoc_band & BAND_AN)) | ||
482 | adapter->adhoc_11n_enabled = true; | ||
483 | else | ||
484 | adapter->adhoc_11n_enabled = false; | ||
485 | |||
486 | return 0; | ||
487 | } | ||
488 | |||
489 | /* | ||
490 | * IOCTL request handler to set/get active channel. | ||
491 | * | ||
492 | * This function performs validity checking on channel/frequency | ||
493 | * compatibility and returns failure if not valid. | ||
494 | */ | ||
495 | int mwifiex_bss_set_channel(struct mwifiex_private *priv, | ||
496 | struct mwifiex_chan_freq_power *chan) | ||
497 | { | ||
498 | struct mwifiex_adapter *adapter = priv->adapter; | ||
499 | struct mwifiex_chan_freq_power *cfp = NULL; | ||
500 | |||
501 | if (!chan) | ||
502 | return -1; | ||
503 | |||
504 | if (!chan->channel && !chan->freq) | ||
505 | return -1; | ||
506 | if (adapter->adhoc_start_band & BAND_AN) | ||
507 | adapter->adhoc_start_band = BAND_G | BAND_B | BAND_GN; | ||
508 | else if (adapter->adhoc_start_band & BAND_A) | ||
509 | adapter->adhoc_start_band = BAND_G | BAND_B; | ||
510 | if (chan->channel) { | ||
511 | if (chan->channel <= MAX_CHANNEL_BAND_BG) | ||
512 | cfp = mwifiex_get_cfp_by_band_and_channel_from_cfg80211 | ||
513 | (priv, 0, (u16) chan->channel); | ||
514 | if (!cfp) { | ||
515 | cfp = mwifiex_get_cfp_by_band_and_channel_from_cfg80211 | ||
516 | (priv, BAND_A, (u16) chan->channel); | ||
517 | if (cfp) { | ||
518 | if (adapter->adhoc_11n_enabled) | ||
519 | adapter->adhoc_start_band = BAND_A | ||
520 | | BAND_AN; | ||
521 | else | ||
522 | adapter->adhoc_start_band = BAND_A; | ||
523 | } | ||
524 | } | ||
525 | } else { | ||
526 | if (chan->freq <= MAX_FREQUENCY_BAND_BG) | ||
527 | cfp = mwifiex_get_cfp_by_band_and_freq_from_cfg80211( | ||
528 | priv, 0, chan->freq); | ||
529 | if (!cfp) { | ||
530 | cfp = mwifiex_get_cfp_by_band_and_freq_from_cfg80211 | ||
531 | (priv, BAND_A, chan->freq); | ||
532 | if (cfp) { | ||
533 | if (adapter->adhoc_11n_enabled) | ||
534 | adapter->adhoc_start_band = BAND_A | ||
535 | | BAND_AN; | ||
536 | else | ||
537 | adapter->adhoc_start_band = BAND_A; | ||
538 | } | ||
539 | } | ||
540 | } | ||
541 | if (!cfp || !cfp->channel) { | ||
542 | dev_err(adapter->dev, "invalid channel/freq\n"); | ||
543 | return -1; | ||
544 | } | ||
545 | priv->adhoc_channel = (u8) cfp->channel; | ||
546 | chan->channel = cfp->channel; | ||
547 | chan->freq = cfp->freq; | ||
548 | |||
549 | return 0; | ||
550 | } | ||
551 | |||
552 | /* | ||
553 | * IOCTL request handler to set/get Ad-Hoc channel. | ||
554 | * | ||
555 | * This function prepares the correct firmware command and | ||
556 | * issues it to set or get the ad-hoc channel. | ||
557 | */ | ||
558 | static int mwifiex_bss_ioctl_ibss_channel(struct mwifiex_private *priv, | ||
559 | u16 action, u16 *channel) | ||
560 | { | ||
561 | if (action == HostCmd_ACT_GEN_GET) { | ||
562 | if (!priv->media_connected) { | ||
563 | *channel = priv->adhoc_channel; | ||
564 | return 0; | ||
565 | } | ||
566 | } else { | ||
567 | priv->adhoc_channel = (u8) *channel; | ||
568 | } | ||
569 | |||
570 | return mwifiex_send_cmd_sync(priv, HostCmd_CMD_802_11_RF_CHANNEL, | ||
571 | action, 0, channel); | ||
572 | } | ||
573 | |||
574 | /* | ||
575 | * IOCTL request handler to find a particular BSS. | ||
576 | * | ||
577 | * The BSS can be searched with either a BSSID or a SSID. If none of | ||
578 | * these are provided, just the best BSS (best RSSI) is returned. | ||
579 | */ | ||
580 | int mwifiex_bss_ioctl_find_bss(struct mwifiex_private *priv, | ||
581 | struct mwifiex_ssid_bssid *ssid_bssid) | ||
582 | { | ||
583 | struct mwifiex_adapter *adapter = priv->adapter; | ||
584 | struct mwifiex_bssdescriptor *bss_desc; | ||
585 | u8 zero_mac[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 }; | ||
586 | u8 mac[ETH_ALEN]; | ||
587 | int i = 0; | ||
588 | |||
589 | if (memcmp(ssid_bssid->bssid, zero_mac, sizeof(zero_mac))) { | ||
590 | i = mwifiex_find_bssid_in_list(priv, | ||
591 | (u8 *) ssid_bssid->bssid, | ||
592 | priv->bss_mode); | ||
593 | if (i < 0) { | ||
594 | memcpy(mac, ssid_bssid->bssid, sizeof(mac)); | ||
595 | dev_err(adapter->dev, "cannot find bssid %pM\n", mac); | ||
596 | return -1; | ||
597 | } | ||
598 | bss_desc = &adapter->scan_table[i]; | ||
599 | memcpy(&ssid_bssid->ssid, &bss_desc->ssid, | ||
600 | sizeof(struct mwifiex_802_11_ssid)); | ||
601 | } else if (ssid_bssid->ssid.ssid_len) { | ||
602 | i = mwifiex_find_ssid_in_list(priv, &ssid_bssid->ssid, NULL, | ||
603 | priv->bss_mode); | ||
604 | if (i < 0) { | ||
605 | dev_err(adapter->dev, "cannot find ssid %s\n", | ||
606 | ssid_bssid->ssid.ssid); | ||
607 | return -1; | ||
608 | } | ||
609 | bss_desc = &adapter->scan_table[i]; | ||
610 | memcpy(ssid_bssid->bssid, bss_desc->mac_address, ETH_ALEN); | ||
611 | } else { | ||
612 | return mwifiex_find_best_network(priv, ssid_bssid); | ||
613 | } | ||
614 | |||
615 | return 0; | ||
616 | } | ||
617 | |||
618 | /* | ||
619 | * IOCTL request handler to change Ad-Hoc channel. | ||
620 | * | ||
621 | * This function allocates the IOCTL request buffer, fills it | ||
622 | * with requisite parameters and calls the IOCTL handler. | ||
623 | * | ||
624 | * The function follows the following steps to perform the change - | ||
625 | * - Get current IBSS information | ||
626 | * - Get current channel | ||
627 | * - If no change is required, return | ||
628 | * - If not connected, change channel and return | ||
629 | * - If connected, | ||
630 | * - Disconnect | ||
631 | * - Change channel | ||
632 | * - Perform specific SSID scan with same SSID | ||
633 | * - Start/Join the IBSS | ||
634 | */ | ||
635 | int | ||
636 | mwifiex_drv_change_adhoc_chan(struct mwifiex_private *priv, int channel) | ||
637 | { | ||
638 | int ret; | ||
639 | struct mwifiex_bss_info bss_info; | ||
640 | struct mwifiex_ssid_bssid ssid_bssid; | ||
641 | u16 curr_chan = 0; | ||
642 | |||
643 | memset(&bss_info, 0, sizeof(bss_info)); | ||
644 | |||
645 | /* Get BSS information */ | ||
646 | if (mwifiex_get_bss_info(priv, &bss_info)) | ||
647 | return -1; | ||
648 | |||
649 | /* Get current channel */ | ||
650 | ret = mwifiex_bss_ioctl_ibss_channel(priv, HostCmd_ACT_GEN_GET, | ||
651 | &curr_chan); | ||
652 | |||
653 | if (curr_chan == channel) { | ||
654 | ret = 0; | ||
655 | goto done; | ||
656 | } | ||
657 | dev_dbg(priv->adapter->dev, "cmd: updating channel from %d to %d\n", | ||
658 | curr_chan, channel); | ||
659 | |||
660 | if (!bss_info.media_connected) { | ||
661 | ret = 0; | ||
662 | goto done; | ||
663 | } | ||
664 | |||
665 | /* Do disonnect */ | ||
666 | memset(&ssid_bssid, 0, ETH_ALEN); | ||
667 | ret = mwifiex_deauthenticate(priv, ssid_bssid.bssid); | ||
668 | |||
669 | ret = mwifiex_bss_ioctl_ibss_channel(priv, HostCmd_ACT_GEN_SET, | ||
670 | (u16 *) &channel); | ||
671 | |||
672 | /* Do specific SSID scanning */ | ||
673 | if (mwifiex_request_scan(priv, &bss_info.ssid)) { | ||
674 | ret = -1; | ||
675 | goto done; | ||
676 | } | ||
677 | /* Start/Join Adhoc network */ | ||
678 | memset(&ssid_bssid, 0, sizeof(struct mwifiex_ssid_bssid)); | ||
679 | memcpy(&ssid_bssid.ssid, &bss_info.ssid, | ||
680 | sizeof(struct mwifiex_802_11_ssid)); | ||
681 | |||
682 | ret = mwifiex_bss_start(priv, &ssid_bssid); | ||
683 | done: | ||
684 | return ret; | ||
685 | } | ||
686 | |||
687 | /* | ||
688 | * IOCTL request handler to get rate. | ||
689 | * | ||
690 | * This function prepares the correct firmware command and | ||
691 | * issues it to get the current rate if it is connected, | ||
692 | * otherwise, the function returns the lowest supported rate | ||
693 | * for the band. | ||
694 | */ | ||
695 | static int mwifiex_rate_ioctl_get_rate_value(struct mwifiex_private *priv, | ||
696 | struct mwifiex_rate_cfg *rate_cfg) | ||
697 | { | ||
698 | struct mwifiex_adapter *adapter = priv->adapter; | ||
699 | |||
700 | rate_cfg->is_rate_auto = priv->is_data_rate_auto; | ||
701 | if (!priv->media_connected) { | ||
702 | switch (adapter->config_bands) { | ||
703 | case BAND_B: | ||
704 | /* Return the lowest supported rate for B band */ | ||
705 | rate_cfg->rate = supported_rates_b[0] & 0x7f; | ||
706 | break; | ||
707 | case BAND_G: | ||
708 | case BAND_G | BAND_GN: | ||
709 | /* Return the lowest supported rate for G band */ | ||
710 | rate_cfg->rate = supported_rates_g[0] & 0x7f; | ||
711 | break; | ||
712 | case BAND_B | BAND_G: | ||
713 | case BAND_A | BAND_B | BAND_G: | ||
714 | case BAND_A | BAND_B: | ||
715 | case BAND_A | BAND_B | BAND_G | BAND_AN | BAND_GN: | ||
716 | case BAND_B | BAND_G | BAND_GN: | ||
717 | /* Return the lowest supported rate for BG band */ | ||
718 | rate_cfg->rate = supported_rates_bg[0] & 0x7f; | ||
719 | break; | ||
720 | case BAND_A: | ||
721 | case BAND_A | BAND_G: | ||
722 | case BAND_A | BAND_G | BAND_AN | BAND_GN: | ||
723 | case BAND_A | BAND_AN: | ||
724 | /* Return the lowest supported rate for A band */ | ||
725 | rate_cfg->rate = supported_rates_a[0] & 0x7f; | ||
726 | break; | ||
727 | case BAND_GN: | ||
728 | /* Return the lowest supported rate for N band */ | ||
729 | rate_cfg->rate = supported_rates_n[0] & 0x7f; | ||
730 | break; | ||
731 | default: | ||
732 | dev_warn(adapter->dev, "invalid band %#x\n", | ||
733 | adapter->config_bands); | ||
734 | break; | ||
735 | } | ||
736 | } else { | ||
737 | return mwifiex_send_cmd_sync(priv, | ||
738 | HostCmd_CMD_802_11_TX_RATE_QUERY, | ||
739 | HostCmd_ACT_GEN_GET, 0, NULL); | ||
740 | } | ||
741 | |||
742 | return 0; | ||
743 | } | ||
744 | |||
745 | /* | ||
746 | * IOCTL request handler to set rate. | ||
747 | * | ||
748 | * This function prepares the correct firmware command and | ||
749 | * issues it to set the current rate. | ||
750 | * | ||
751 | * The function also performs validation checking on the supplied value. | ||
752 | */ | ||
753 | static int mwifiex_rate_ioctl_set_rate_value(struct mwifiex_private *priv, | ||
754 | struct mwifiex_rate_cfg *rate_cfg) | ||
755 | { | ||
756 | u8 rates[MWIFIEX_SUPPORTED_RATES]; | ||
757 | u8 *rate; | ||
758 | int rate_index, ret; | ||
759 | u16 bitmap_rates[MAX_BITMAP_RATES_SIZE]; | ||
760 | u32 i; | ||
761 | struct mwifiex_adapter *adapter = priv->adapter; | ||
762 | |||
763 | if (rate_cfg->is_rate_auto) { | ||
764 | memset(bitmap_rates, 0, sizeof(bitmap_rates)); | ||
765 | /* Support all HR/DSSS rates */ | ||
766 | bitmap_rates[0] = 0x000F; | ||
767 | /* Support all OFDM rates */ | ||
768 | bitmap_rates[1] = 0x00FF; | ||
769 | /* Support all HT-MCSs rate */ | ||
770 | for (i = 0; i < ARRAY_SIZE(priv->bitmap_rates) - 3; i++) | ||
771 | bitmap_rates[i + 2] = 0xFFFF; | ||
772 | bitmap_rates[9] = 0x3FFF; | ||
773 | } else { | ||
774 | memset(rates, 0, sizeof(rates)); | ||
775 | mwifiex_get_active_data_rates(priv, rates); | ||
776 | rate = rates; | ||
777 | for (i = 0; (rate[i] && i < MWIFIEX_SUPPORTED_RATES); i++) { | ||
778 | dev_dbg(adapter->dev, "info: rate=%#x wanted=%#x\n", | ||
779 | rate[i], rate_cfg->rate); | ||
780 | if ((rate[i] & 0x7f) == (rate_cfg->rate & 0x7f)) | ||
781 | break; | ||
782 | } | ||
783 | if (!rate[i] || (i == MWIFIEX_SUPPORTED_RATES)) { | ||
784 | dev_err(adapter->dev, "fixed data rate %#x is out " | ||
785 | "of range\n", rate_cfg->rate); | ||
786 | return -1; | ||
787 | } | ||
788 | memset(bitmap_rates, 0, sizeof(bitmap_rates)); | ||
789 | |||
790 | rate_index = mwifiex_data_rate_to_index(rate_cfg->rate); | ||
791 | |||
792 | /* Only allow b/g rates to be set */ | ||
793 | if (rate_index >= MWIFIEX_RATE_INDEX_HRDSSS0 && | ||
794 | rate_index <= MWIFIEX_RATE_INDEX_HRDSSS3) { | ||
795 | bitmap_rates[0] = 1 << rate_index; | ||
796 | } else { | ||
797 | rate_index -= 1; /* There is a 0x00 in the table */ | ||
798 | if (rate_index >= MWIFIEX_RATE_INDEX_OFDM0 && | ||
799 | rate_index <= MWIFIEX_RATE_INDEX_OFDM7) | ||
800 | bitmap_rates[1] = 1 << (rate_index - | ||
801 | MWIFIEX_RATE_INDEX_OFDM0); | ||
802 | } | ||
803 | } | ||
804 | |||
805 | ret = mwifiex_send_cmd_sync(priv, HostCmd_CMD_TX_RATE_CFG, | ||
806 | HostCmd_ACT_GEN_SET, 0, bitmap_rates); | ||
807 | |||
808 | return ret; | ||
809 | } | ||
810 | |||
811 | /* | ||
812 | * IOCTL request handler to set/get rate. | ||
813 | * | ||
814 | * This function can be used to set/get either the rate value or the | ||
815 | * rate index. | ||
816 | */ | ||
817 | static int mwifiex_rate_ioctl_cfg(struct mwifiex_private *priv, | ||
818 | struct mwifiex_rate_cfg *rate_cfg) | ||
819 | { | ||
820 | int status; | ||
821 | |||
822 | if (!rate_cfg) | ||
823 | return -1; | ||
824 | |||
825 | if (rate_cfg->action == HostCmd_ACT_GEN_GET) | ||
826 | status = mwifiex_rate_ioctl_get_rate_value(priv, rate_cfg); | ||
827 | else | ||
828 | status = mwifiex_rate_ioctl_set_rate_value(priv, rate_cfg); | ||
829 | |||
830 | return status; | ||
831 | } | ||
832 | |||
833 | /* | ||
834 | * Sends IOCTL request to get the data rate. | ||
835 | * | ||
836 | * This function allocates the IOCTL request buffer, fills it | ||
837 | * with requisite parameters and calls the IOCTL handler. | ||
838 | */ | ||
839 | int mwifiex_drv_get_data_rate(struct mwifiex_private *priv, | ||
840 | struct mwifiex_rate_cfg *rate) | ||
841 | { | ||
842 | int ret; | ||
843 | |||
844 | memset(rate, 0, sizeof(struct mwifiex_rate_cfg)); | ||
845 | rate->action = HostCmd_ACT_GEN_GET; | ||
846 | ret = mwifiex_rate_ioctl_cfg(priv, rate); | ||
847 | |||
848 | if (!ret) { | ||
849 | if (rate && rate->is_rate_auto) | ||
850 | rate->rate = mwifiex_index_to_data_rate(priv->tx_rate, | ||
851 | priv->tx_htinfo); | ||
852 | else if (rate) | ||
853 | rate->rate = priv->data_rate; | ||
854 | } else { | ||
855 | ret = -1; | ||
856 | } | ||
857 | |||
858 | return ret; | ||
859 | } | ||
860 | |||
861 | /* | ||
862 | * IOCTL request handler to set tx power configuration. | ||
863 | * | ||
864 | * This function prepares the correct firmware command and | ||
865 | * issues it. | ||
866 | * | ||
867 | * For non-auto power mode, all the following power groups are set - | ||
868 | * - Modulation class HR/DSSS | ||
869 | * - Modulation class OFDM | ||
870 | * - Modulation class HTBW20 | ||
871 | * - Modulation class HTBW40 | ||
872 | */ | ||
873 | int mwifiex_set_tx_power(struct mwifiex_private *priv, | ||
874 | struct mwifiex_power_cfg *power_cfg) | ||
875 | { | ||
876 | int ret; | ||
877 | struct host_cmd_ds_txpwr_cfg *txp_cfg; | ||
878 | struct mwifiex_types_power_group *pg_tlv; | ||
879 | struct mwifiex_power_group *pg; | ||
880 | u8 *buf; | ||
881 | u16 dbm = 0; | ||
882 | |||
883 | if (!power_cfg->is_power_auto) { | ||
884 | dbm = (u16) power_cfg->power_level; | ||
885 | if ((dbm < priv->min_tx_power_level) || | ||
886 | (dbm > priv->max_tx_power_level)) { | ||
887 | dev_err(priv->adapter->dev, "txpower value %d dBm" | ||
888 | " is out of range (%d dBm-%d dBm)\n", | ||
889 | dbm, priv->min_tx_power_level, | ||
890 | priv->max_tx_power_level); | ||
891 | return -1; | ||
892 | } | ||
893 | } | ||
894 | buf = kzalloc(MWIFIEX_SIZE_OF_CMD_BUFFER, GFP_KERNEL); | ||
895 | if (!buf) { | ||
896 | dev_err(priv->adapter->dev, "%s: failed to alloc cmd buffer\n", | ||
897 | __func__); | ||
898 | return -ENOMEM; | ||
899 | } | ||
900 | |||
901 | txp_cfg = (struct host_cmd_ds_txpwr_cfg *) buf; | ||
902 | txp_cfg->action = cpu_to_le16(HostCmd_ACT_GEN_SET); | ||
903 | if (!power_cfg->is_power_auto) { | ||
904 | txp_cfg->mode = cpu_to_le32(1); | ||
905 | pg_tlv = (struct mwifiex_types_power_group *) (buf + | ||
906 | sizeof(struct host_cmd_ds_txpwr_cfg)); | ||
907 | pg_tlv->type = TLV_TYPE_POWER_GROUP; | ||
908 | pg_tlv->length = 4 * sizeof(struct mwifiex_power_group); | ||
909 | pg = (struct mwifiex_power_group *) (buf + | ||
910 | sizeof(struct host_cmd_ds_txpwr_cfg) + | ||
911 | sizeof(struct mwifiex_types_power_group)); | ||
912 | /* Power group for modulation class HR/DSSS */ | ||
913 | pg->first_rate_code = 0x00; | ||
914 | pg->last_rate_code = 0x03; | ||
915 | pg->modulation_class = MOD_CLASS_HR_DSSS; | ||
916 | pg->power_step = 0; | ||
917 | pg->power_min = (s8) dbm; | ||
918 | pg->power_max = (s8) dbm; | ||
919 | pg++; | ||
920 | /* Power group for modulation class OFDM */ | ||
921 | pg->first_rate_code = 0x00; | ||
922 | pg->last_rate_code = 0x07; | ||
923 | pg->modulation_class = MOD_CLASS_OFDM; | ||
924 | pg->power_step = 0; | ||
925 | pg->power_min = (s8) dbm; | ||
926 | pg->power_max = (s8) dbm; | ||
927 | pg++; | ||
928 | /* Power group for modulation class HTBW20 */ | ||
929 | pg->first_rate_code = 0x00; | ||
930 | pg->last_rate_code = 0x20; | ||
931 | pg->modulation_class = MOD_CLASS_HT; | ||
932 | pg->power_step = 0; | ||
933 | pg->power_min = (s8) dbm; | ||
934 | pg->power_max = (s8) dbm; | ||
935 | pg->ht_bandwidth = HT_BW_20; | ||
936 | pg++; | ||
937 | /* Power group for modulation class HTBW40 */ | ||
938 | pg->first_rate_code = 0x00; | ||
939 | pg->last_rate_code = 0x20; | ||
940 | pg->modulation_class = MOD_CLASS_HT; | ||
941 | pg->power_step = 0; | ||
942 | pg->power_min = (s8) dbm; | ||
943 | pg->power_max = (s8) dbm; | ||
944 | pg->ht_bandwidth = HT_BW_40; | ||
945 | } | ||
946 | ret = mwifiex_send_cmd_sync(priv, HostCmd_CMD_TXPWR_CFG, | ||
947 | HostCmd_ACT_GEN_SET, 0, buf); | ||
948 | |||
949 | kfree(buf); | ||
950 | return ret; | ||
951 | } | ||
952 | |||
953 | /* | ||
954 | * IOCTL request handler to get power save mode. | ||
955 | * | ||
956 | * This function prepares the correct firmware command and | ||
957 | * issues it. | ||
958 | */ | ||
959 | int mwifiex_drv_set_power(struct mwifiex_private *priv, u32 *ps_mode) | ||
960 | { | ||
961 | int ret; | ||
962 | struct mwifiex_adapter *adapter = priv->adapter; | ||
963 | u16 sub_cmd; | ||
964 | |||
965 | if (*ps_mode) | ||
966 | adapter->ps_mode = MWIFIEX_802_11_POWER_MODE_PSP; | ||
967 | else | ||
968 | adapter->ps_mode = MWIFIEX_802_11_POWER_MODE_CAM; | ||
969 | sub_cmd = (*ps_mode) ? EN_AUTO_PS : DIS_AUTO_PS; | ||
970 | ret = mwifiex_send_cmd_sync(priv, HostCmd_CMD_802_11_PS_MODE_ENH, | ||
971 | sub_cmd, BITMAP_STA_PS, NULL); | ||
972 | if ((!ret) && (sub_cmd == DIS_AUTO_PS)) | ||
973 | ret = mwifiex_send_cmd_async(priv, | ||
974 | HostCmd_CMD_802_11_PS_MODE_ENH, GET_PS, | ||
975 | 0, NULL); | ||
976 | |||
977 | return ret; | ||
978 | } | ||
979 | |||
980 | /* | ||
981 | * IOCTL request handler to set/reset WPA IE. | ||
982 | * | ||
983 | * The supplied WPA IE is treated as a opaque buffer. Only the first field | ||
984 | * is checked to determine WPA version. If buffer length is zero, the existing | ||
985 | * WPA IE is reset. | ||
986 | */ | ||
987 | static int mwifiex_set_wpa_ie_helper(struct mwifiex_private *priv, | ||
988 | u8 *ie_data_ptr, u16 ie_len) | ||
989 | { | ||
990 | if (ie_len) { | ||
991 | if (ie_len > sizeof(priv->wpa_ie)) { | ||
992 | dev_err(priv->adapter->dev, | ||
993 | "failed to copy WPA IE, too big\n"); | ||
994 | return -1; | ||
995 | } | ||
996 | memcpy(priv->wpa_ie, ie_data_ptr, ie_len); | ||
997 | priv->wpa_ie_len = (u8) ie_len; | ||
998 | dev_dbg(priv->adapter->dev, "cmd: Set Wpa_ie_len=%d IE=%#x\n", | ||
999 | priv->wpa_ie_len, priv->wpa_ie[0]); | ||
1000 | |||
1001 | if (priv->wpa_ie[0] == WLAN_EID_WPA) { | ||
1002 | priv->sec_info.wpa_enabled = true; | ||
1003 | } else if (priv->wpa_ie[0] == WLAN_EID_RSN) { | ||
1004 | priv->sec_info.wpa2_enabled = true; | ||
1005 | } else { | ||
1006 | priv->sec_info.wpa_enabled = false; | ||
1007 | priv->sec_info.wpa2_enabled = false; | ||
1008 | } | ||
1009 | } else { | ||
1010 | memset(priv->wpa_ie, 0, sizeof(priv->wpa_ie)); | ||
1011 | priv->wpa_ie_len = 0; | ||
1012 | dev_dbg(priv->adapter->dev, "info: reset wpa_ie_len=%d IE=%#x\n", | ||
1013 | priv->wpa_ie_len, priv->wpa_ie[0]); | ||
1014 | priv->sec_info.wpa_enabled = false; | ||
1015 | priv->sec_info.wpa2_enabled = false; | ||
1016 | } | ||
1017 | |||
1018 | return 0; | ||
1019 | } | ||
1020 | |||
1021 | /* | ||
1022 | * IOCTL request handler to set/reset WAPI IE. | ||
1023 | * | ||
1024 | * The supplied WAPI IE is treated as a opaque buffer. Only the first field | ||
1025 | * is checked to internally enable WAPI. If buffer length is zero, the existing | ||
1026 | * WAPI IE is reset. | ||
1027 | */ | ||
1028 | static int mwifiex_set_wapi_ie(struct mwifiex_private *priv, | ||
1029 | u8 *ie_data_ptr, u16 ie_len) | ||
1030 | { | ||
1031 | if (ie_len) { | ||
1032 | if (ie_len > sizeof(priv->wapi_ie)) { | ||
1033 | dev_dbg(priv->adapter->dev, | ||
1034 | "info: failed to copy WAPI IE, too big\n"); | ||
1035 | return -1; | ||
1036 | } | ||
1037 | memcpy(priv->wapi_ie, ie_data_ptr, ie_len); | ||
1038 | priv->wapi_ie_len = ie_len; | ||
1039 | dev_dbg(priv->adapter->dev, "cmd: Set wapi_ie_len=%d IE=%#x\n", | ||
1040 | priv->wapi_ie_len, priv->wapi_ie[0]); | ||
1041 | |||
1042 | if (priv->wapi_ie[0] == WLAN_EID_BSS_AC_ACCESS_DELAY) | ||
1043 | priv->sec_info.wapi_enabled = true; | ||
1044 | } else { | ||
1045 | memset(priv->wapi_ie, 0, sizeof(priv->wapi_ie)); | ||
1046 | priv->wapi_ie_len = ie_len; | ||
1047 | dev_dbg(priv->adapter->dev, | ||
1048 | "info: Reset wapi_ie_len=%d IE=%#x\n", | ||
1049 | priv->wapi_ie_len, priv->wapi_ie[0]); | ||
1050 | priv->sec_info.wapi_enabled = false; | ||
1051 | } | ||
1052 | return 0; | ||
1053 | } | ||
1054 | |||
1055 | /* | ||
1056 | * IOCTL request handler to set WAPI key. | ||
1057 | * | ||
1058 | * This function prepares the correct firmware command and | ||
1059 | * issues it. | ||
1060 | */ | ||
1061 | static int mwifiex_sec_ioctl_set_wapi_key(struct mwifiex_private *priv, | ||
1062 | struct mwifiex_ds_encrypt_key *encrypt_key) | ||
1063 | { | ||
1064 | |||
1065 | return mwifiex_send_cmd_sync(priv, HostCmd_CMD_802_11_KEY_MATERIAL, | ||
1066 | HostCmd_ACT_GEN_SET, KEY_INFO_ENABLED, | ||
1067 | encrypt_key); | ||
1068 | } | ||
1069 | |||
1070 | /* | ||
1071 | * IOCTL request handler to set WEP network key. | ||
1072 | * | ||
1073 | * This function prepares the correct firmware command and | ||
1074 | * issues it, after validation checks. | ||
1075 | */ | ||
1076 | static int mwifiex_sec_ioctl_set_wep_key(struct mwifiex_private *priv, | ||
1077 | struct mwifiex_ds_encrypt_key *encrypt_key) | ||
1078 | { | ||
1079 | int ret; | ||
1080 | struct mwifiex_wep_key *wep_key; | ||
1081 | int index; | ||
1082 | |||
1083 | if (priv->wep_key_curr_index >= NUM_WEP_KEYS) | ||
1084 | priv->wep_key_curr_index = 0; | ||
1085 | wep_key = &priv->wep_key[priv->wep_key_curr_index]; | ||
1086 | index = encrypt_key->key_index; | ||
1087 | if (encrypt_key->key_disable) { | ||
1088 | priv->sec_info.wep_status = MWIFIEX_802_11_WEP_DISABLED; | ||
1089 | } else if (!encrypt_key->key_len) { | ||
1090 | /* Copy the required key as the current key */ | ||
1091 | wep_key = &priv->wep_key[index]; | ||
1092 | if (!wep_key->key_length) { | ||
1093 | dev_err(priv->adapter->dev, | ||
1094 | "key not set, so cannot enable it\n"); | ||
1095 | return -1; | ||
1096 | } | ||
1097 | priv->wep_key_curr_index = (u16) index; | ||
1098 | priv->sec_info.wep_status = MWIFIEX_802_11_WEP_ENABLED; | ||
1099 | } else { | ||
1100 | wep_key = &priv->wep_key[index]; | ||
1101 | memset(wep_key, 0, sizeof(struct mwifiex_wep_key)); | ||
1102 | /* Copy the key in the driver */ | ||
1103 | memcpy(wep_key->key_material, | ||
1104 | encrypt_key->key_material, | ||
1105 | encrypt_key->key_len); | ||
1106 | wep_key->key_index = index; | ||
1107 | wep_key->key_length = encrypt_key->key_len; | ||
1108 | priv->sec_info.wep_status = MWIFIEX_802_11_WEP_ENABLED; | ||
1109 | } | ||
1110 | if (wep_key->key_length) { | ||
1111 | /* Send request to firmware */ | ||
1112 | ret = mwifiex_send_cmd_async(priv, | ||
1113 | HostCmd_CMD_802_11_KEY_MATERIAL, | ||
1114 | HostCmd_ACT_GEN_SET, 0, NULL); | ||
1115 | if (ret) | ||
1116 | return ret; | ||
1117 | } | ||
1118 | if (priv->sec_info.wep_status == MWIFIEX_802_11_WEP_ENABLED) | ||
1119 | priv->curr_pkt_filter |= HostCmd_ACT_MAC_WEP_ENABLE; | ||
1120 | else | ||
1121 | priv->curr_pkt_filter &= ~HostCmd_ACT_MAC_WEP_ENABLE; | ||
1122 | |||
1123 | ret = mwifiex_send_cmd_sync(priv, HostCmd_CMD_MAC_CONTROL, | ||
1124 | HostCmd_ACT_GEN_SET, 0, | ||
1125 | &priv->curr_pkt_filter); | ||
1126 | |||
1127 | return ret; | ||
1128 | } | ||
1129 | |||
1130 | /* | ||
1131 | * IOCTL request handler to set WPA key. | ||
1132 | * | ||
1133 | * This function prepares the correct firmware command and | ||
1134 | * issues it, after validation checks. | ||
1135 | * | ||
1136 | * Current driver only supports key length of up to 32 bytes. | ||
1137 | * | ||
1138 | * This function can also be used to disable a currently set key. | ||
1139 | */ | ||
1140 | static int mwifiex_sec_ioctl_set_wpa_key(struct mwifiex_private *priv, | ||
1141 | struct mwifiex_ds_encrypt_key *encrypt_key) | ||
1142 | { | ||
1143 | int ret; | ||
1144 | u8 remove_key = false; | ||
1145 | struct host_cmd_ds_802_11_key_material *ibss_key; | ||
1146 | |||
1147 | /* Current driver only supports key length of up to 32 bytes */ | ||
1148 | if (encrypt_key->key_len > WLAN_MAX_KEY_LEN) { | ||
1149 | dev_err(priv->adapter->dev, "key length too long\n"); | ||
1150 | return -1; | ||
1151 | } | ||
1152 | |||
1153 | if (priv->bss_mode == NL80211_IFTYPE_ADHOC) { | ||
1154 | /* | ||
1155 | * IBSS/WPA-None uses only one key (Group) for both receiving | ||
1156 | * and sending unicast and multicast packets. | ||
1157 | */ | ||
1158 | /* Send the key as PTK to firmware */ | ||
1159 | encrypt_key->key_index = MWIFIEX_KEY_INDEX_UNICAST; | ||
1160 | ret = mwifiex_send_cmd_async(priv, | ||
1161 | HostCmd_CMD_802_11_KEY_MATERIAL, | ||
1162 | HostCmd_ACT_GEN_SET, KEY_INFO_ENABLED, | ||
1163 | encrypt_key); | ||
1164 | if (ret) | ||
1165 | return ret; | ||
1166 | |||
1167 | ibss_key = &priv->aes_key; | ||
1168 | memset(ibss_key, 0, | ||
1169 | sizeof(struct host_cmd_ds_802_11_key_material)); | ||
1170 | /* Copy the key in the driver */ | ||
1171 | memcpy(ibss_key->key_param_set.key, encrypt_key->key_material, | ||
1172 | encrypt_key->key_len); | ||
1173 | memcpy(&ibss_key->key_param_set.key_len, &encrypt_key->key_len, | ||
1174 | sizeof(ibss_key->key_param_set.key_len)); | ||
1175 | ibss_key->key_param_set.key_type_id | ||
1176 | = cpu_to_le16(KEY_TYPE_ID_TKIP); | ||
1177 | ibss_key->key_param_set.key_info = cpu_to_le16(KEY_ENABLED); | ||
1178 | |||
1179 | /* Send the key as GTK to firmware */ | ||
1180 | encrypt_key->key_index = ~MWIFIEX_KEY_INDEX_UNICAST; | ||
1181 | } | ||
1182 | |||
1183 | if (!encrypt_key->key_index) | ||
1184 | encrypt_key->key_index = MWIFIEX_KEY_INDEX_UNICAST; | ||
1185 | |||
1186 | if (remove_key) | ||
1187 | ret = mwifiex_send_cmd_sync(priv, | ||
1188 | HostCmd_CMD_802_11_KEY_MATERIAL, | ||
1189 | HostCmd_ACT_GEN_SET, !(KEY_INFO_ENABLED), | ||
1190 | encrypt_key); | ||
1191 | else | ||
1192 | ret = mwifiex_send_cmd_sync(priv, | ||
1193 | HostCmd_CMD_802_11_KEY_MATERIAL, | ||
1194 | HostCmd_ACT_GEN_SET, KEY_INFO_ENABLED, | ||
1195 | encrypt_key); | ||
1196 | |||
1197 | return ret; | ||
1198 | } | ||
1199 | |||
1200 | /* | ||
1201 | * IOCTL request handler to set/get network keys. | ||
1202 | * | ||
1203 | * This is a generic key handling function which supports WEP, WPA | ||
1204 | * and WAPI. | ||
1205 | */ | ||
1206 | static int | ||
1207 | mwifiex_sec_ioctl_encrypt_key(struct mwifiex_private *priv, | ||
1208 | struct mwifiex_ds_encrypt_key *encrypt_key) | ||
1209 | { | ||
1210 | int status; | ||
1211 | |||
1212 | if (encrypt_key->is_wapi_key) | ||
1213 | status = mwifiex_sec_ioctl_set_wapi_key(priv, encrypt_key); | ||
1214 | else if (encrypt_key->key_len > WLAN_KEY_LEN_WEP104) | ||
1215 | status = mwifiex_sec_ioctl_set_wpa_key(priv, encrypt_key); | ||
1216 | else | ||
1217 | status = mwifiex_sec_ioctl_set_wep_key(priv, encrypt_key); | ||
1218 | return status; | ||
1219 | } | ||
1220 | |||
1221 | /* | ||
1222 | * This function returns the driver version. | ||
1223 | */ | ||
1224 | int | ||
1225 | mwifiex_drv_get_driver_version(struct mwifiex_adapter *adapter, char *version, | ||
1226 | int max_len) | ||
1227 | { | ||
1228 | union { | ||
1229 | u32 l; | ||
1230 | u8 c[4]; | ||
1231 | } ver; | ||
1232 | char fw_ver[32]; | ||
1233 | |||
1234 | ver.l = adapter->fw_release_number; | ||
1235 | sprintf(fw_ver, "%u.%u.%u.p%u", ver.c[2], ver.c[1], ver.c[0], ver.c[3]); | ||
1236 | |||
1237 | snprintf(version, max_len, driver_version, fw_ver); | ||
1238 | |||
1239 | dev_dbg(adapter->dev, "info: MWIFIEX VERSION: %s\n", version); | ||
1240 | |||
1241 | return 0; | ||
1242 | } | ||
1243 | |||
1244 | /* | ||
1245 | * Sends IOCTL request to get signal information. | ||
1246 | * | ||
1247 | * This function allocates the IOCTL request buffer, fills it | ||
1248 | * with requisite parameters and calls the IOCTL handler. | ||
1249 | */ | ||
1250 | int mwifiex_get_signal_info(struct mwifiex_private *priv, | ||
1251 | struct mwifiex_ds_get_signal *signal) | ||
1252 | { | ||
1253 | int status; | ||
1254 | |||
1255 | signal->selector = ALL_RSSI_INFO_MASK; | ||
1256 | |||
1257 | /* Signal info can be obtained only if connected */ | ||
1258 | if (!priv->media_connected) { | ||
1259 | dev_dbg(priv->adapter->dev, | ||
1260 | "info: Can not get signal in disconnected state\n"); | ||
1261 | return -1; | ||
1262 | } | ||
1263 | |||
1264 | status = mwifiex_send_cmd_sync(priv, HostCmd_CMD_RSSI_INFO, | ||
1265 | HostCmd_ACT_GEN_GET, 0, signal); | ||
1266 | |||
1267 | if (!status) { | ||
1268 | if (signal->selector & BCN_RSSI_AVG_MASK) | ||
1269 | priv->w_stats.qual.level = signal->bcn_rssi_avg; | ||
1270 | if (signal->selector & BCN_NF_AVG_MASK) | ||
1271 | priv->w_stats.qual.noise = signal->bcn_nf_avg; | ||
1272 | } | ||
1273 | |||
1274 | return status; | ||
1275 | } | ||
1276 | |||
1277 | /* | ||
1278 | * Sends IOCTL request to set encoding parameters. | ||
1279 | * | ||
1280 | * This function allocates the IOCTL request buffer, fills it | ||
1281 | * with requisite parameters and calls the IOCTL handler. | ||
1282 | */ | ||
1283 | int mwifiex_set_encode(struct mwifiex_private *priv, const u8 *key, | ||
1284 | int key_len, u8 key_index, int disable) | ||
1285 | { | ||
1286 | struct mwifiex_ds_encrypt_key encrypt_key; | ||
1287 | |||
1288 | memset(&encrypt_key, 0, sizeof(struct mwifiex_ds_encrypt_key)); | ||
1289 | encrypt_key.key_len = key_len; | ||
1290 | if (!disable) { | ||
1291 | encrypt_key.key_index = key_index; | ||
1292 | if (key_len) | ||
1293 | memcpy(encrypt_key.key_material, key, key_len); | ||
1294 | } else { | ||
1295 | encrypt_key.key_disable = true; | ||
1296 | } | ||
1297 | |||
1298 | return mwifiex_sec_ioctl_encrypt_key(priv, &encrypt_key); | ||
1299 | } | ||
1300 | |||
1301 | /* | ||
1302 | * Sends IOCTL request to get extended version. | ||
1303 | * | ||
1304 | * This function allocates the IOCTL request buffer, fills it | ||
1305 | * with requisite parameters and calls the IOCTL handler. | ||
1306 | */ | ||
1307 | int | ||
1308 | mwifiex_get_ver_ext(struct mwifiex_private *priv) | ||
1309 | { | ||
1310 | struct mwifiex_ver_ext ver_ext; | ||
1311 | |||
1312 | memset(&ver_ext, 0, sizeof(struct host_cmd_ds_version_ext)); | ||
1313 | if (mwifiex_send_cmd_sync(priv, HostCmd_CMD_VERSION_EXT, | ||
1314 | HostCmd_ACT_GEN_GET, 0, &ver_ext)) | ||
1315 | return -1; | ||
1316 | |||
1317 | return 0; | ||
1318 | } | ||
1319 | |||
1320 | /* | ||
1321 | * Sends IOCTL request to get statistics information. | ||
1322 | * | ||
1323 | * This function allocates the IOCTL request buffer, fills it | ||
1324 | * with requisite parameters and calls the IOCTL handler. | ||
1325 | */ | ||
1326 | int | ||
1327 | mwifiex_get_stats_info(struct mwifiex_private *priv, | ||
1328 | struct mwifiex_ds_get_stats *log) | ||
1329 | { | ||
1330 | int ret; | ||
1331 | |||
1332 | ret = mwifiex_send_cmd_sync(priv, HostCmd_CMD_802_11_GET_LOG, | ||
1333 | HostCmd_ACT_GEN_GET, 0, log); | ||
1334 | |||
1335 | if (!ret) { | ||
1336 | priv->w_stats.discard.fragment = log->fcs_error; | ||
1337 | priv->w_stats.discard.retries = log->retry; | ||
1338 | priv->w_stats.discard.misc = log->ack_failure; | ||
1339 | } | ||
1340 | |||
1341 | return ret; | ||
1342 | } | ||
1343 | |||
1344 | /* | ||
1345 | * IOCTL request handler to read/write register. | ||
1346 | * | ||
1347 | * This function prepares the correct firmware command and | ||
1348 | * issues it. | ||
1349 | * | ||
1350 | * Access to the following registers are supported - | ||
1351 | * - MAC | ||
1352 | * - BBP | ||
1353 | * - RF | ||
1354 | * - PMIC | ||
1355 | * - CAU | ||
1356 | */ | ||
1357 | static int mwifiex_reg_mem_ioctl_reg_rw(struct mwifiex_private *priv, | ||
1358 | struct mwifiex_ds_reg_rw *reg_rw, | ||
1359 | u16 action) | ||
1360 | { | ||
1361 | u16 cmd_no; | ||
1362 | |||
1363 | switch (le32_to_cpu(reg_rw->type)) { | ||
1364 | case MWIFIEX_REG_MAC: | ||
1365 | cmd_no = HostCmd_CMD_MAC_REG_ACCESS; | ||
1366 | break; | ||
1367 | case MWIFIEX_REG_BBP: | ||
1368 | cmd_no = HostCmd_CMD_BBP_REG_ACCESS; | ||
1369 | break; | ||
1370 | case MWIFIEX_REG_RF: | ||
1371 | cmd_no = HostCmd_CMD_RF_REG_ACCESS; | ||
1372 | break; | ||
1373 | case MWIFIEX_REG_PMIC: | ||
1374 | cmd_no = HostCmd_CMD_PMIC_REG_ACCESS; | ||
1375 | break; | ||
1376 | case MWIFIEX_REG_CAU: | ||
1377 | cmd_no = HostCmd_CMD_CAU_REG_ACCESS; | ||
1378 | break; | ||
1379 | default: | ||
1380 | return -1; | ||
1381 | } | ||
1382 | |||
1383 | return mwifiex_send_cmd_sync(priv, cmd_no, action, 0, reg_rw); | ||
1384 | |||
1385 | } | ||
1386 | |||
1387 | /* | ||
1388 | * Sends IOCTL request to write to a register. | ||
1389 | * | ||
1390 | * This function allocates the IOCTL request buffer, fills it | ||
1391 | * with requisite parameters and calls the IOCTL handler. | ||
1392 | */ | ||
1393 | int | ||
1394 | mwifiex_reg_write(struct mwifiex_private *priv, u32 reg_type, | ||
1395 | u32 reg_offset, u32 reg_value) | ||
1396 | { | ||
1397 | struct mwifiex_ds_reg_rw reg_rw; | ||
1398 | |||
1399 | reg_rw.type = cpu_to_le32(reg_type); | ||
1400 | reg_rw.offset = cpu_to_le32(reg_offset); | ||
1401 | reg_rw.value = cpu_to_le32(reg_value); | ||
1402 | |||
1403 | return mwifiex_reg_mem_ioctl_reg_rw(priv, ®_rw, HostCmd_ACT_GEN_SET); | ||
1404 | } | ||
1405 | |||
1406 | /* | ||
1407 | * Sends IOCTL request to read from a register. | ||
1408 | * | ||
1409 | * This function allocates the IOCTL request buffer, fills it | ||
1410 | * with requisite parameters and calls the IOCTL handler. | ||
1411 | */ | ||
1412 | int | ||
1413 | mwifiex_reg_read(struct mwifiex_private *priv, u32 reg_type, | ||
1414 | u32 reg_offset, u32 *value) | ||
1415 | { | ||
1416 | int ret; | ||
1417 | struct mwifiex_ds_reg_rw reg_rw; | ||
1418 | |||
1419 | reg_rw.type = cpu_to_le32(reg_type); | ||
1420 | reg_rw.offset = cpu_to_le32(reg_offset); | ||
1421 | ret = mwifiex_reg_mem_ioctl_reg_rw(priv, ®_rw, HostCmd_ACT_GEN_GET); | ||
1422 | |||
1423 | if (ret) | ||
1424 | goto done; | ||
1425 | |||
1426 | *value = le32_to_cpu(reg_rw.value); | ||
1427 | |||
1428 | done: | ||
1429 | return ret; | ||
1430 | } | ||
1431 | |||
1432 | /* | ||
1433 | * Sends IOCTL request to read from EEPROM. | ||
1434 | * | ||
1435 | * This function allocates the IOCTL request buffer, fills it | ||
1436 | * with requisite parameters and calls the IOCTL handler. | ||
1437 | */ | ||
1438 | int | ||
1439 | mwifiex_eeprom_read(struct mwifiex_private *priv, u16 offset, u16 bytes, | ||
1440 | u8 *value) | ||
1441 | { | ||
1442 | int ret; | ||
1443 | struct mwifiex_ds_read_eeprom rd_eeprom; | ||
1444 | |||
1445 | rd_eeprom.offset = cpu_to_le16((u16) offset); | ||
1446 | rd_eeprom.byte_count = cpu_to_le16((u16) bytes); | ||
1447 | |||
1448 | /* Send request to firmware */ | ||
1449 | ret = mwifiex_send_cmd_sync(priv, HostCmd_CMD_802_11_EEPROM_ACCESS, | ||
1450 | HostCmd_ACT_GEN_GET, 0, &rd_eeprom); | ||
1451 | |||
1452 | if (!ret) | ||
1453 | memcpy(value, rd_eeprom.value, MAX_EEPROM_DATA); | ||
1454 | return ret; | ||
1455 | } | ||
1456 | |||
1457 | /* | ||
1458 | * This function sets a generic IE. In addition to generic IE, it can | ||
1459 | * also handle WPA, WPA2 and WAPI IEs. | ||
1460 | */ | ||
1461 | static int | ||
1462 | mwifiex_set_gen_ie_helper(struct mwifiex_private *priv, u8 *ie_data_ptr, | ||
1463 | u16 ie_len) | ||
1464 | { | ||
1465 | int ret = 0; | ||
1466 | struct ieee_types_vendor_header *pvendor_ie; | ||
1467 | const u8 wpa_oui[] = { 0x00, 0x50, 0xf2, 0x01 }; | ||
1468 | const u8 wps_oui[] = { 0x00, 0x50, 0xf2, 0x04 }; | ||
1469 | |||
1470 | /* If the passed length is zero, reset the buffer */ | ||
1471 | if (!ie_len) { | ||
1472 | priv->gen_ie_buf_len = 0; | ||
1473 | priv->wps.session_enable = false; | ||
1474 | |||
1475 | return 0; | ||
1476 | } else if (!ie_data_ptr) { | ||
1477 | return -1; | ||
1478 | } | ||
1479 | pvendor_ie = (struct ieee_types_vendor_header *) ie_data_ptr; | ||
1480 | /* Test to see if it is a WPA IE, if not, then it is a gen IE */ | ||
1481 | if (((pvendor_ie->element_id == WLAN_EID_WPA) | ||
1482 | && (!memcmp(pvendor_ie->oui, wpa_oui, sizeof(wpa_oui)))) | ||
1483 | || (pvendor_ie->element_id == WLAN_EID_RSN)) { | ||
1484 | |||
1485 | /* IE is a WPA/WPA2 IE so call set_wpa function */ | ||
1486 | ret = mwifiex_set_wpa_ie_helper(priv, ie_data_ptr, ie_len); | ||
1487 | priv->wps.session_enable = false; | ||
1488 | |||
1489 | return ret; | ||
1490 | } else if (pvendor_ie->element_id == WLAN_EID_BSS_AC_ACCESS_DELAY) { | ||
1491 | /* IE is a WAPI IE so call set_wapi function */ | ||
1492 | ret = mwifiex_set_wapi_ie(priv, ie_data_ptr, ie_len); | ||
1493 | |||
1494 | return ret; | ||
1495 | } | ||
1496 | /* | ||
1497 | * Verify that the passed length is not larger than the | ||
1498 | * available space remaining in the buffer | ||
1499 | */ | ||
1500 | if (ie_len < (sizeof(priv->gen_ie_buf) - priv->gen_ie_buf_len)) { | ||
1501 | |||
1502 | /* Test to see if it is a WPS IE, if so, enable | ||
1503 | * wps session flag | ||
1504 | */ | ||
1505 | pvendor_ie = (struct ieee_types_vendor_header *) ie_data_ptr; | ||
1506 | if ((pvendor_ie->element_id == WLAN_EID_VENDOR_SPECIFIC) | ||
1507 | && (!memcmp(pvendor_ie->oui, wps_oui, | ||
1508 | sizeof(wps_oui)))) { | ||
1509 | priv->wps.session_enable = true; | ||
1510 | dev_dbg(priv->adapter->dev, | ||
1511 | "info: WPS Session Enabled.\n"); | ||
1512 | } | ||
1513 | |||
1514 | /* Append the passed data to the end of the | ||
1515 | genIeBuffer */ | ||
1516 | memcpy(priv->gen_ie_buf + priv->gen_ie_buf_len, ie_data_ptr, | ||
1517 | ie_len); | ||
1518 | /* Increment the stored buffer length by the | ||
1519 | size passed */ | ||
1520 | priv->gen_ie_buf_len += ie_len; | ||
1521 | } else { | ||
1522 | /* Passed data does not fit in the remaining | ||
1523 | buffer space */ | ||
1524 | ret = -1; | ||
1525 | } | ||
1526 | |||
1527 | /* Return 0, or -1 for error case */ | ||
1528 | return ret; | ||
1529 | } | ||
1530 | |||
1531 | /* | ||
1532 | * IOCTL request handler to set/get generic IE. | ||
1533 | * | ||
1534 | * In addition to various generic IEs, this function can also be | ||
1535 | * used to set the ARP filter. | ||
1536 | */ | ||
1537 | static int mwifiex_misc_ioctl_gen_ie(struct mwifiex_private *priv, | ||
1538 | struct mwifiex_ds_misc_gen_ie *gen_ie, | ||
1539 | u16 action) | ||
1540 | { | ||
1541 | struct mwifiex_adapter *adapter = priv->adapter; | ||
1542 | |||
1543 | switch (gen_ie->type) { | ||
1544 | case MWIFIEX_IE_TYPE_GEN_IE: | ||
1545 | if (action == HostCmd_ACT_GEN_GET) { | ||
1546 | gen_ie->len = priv->wpa_ie_len; | ||
1547 | memcpy(gen_ie->ie_data, priv->wpa_ie, gen_ie->len); | ||
1548 | } else { | ||
1549 | mwifiex_set_gen_ie_helper(priv, gen_ie->ie_data, | ||
1550 | (u16) gen_ie->len); | ||
1551 | } | ||
1552 | break; | ||
1553 | case MWIFIEX_IE_TYPE_ARP_FILTER: | ||
1554 | memset(adapter->arp_filter, 0, sizeof(adapter->arp_filter)); | ||
1555 | if (gen_ie->len > ARP_FILTER_MAX_BUF_SIZE) { | ||
1556 | adapter->arp_filter_size = 0; | ||
1557 | dev_err(adapter->dev, "invalid ARP filter size\n"); | ||
1558 | return -1; | ||
1559 | } else { | ||
1560 | memcpy(adapter->arp_filter, gen_ie->ie_data, | ||
1561 | gen_ie->len); | ||
1562 | adapter->arp_filter_size = gen_ie->len; | ||
1563 | } | ||
1564 | break; | ||
1565 | default: | ||
1566 | dev_err(adapter->dev, "invalid IE type\n"); | ||
1567 | return -1; | ||
1568 | } | ||
1569 | return 0; | ||
1570 | } | ||
1571 | |||
1572 | /* | ||
1573 | * Sends IOCTL request to set a generic IE. | ||
1574 | * | ||
1575 | * This function allocates the IOCTL request buffer, fills it | ||
1576 | * with requisite parameters and calls the IOCTL handler. | ||
1577 | */ | ||
1578 | int | ||
1579 | mwifiex_set_gen_ie(struct mwifiex_private *priv, u8 *ie, int ie_len) | ||
1580 | { | ||
1581 | struct mwifiex_ds_misc_gen_ie gen_ie; | ||
1582 | |||
1583 | if (ie_len > IW_CUSTOM_MAX) | ||
1584 | return -EFAULT; | ||
1585 | |||
1586 | gen_ie.type = MWIFIEX_IE_TYPE_GEN_IE; | ||
1587 | gen_ie.len = ie_len; | ||
1588 | memcpy(gen_ie.ie_data, ie, ie_len); | ||
1589 | if (mwifiex_misc_ioctl_gen_ie(priv, &gen_ie, HostCmd_ACT_GEN_SET)) | ||
1590 | return -EFAULT; | ||
1591 | |||
1592 | return 0; | ||
1593 | } | ||
diff --git a/drivers/net/wireless/mwifiex/sta_rx.c b/drivers/net/wireless/mwifiex/sta_rx.c new file mode 100644 index 000000000000..1fdddece7479 --- /dev/null +++ b/drivers/net/wireless/mwifiex/sta_rx.c | |||
@@ -0,0 +1,200 @@ | |||
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; | ||
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; | ||
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 | |||
145 | if (local_rx_pd->rx_pkt_type == PKT_TYPE_AMSDU) { | ||
146 | struct sk_buff_head list; | ||
147 | struct sk_buff *rx_skb; | ||
148 | |||
149 | __skb_queue_head_init(&list); | ||
150 | |||
151 | skb_pull(skb, local_rx_pd->rx_pkt_offset); | ||
152 | skb_trim(skb, local_rx_pd->rx_pkt_length); | ||
153 | |||
154 | ieee80211_amsdu_to_8023s(skb, &list, priv->curr_addr, | ||
155 | priv->wdev->iftype, 0, false); | ||
156 | |||
157 | while (!skb_queue_empty(&list)) { | ||
158 | rx_skb = __skb_dequeue(&list); | ||
159 | ret = mwifiex_recv_packet(adapter, rx_skb); | ||
160 | if (ret == -1) | ||
161 | dev_err(adapter->dev, "Rx of A-MSDU failed"); | ||
162 | } | ||
163 | return 0; | ||
164 | } | ||
165 | |||
166 | /* | ||
167 | * If the packet is not an unicast packet then send the packet | ||
168 | * directly to os. Don't pass thru rx reordering | ||
169 | */ | ||
170 | if (!IS_11N_ENABLED(priv) || | ||
171 | memcmp(priv->curr_addr, rx_pkt_hdr->eth803_hdr.h_dest, ETH_ALEN)) { | ||
172 | mwifiex_process_rx_packet(adapter, skb); | ||
173 | return ret; | ||
174 | } | ||
175 | |||
176 | if (mwifiex_queuing_ra_based(priv)) { | ||
177 | memcpy(ta, rx_pkt_hdr->eth803_hdr.h_source, ETH_ALEN); | ||
178 | } else { | ||
179 | if (rx_pkt_type != PKT_TYPE_BAR) | ||
180 | priv->rx_seq[local_rx_pd->priority] = | ||
181 | local_rx_pd->seq_num; | ||
182 | memcpy(ta, priv->curr_bss_params.bss_descriptor.mac_address, | ||
183 | ETH_ALEN); | ||
184 | } | ||
185 | |||
186 | /* Reorder and send to OS */ | ||
187 | ret = mwifiex_11n_rx_reorder_pkt(priv, local_rx_pd->seq_num, | ||
188 | local_rx_pd->priority, ta, | ||
189 | (u8) local_rx_pd->rx_pkt_type, | ||
190 | (void *) skb); | ||
191 | |||
192 | if (ret || (rx_pkt_type == PKT_TYPE_BAR)) { | ||
193 | if (priv && (ret == -1)) | ||
194 | priv->stats.rx_dropped++; | ||
195 | |||
196 | dev_kfree_skb_any(skb); | ||
197 | } | ||
198 | |||
199 | return ret; | ||
200 | } | ||
diff --git a/drivers/net/wireless/mwifiex/sta_tx.c b/drivers/net/wireless/mwifiex/sta_tx.c new file mode 100644 index 000000000000..fa6221bc9104 --- /dev/null +++ b/drivers/net/wireless/mwifiex/sta_tx.c | |||
@@ -0,0 +1,198 @@ | |||
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 = -1; | ||
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; | ||
117 | int ret; | ||
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 | |||
184 | if (!adapter->sleep_period.period) | ||
185 | return ret; | ||
186 | if (mwifiex_wmm_lists_empty(adapter)) | ||
187 | ret = true; | ||
188 | |||
189 | if (ret && !adapter->cmd_sent && !adapter->curr_cmd | ||
190 | && !is_command_pending(adapter)) { | ||
191 | adapter->delay_null_pkt = false; | ||
192 | ret = true; | ||
193 | } else { | ||
194 | ret = false; | ||
195 | adapter->delay_null_pkt = true; | ||
196 | } | ||
197 | return ret; | ||
198 | } | ||
diff --git a/drivers/net/wireless/mwifiex/txrx.c b/drivers/net/wireless/mwifiex/txrx.c new file mode 100644 index 000000000000..aaa50c074196 --- /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 | struct mwifiex_private *priv = | ||
40 | mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY); | ||
41 | struct rxpd *local_rx_pd; | ||
42 | struct mwifiex_rxinfo *rx_info = MWIFIEX_SKB_RXCB(skb); | ||
43 | |||
44 | local_rx_pd = (struct rxpd *) (skb->data); | ||
45 | /* Get the BSS number from rxpd, get corresponding priv */ | ||
46 | priv = mwifiex_get_priv_by_id(adapter, local_rx_pd->bss_num & | ||
47 | BSS_NUM_MASK, local_rx_pd->bss_type); | ||
48 | if (!priv) | ||
49 | priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY); | ||
50 | |||
51 | rx_info->bss_index = priv->bss_index; | ||
52 | |||
53 | return mwifiex_process_sta_rx_packet(adapter, skb); | ||
54 | } | ||
55 | EXPORT_SYMBOL_GPL(mwifiex_handle_rx_packet); | ||
56 | |||
57 | /* | ||
58 | * This function sends a packet to device. | ||
59 | * | ||
60 | * It processes the packet to add the TxPD, checks condition and | ||
61 | * sends the processed packet to firmware for transmission. | ||
62 | * | ||
63 | * On successful completion, the function calls the completion callback | ||
64 | * and logs the time. | ||
65 | */ | ||
66 | int mwifiex_process_tx(struct mwifiex_private *priv, struct sk_buff *skb, | ||
67 | struct mwifiex_tx_param *tx_param) | ||
68 | { | ||
69 | int ret = -1; | ||
70 | struct mwifiex_adapter *adapter = priv->adapter; | ||
71 | u8 *head_ptr; | ||
72 | struct txpd *local_tx_pd = NULL; | ||
73 | |||
74 | head_ptr = (u8 *) mwifiex_process_sta_txpd(priv, skb); | ||
75 | if (head_ptr) { | ||
76 | if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA) | ||
77 | local_tx_pd = | ||
78 | (struct txpd *) (head_ptr + INTF_HEADER_LEN); | ||
79 | |||
80 | ret = adapter->if_ops.host_to_card(adapter, MWIFIEX_TYPE_DATA, | ||
81 | skb->data, skb->len, tx_param); | ||
82 | } | ||
83 | |||
84 | switch (ret) { | ||
85 | case -EBUSY: | ||
86 | if ((GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA) && | ||
87 | (adapter->pps_uapsd_mode) && | ||
88 | (adapter->tx_lock_flag)) { | ||
89 | priv->adapter->tx_lock_flag = false; | ||
90 | local_tx_pd->flags = 0; | ||
91 | } | ||
92 | dev_dbg(adapter->dev, "data: -EBUSY is returned\n"); | ||
93 | break; | ||
94 | case -1: | ||
95 | adapter->data_sent = false; | ||
96 | dev_err(adapter->dev, "mwifiex_write_data_async failed: 0x%X\n", | ||
97 | ret); | ||
98 | adapter->dbg.num_tx_host_to_card_failure++; | ||
99 | mwifiex_write_data_complete(adapter, skb, ret); | ||
100 | break; | ||
101 | case -EINPROGRESS: | ||
102 | adapter->data_sent = false; | ||
103 | break; | ||
104 | case 0: | ||
105 | mwifiex_write_data_complete(adapter, skb, ret); | ||
106 | break; | ||
107 | default: | ||
108 | break; | ||
109 | } | ||
110 | |||
111 | return ret; | ||
112 | } | ||
113 | |||
114 | /* | ||
115 | * Packet send completion callback handler. | ||
116 | * | ||
117 | * It either frees the buffer directly or forwards it to another | ||
118 | * completion callback which checks conditions, updates statistics, | ||
119 | * wakes up stalled traffic queue if required, and then frees the buffer. | ||
120 | */ | ||
121 | int mwifiex_write_data_complete(struct mwifiex_adapter *adapter, | ||
122 | struct sk_buff *skb, int status) | ||
123 | { | ||
124 | struct mwifiex_private *priv, *tpriv; | ||
125 | struct mwifiex_txinfo *tx_info; | ||
126 | int i; | ||
127 | |||
128 | if (!skb) | ||
129 | return 0; | ||
130 | |||
131 | tx_info = MWIFIEX_SKB_TXCB(skb); | ||
132 | priv = mwifiex_bss_index_to_priv(adapter, tx_info->bss_index); | ||
133 | if (!priv) | ||
134 | goto done; | ||
135 | |||
136 | priv->netdev->trans_start = jiffies; | ||
137 | if (!status) { | ||
138 | priv->stats.tx_packets++; | ||
139 | priv->stats.tx_bytes += skb->len; | ||
140 | } else { | ||
141 | priv->stats.tx_errors++; | ||
142 | } | ||
143 | |||
144 | if (atomic_dec_return(&adapter->tx_pending) >= LOW_TX_PENDING) | ||
145 | goto done; | ||
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; | ||
175 | struct mwifiex_private *priv; | ||
176 | struct sk_buff *skb_parent; | ||
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..d41291529bc0 --- /dev/null +++ b/drivers/net/wireless/mwifiex/util.c | |||
@@ -0,0 +1,202 @@ | |||
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 | * This function sends init/shutdown command | ||
59 | * to firmware. | ||
60 | */ | ||
61 | int mwifiex_init_shutdown_fw(struct mwifiex_private *priv, | ||
62 | u32 func_init_shutdown) | ||
63 | { | ||
64 | u16 cmd; | ||
65 | |||
66 | if (func_init_shutdown == MWIFIEX_FUNC_INIT) { | ||
67 | cmd = HostCmd_CMD_FUNC_INIT; | ||
68 | } else if (func_init_shutdown == MWIFIEX_FUNC_SHUTDOWN) { | ||
69 | cmd = HostCmd_CMD_FUNC_SHUTDOWN; | ||
70 | } else { | ||
71 | dev_err(priv->adapter->dev, "unsupported parameter\n"); | ||
72 | return -1; | ||
73 | } | ||
74 | |||
75 | return mwifiex_send_cmd_sync(priv, cmd, HostCmd_ACT_GEN_SET, 0, NULL); | ||
76 | } | ||
77 | EXPORT_SYMBOL_GPL(mwifiex_init_shutdown_fw); | ||
78 | |||
79 | /* | ||
80 | * IOCTL request handler to set/get debug information. | ||
81 | * | ||
82 | * This function collates/sets the information from/to different driver | ||
83 | * structures. | ||
84 | */ | ||
85 | int mwifiex_get_debug_info(struct mwifiex_private *priv, | ||
86 | struct mwifiex_debug_info *info) | ||
87 | { | ||
88 | struct mwifiex_adapter *adapter = priv->adapter; | ||
89 | |||
90 | if (info) { | ||
91 | memcpy(info->packets_out, | ||
92 | priv->wmm.packets_out, | ||
93 | sizeof(priv->wmm.packets_out)); | ||
94 | info->max_tx_buf_size = (u32) adapter->max_tx_buf_size; | ||
95 | info->tx_buf_size = (u32) adapter->tx_buf_size; | ||
96 | info->rx_tbl_num = mwifiex_get_rx_reorder_tbl( | ||
97 | priv, info->rx_tbl); | ||
98 | info->tx_tbl_num = mwifiex_get_tx_ba_stream_tbl( | ||
99 | priv, info->tx_tbl); | ||
100 | info->ps_mode = adapter->ps_mode; | ||
101 | info->ps_state = adapter->ps_state; | ||
102 | info->is_deep_sleep = adapter->is_deep_sleep; | ||
103 | info->pm_wakeup_card_req = adapter->pm_wakeup_card_req; | ||
104 | info->pm_wakeup_fw_try = adapter->pm_wakeup_fw_try; | ||
105 | info->is_hs_configured = adapter->is_hs_configured; | ||
106 | info->hs_activated = adapter->hs_activated; | ||
107 | info->num_cmd_host_to_card_failure | ||
108 | = adapter->dbg.num_cmd_host_to_card_failure; | ||
109 | info->num_cmd_sleep_cfm_host_to_card_failure | ||
110 | = adapter->dbg.num_cmd_sleep_cfm_host_to_card_failure; | ||
111 | info->num_tx_host_to_card_failure | ||
112 | = adapter->dbg.num_tx_host_to_card_failure; | ||
113 | info->num_event_deauth = adapter->dbg.num_event_deauth; | ||
114 | info->num_event_disassoc = adapter->dbg.num_event_disassoc; | ||
115 | info->num_event_link_lost = adapter->dbg.num_event_link_lost; | ||
116 | info->num_cmd_deauth = adapter->dbg.num_cmd_deauth; | ||
117 | info->num_cmd_assoc_success = | ||
118 | adapter->dbg.num_cmd_assoc_success; | ||
119 | info->num_cmd_assoc_failure = | ||
120 | adapter->dbg.num_cmd_assoc_failure; | ||
121 | info->num_tx_timeout = adapter->dbg.num_tx_timeout; | ||
122 | info->num_cmd_timeout = adapter->dbg.num_cmd_timeout; | ||
123 | info->timeout_cmd_id = adapter->dbg.timeout_cmd_id; | ||
124 | info->timeout_cmd_act = adapter->dbg.timeout_cmd_act; | ||
125 | memcpy(info->last_cmd_id, adapter->dbg.last_cmd_id, | ||
126 | sizeof(adapter->dbg.last_cmd_id)); | ||
127 | memcpy(info->last_cmd_act, adapter->dbg.last_cmd_act, | ||
128 | sizeof(adapter->dbg.last_cmd_act)); | ||
129 | info->last_cmd_index = adapter->dbg.last_cmd_index; | ||
130 | memcpy(info->last_cmd_resp_id, adapter->dbg.last_cmd_resp_id, | ||
131 | sizeof(adapter->dbg.last_cmd_resp_id)); | ||
132 | info->last_cmd_resp_index = adapter->dbg.last_cmd_resp_index; | ||
133 | memcpy(info->last_event, adapter->dbg.last_event, | ||
134 | sizeof(adapter->dbg.last_event)); | ||
135 | info->last_event_index = adapter->dbg.last_event_index; | ||
136 | info->data_sent = adapter->data_sent; | ||
137 | info->cmd_sent = adapter->cmd_sent; | ||
138 | info->cmd_resp_received = adapter->cmd_resp_received; | ||
139 | } | ||
140 | |||
141 | return 0; | ||
142 | } | ||
143 | |||
144 | /* | ||
145 | * This function processes the received packet before sending it to the | ||
146 | * kernel. | ||
147 | * | ||
148 | * It extracts the SKB from the received buffer and sends it to kernel. | ||
149 | * In case the received buffer does not contain the data in SKB format, | ||
150 | * the function creates a blank SKB, fills it with the data from the | ||
151 | * received buffer and then sends this new SKB to the kernel. | ||
152 | */ | ||
153 | int mwifiex_recv_packet(struct mwifiex_adapter *adapter, struct sk_buff *skb) | ||
154 | { | ||
155 | struct mwifiex_rxinfo *rx_info; | ||
156 | struct mwifiex_private *priv; | ||
157 | |||
158 | if (!skb) | ||
159 | return -1; | ||
160 | |||
161 | rx_info = MWIFIEX_SKB_RXCB(skb); | ||
162 | priv = mwifiex_bss_index_to_priv(adapter, rx_info->bss_index); | ||
163 | if (!priv) | ||
164 | return -1; | ||
165 | |||
166 | skb->dev = priv->netdev; | ||
167 | skb->protocol = eth_type_trans(skb, priv->netdev); | ||
168 | skb->ip_summed = CHECKSUM_NONE; | ||
169 | priv->stats.rx_bytes += skb->len; | ||
170 | priv->stats.rx_packets++; | ||
171 | if (in_interrupt()) | ||
172 | netif_rx(skb); | ||
173 | else | ||
174 | netif_rx_ni(skb); | ||
175 | |||
176 | return 0; | ||
177 | } | ||
178 | |||
179 | /* | ||
180 | * IOCTL completion callback handler. | ||
181 | * | ||
182 | * This function is called when a pending IOCTL is completed. | ||
183 | * | ||
184 | * If work queue support is enabled, the function wakes up the | ||
185 | * corresponding waiting function. Otherwise, it processes the | ||
186 | * IOCTL response and frees the response buffer. | ||
187 | */ | ||
188 | int mwifiex_complete_cmd(struct mwifiex_adapter *adapter) | ||
189 | { | ||
190 | atomic_dec(&adapter->cmd_pending); | ||
191 | dev_dbg(adapter->dev, "cmd completed: status=%d\n", | ||
192 | adapter->cmd_wait_q.status); | ||
193 | |||
194 | adapter->cmd_wait_q.condition = true; | ||
195 | |||
196 | if (adapter->cmd_wait_q.status == -ETIMEDOUT) | ||
197 | dev_err(adapter->dev, "cmd timeout\n"); | ||
198 | else | ||
199 | wake_up_interruptible(&adapter->cmd_wait_q.wait); | ||
200 | |||
201 | return 0; | ||
202 | } | ||
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..91634daec306 --- /dev/null +++ b/drivers/net/wireless/mwifiex/wmm.c | |||
@@ -0,0 +1,1264 @@ | |||
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_wmm_desc *wmm) | ||
181 | { | ||
182 | u8 *queue_priority = wmm->queue_priority; | ||
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 | for (i = 0; i < MAX_NUM_TID; ++i) | ||
191 | tos_to_tid_inv[tos_to_tid[i]] = (u8)i; | ||
192 | |||
193 | atomic_set(&wmm->highest_queued_prio, HIGH_PRIO_TID); | ||
194 | } | ||
195 | |||
196 | /* | ||
197 | * This function initializes WMM priority queues. | ||
198 | */ | ||
199 | void | ||
200 | mwifiex_wmm_setup_queue_priorities(struct mwifiex_private *priv, | ||
201 | struct ieee_types_wmm_parameter *wmm_ie) | ||
202 | { | ||
203 | u16 cw_min, avg_back_off, tmp[4]; | ||
204 | u32 i, j, num_ac; | ||
205 | u8 ac_idx; | ||
206 | |||
207 | if (!wmm_ie || !priv->wmm_enabled) { | ||
208 | /* WMM is not enabled, just set the defaults and return */ | ||
209 | mwifiex_wmm_default_queue_priorities(priv); | ||
210 | return; | ||
211 | } | ||
212 | |||
213 | dev_dbg(priv->adapter->dev, "info: WMM Parameter IE: version=%d, " | ||
214 | "qos_info Parameter Set Count=%d, Reserved=%#x\n", | ||
215 | wmm_ie->vend_hdr.version, wmm_ie->qos_info_bitmap & | ||
216 | IEEE80211_WMM_IE_AP_QOSINFO_PARAM_SET_CNT_MASK, | ||
217 | wmm_ie->reserved); | ||
218 | |||
219 | for (num_ac = 0; num_ac < ARRAY_SIZE(wmm_ie->ac_params); num_ac++) { | ||
220 | cw_min = (1 << (wmm_ie->ac_params[num_ac].ecw_bitmap & | ||
221 | MWIFIEX_ECW_MIN)) - 1; | ||
222 | avg_back_off = (cw_min >> 1) + | ||
223 | (wmm_ie->ac_params[num_ac].aci_aifsn_bitmap & | ||
224 | MWIFIEX_AIFSN); | ||
225 | |||
226 | ac_idx = wmm_aci_to_qidx_map[(wmm_ie->ac_params[num_ac]. | ||
227 | aci_aifsn_bitmap & | ||
228 | MWIFIEX_ACI) >> 5]; | ||
229 | priv->wmm.queue_priority[ac_idx] = ac_idx; | ||
230 | tmp[ac_idx] = avg_back_off; | ||
231 | |||
232 | dev_dbg(priv->adapter->dev, "info: WMM: CWmax=%d CWmin=%d Avg Back-off=%d\n", | ||
233 | (1 << ((wmm_ie->ac_params[num_ac].ecw_bitmap & | ||
234 | MWIFIEX_ECW_MAX) >> 4)) - 1, | ||
235 | cw_min, avg_back_off); | ||
236 | mwifiex_wmm_ac_debug_print(&wmm_ie->ac_params[num_ac]); | ||
237 | } | ||
238 | |||
239 | /* Bubble sort */ | ||
240 | for (i = 0; i < num_ac; i++) { | ||
241 | for (j = 1; j < num_ac - i; j++) { | ||
242 | if (tmp[j - 1] > tmp[j]) { | ||
243 | swap(tmp[j - 1], tmp[j]); | ||
244 | swap(priv->wmm.queue_priority[j - 1], | ||
245 | priv->wmm.queue_priority[j]); | ||
246 | } else if (tmp[j - 1] == tmp[j]) { | ||
247 | if (priv->wmm.queue_priority[j - 1] | ||
248 | < priv->wmm.queue_priority[j]) | ||
249 | swap(priv->wmm.queue_priority[j - 1], | ||
250 | priv->wmm.queue_priority[j]); | ||
251 | } | ||
252 | } | ||
253 | } | ||
254 | |||
255 | mwifiex_wmm_queue_priorities_tid(&priv->wmm); | ||
256 | } | ||
257 | |||
258 | /* | ||
259 | * This function evaluates whether or not an AC is to be downgraded. | ||
260 | * | ||
261 | * In case the AC is not enabled, the highest AC is returned that is | ||
262 | * enabled and does not require admission control. | ||
263 | */ | ||
264 | static enum mwifiex_wmm_ac_e | ||
265 | mwifiex_wmm_eval_downgrade_ac(struct mwifiex_private *priv, | ||
266 | enum mwifiex_wmm_ac_e eval_ac) | ||
267 | { | ||
268 | int down_ac; | ||
269 | enum mwifiex_wmm_ac_e ret_ac; | ||
270 | struct mwifiex_wmm_ac_status *ac_status; | ||
271 | |||
272 | ac_status = &priv->wmm.ac_status[eval_ac]; | ||
273 | |||
274 | if (!ac_status->disabled) | ||
275 | /* Okay to use this AC, its enabled */ | ||
276 | return eval_ac; | ||
277 | |||
278 | /* Setup a default return value of the lowest priority */ | ||
279 | ret_ac = WMM_AC_BK; | ||
280 | |||
281 | /* | ||
282 | * Find the highest AC that is enabled and does not require | ||
283 | * admission control. The spec disallows downgrading to an AC, | ||
284 | * which is enabled due to a completed admission control. | ||
285 | * Unadmitted traffic is not to be sent on an AC with admitted | ||
286 | * traffic. | ||
287 | */ | ||
288 | for (down_ac = WMM_AC_BK; down_ac < eval_ac; down_ac++) { | ||
289 | ac_status = &priv->wmm.ac_status[down_ac]; | ||
290 | |||
291 | if (!ac_status->disabled && !ac_status->flow_required) | ||
292 | /* AC is enabled and does not require admission | ||
293 | control */ | ||
294 | ret_ac = (enum mwifiex_wmm_ac_e) down_ac; | ||
295 | } | ||
296 | |||
297 | return ret_ac; | ||
298 | } | ||
299 | |||
300 | /* | ||
301 | * This function downgrades WMM priority queue. | ||
302 | */ | ||
303 | void | ||
304 | mwifiex_wmm_setup_ac_downgrade(struct mwifiex_private *priv) | ||
305 | { | ||
306 | int ac_val; | ||
307 | |||
308 | dev_dbg(priv->adapter->dev, "info: WMM: AC Priorities:" | ||
309 | "BK(0), BE(1), VI(2), VO(3)\n"); | ||
310 | |||
311 | if (!priv->wmm_enabled) { | ||
312 | /* WMM is not enabled, default priorities */ | ||
313 | for (ac_val = WMM_AC_BK; ac_val <= WMM_AC_VO; ac_val++) | ||
314 | priv->wmm.ac_down_graded_vals[ac_val] = | ||
315 | (enum mwifiex_wmm_ac_e) ac_val; | ||
316 | } else { | ||
317 | for (ac_val = WMM_AC_BK; ac_val <= WMM_AC_VO; ac_val++) { | ||
318 | priv->wmm.ac_down_graded_vals[ac_val] | ||
319 | = mwifiex_wmm_eval_downgrade_ac(priv, | ||
320 | (enum mwifiex_wmm_ac_e) ac_val); | ||
321 | dev_dbg(priv->adapter->dev, "info: WMM: AC PRIO %d maps to %d\n", | ||
322 | ac_val, priv->wmm.ac_down_graded_vals[ac_val]); | ||
323 | } | ||
324 | } | ||
325 | } | ||
326 | |||
327 | /* | ||
328 | * This function converts the IP TOS field to an WMM AC | ||
329 | * Queue assignment. | ||
330 | */ | ||
331 | static enum mwifiex_wmm_ac_e | ||
332 | mwifiex_wmm_convert_tos_to_ac(struct mwifiex_adapter *adapter, u32 tos) | ||
333 | { | ||
334 | /* Map of TOS UP values to WMM AC */ | ||
335 | const enum mwifiex_wmm_ac_e tos_to_ac[] = { WMM_AC_BE, | ||
336 | WMM_AC_BK, | ||
337 | WMM_AC_BK, | ||
338 | WMM_AC_BE, | ||
339 | WMM_AC_VI, | ||
340 | WMM_AC_VI, | ||
341 | WMM_AC_VO, | ||
342 | WMM_AC_VO | ||
343 | }; | ||
344 | |||
345 | if (tos >= ARRAY_SIZE(tos_to_ac)) | ||
346 | return WMM_AC_BE; | ||
347 | |||
348 | return tos_to_ac[tos]; | ||
349 | } | ||
350 | |||
351 | /* | ||
352 | * This function evaluates a given TID and downgrades it to a lower | ||
353 | * TID if the WMM Parameter IE received from the AP indicates that the | ||
354 | * AP is disabled (due to call admission control (ACM bit). Mapping | ||
355 | * of TID to AC is taken care of internally. | ||
356 | */ | ||
357 | static u8 | ||
358 | mwifiex_wmm_downgrade_tid(struct mwifiex_private *priv, u32 tid) | ||
359 | { | ||
360 | enum mwifiex_wmm_ac_e ac, ac_down; | ||
361 | u8 new_tid; | ||
362 | |||
363 | ac = mwifiex_wmm_convert_tos_to_ac(priv->adapter, tid); | ||
364 | ac_down = priv->wmm.ac_down_graded_vals[ac]; | ||
365 | |||
366 | /* Send the index to tid array, picking from the array will be | ||
367 | * taken care by dequeuing function | ||
368 | */ | ||
369 | new_tid = ac_to_tid[ac_down][tid % 2]; | ||
370 | |||
371 | return new_tid; | ||
372 | } | ||
373 | |||
374 | /* | ||
375 | * This function initializes the WMM state information and the | ||
376 | * WMM data path queues. | ||
377 | */ | ||
378 | void | ||
379 | mwifiex_wmm_init(struct mwifiex_adapter *adapter) | ||
380 | { | ||
381 | int i, j; | ||
382 | struct mwifiex_private *priv; | ||
383 | |||
384 | for (j = 0; j < adapter->priv_num; ++j) { | ||
385 | priv = adapter->priv[j]; | ||
386 | if (!priv) | ||
387 | continue; | ||
388 | |||
389 | for (i = 0; i < MAX_NUM_TID; ++i) { | ||
390 | priv->aggr_prio_tbl[i].amsdu = tos_to_tid_inv[i]; | ||
391 | priv->aggr_prio_tbl[i].ampdu_ap = tos_to_tid_inv[i]; | ||
392 | priv->aggr_prio_tbl[i].ampdu_user = tos_to_tid_inv[i]; | ||
393 | priv->wmm.tid_tbl_ptr[i].ra_list_curr = NULL; | ||
394 | } | ||
395 | |||
396 | priv->aggr_prio_tbl[6].amsdu | ||
397 | = priv->aggr_prio_tbl[6].ampdu_ap | ||
398 | = priv->aggr_prio_tbl[6].ampdu_user | ||
399 | = BA_STREAM_NOT_ALLOWED; | ||
400 | |||
401 | priv->aggr_prio_tbl[7].amsdu = priv->aggr_prio_tbl[7].ampdu_ap | ||
402 | = priv->aggr_prio_tbl[7].ampdu_user | ||
403 | = BA_STREAM_NOT_ALLOWED; | ||
404 | |||
405 | priv->add_ba_param.timeout = MWIFIEX_DEFAULT_BLOCK_ACK_TIMEOUT; | ||
406 | priv->add_ba_param.tx_win_size = MWIFIEX_AMPDU_DEF_TXWINSIZE; | ||
407 | priv->add_ba_param.rx_win_size = MWIFIEX_AMPDU_DEF_RXWINSIZE; | ||
408 | |||
409 | atomic_set(&priv->wmm.tx_pkts_queued, 0); | ||
410 | atomic_set(&priv->wmm.highest_queued_prio, HIGH_PRIO_TID); | ||
411 | } | ||
412 | } | ||
413 | |||
414 | /* | ||
415 | * This function checks if WMM Tx queue is empty. | ||
416 | */ | ||
417 | int | ||
418 | mwifiex_wmm_lists_empty(struct mwifiex_adapter *adapter) | ||
419 | { | ||
420 | int i; | ||
421 | struct mwifiex_private *priv; | ||
422 | |||
423 | for (i = 0; i < adapter->priv_num; ++i) { | ||
424 | priv = adapter->priv[i]; | ||
425 | if (priv && atomic_read(&priv->wmm.tx_pkts_queued)) | ||
426 | return false; | ||
427 | } | ||
428 | |||
429 | return true; | ||
430 | } | ||
431 | |||
432 | /* | ||
433 | * This function deletes all packets in an RA list node. | ||
434 | * | ||
435 | * The packet sent completion callback handler are called with | ||
436 | * status failure, after they are dequeued to ensure proper | ||
437 | * cleanup. The RA list node itself is freed at the end. | ||
438 | */ | ||
439 | static void | ||
440 | mwifiex_wmm_del_pkts_in_ralist_node(struct mwifiex_private *priv, | ||
441 | struct mwifiex_ra_list_tbl *ra_list) | ||
442 | { | ||
443 | struct mwifiex_adapter *adapter = priv->adapter; | ||
444 | struct sk_buff *skb, *tmp; | ||
445 | |||
446 | skb_queue_walk_safe(&ra_list->skb_head, skb, tmp) | ||
447 | mwifiex_write_data_complete(adapter, skb, -1); | ||
448 | } | ||
449 | |||
450 | /* | ||
451 | * This function deletes all packets in an RA list. | ||
452 | * | ||
453 | * Each nodes in the RA list are freed individually first, and then | ||
454 | * the RA list itself is freed. | ||
455 | */ | ||
456 | static void | ||
457 | mwifiex_wmm_del_pkts_in_ralist(struct mwifiex_private *priv, | ||
458 | struct list_head *ra_list_head) | ||
459 | { | ||
460 | struct mwifiex_ra_list_tbl *ra_list; | ||
461 | |||
462 | list_for_each_entry(ra_list, ra_list_head, list) | ||
463 | mwifiex_wmm_del_pkts_in_ralist_node(priv, ra_list); | ||
464 | } | ||
465 | |||
466 | /* | ||
467 | * This function deletes all packets in all RA lists. | ||
468 | */ | ||
469 | static void mwifiex_wmm_cleanup_queues(struct mwifiex_private *priv) | ||
470 | { | ||
471 | int i; | ||
472 | |||
473 | for (i = 0; i < MAX_NUM_TID; i++) | ||
474 | mwifiex_wmm_del_pkts_in_ralist(priv, &priv->wmm.tid_tbl_ptr[i]. | ||
475 | ra_list); | ||
476 | |||
477 | atomic_set(&priv->wmm.tx_pkts_queued, 0); | ||
478 | atomic_set(&priv->wmm.highest_queued_prio, HIGH_PRIO_TID); | ||
479 | } | ||
480 | |||
481 | /* | ||
482 | * This function deletes all route addresses from all RA lists. | ||
483 | */ | ||
484 | static void mwifiex_wmm_delete_all_ralist(struct mwifiex_private *priv) | ||
485 | { | ||
486 | struct mwifiex_ra_list_tbl *ra_list, *tmp_node; | ||
487 | int i; | ||
488 | |||
489 | for (i = 0; i < MAX_NUM_TID; ++i) { | ||
490 | dev_dbg(priv->adapter->dev, | ||
491 | "info: ra_list: freeing buf for tid %d\n", i); | ||
492 | list_for_each_entry_safe(ra_list, tmp_node, | ||
493 | &priv->wmm.tid_tbl_ptr[i].ra_list, list) { | ||
494 | list_del(&ra_list->list); | ||
495 | kfree(ra_list); | ||
496 | } | ||
497 | |||
498 | INIT_LIST_HEAD(&priv->wmm.tid_tbl_ptr[i].ra_list); | ||
499 | |||
500 | priv->wmm.tid_tbl_ptr[i].ra_list_curr = NULL; | ||
501 | } | ||
502 | } | ||
503 | |||
504 | /* | ||
505 | * This function cleans up the Tx and Rx queues. | ||
506 | * | ||
507 | * Cleanup includes - | ||
508 | * - All packets in RA lists | ||
509 | * - All entries in Rx reorder table | ||
510 | * - All entries in Tx BA stream table | ||
511 | * - MPA buffer (if required) | ||
512 | * - All RA lists | ||
513 | */ | ||
514 | void | ||
515 | mwifiex_clean_txrx(struct mwifiex_private *priv) | ||
516 | { | ||
517 | unsigned long flags; | ||
518 | |||
519 | mwifiex_11n_cleanup_reorder_tbl(priv); | ||
520 | spin_lock_irqsave(&priv->wmm.ra_list_spinlock, flags); | ||
521 | |||
522 | mwifiex_wmm_cleanup_queues(priv); | ||
523 | mwifiex_11n_delete_all_tx_ba_stream_tbl(priv); | ||
524 | |||
525 | if (priv->adapter->if_ops.cleanup_mpa_buf) | ||
526 | priv->adapter->if_ops.cleanup_mpa_buf(priv->adapter); | ||
527 | |||
528 | mwifiex_wmm_delete_all_ralist(priv); | ||
529 | memcpy(tos_to_tid, ac_to_tid, sizeof(tos_to_tid)); | ||
530 | |||
531 | spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, flags); | ||
532 | } | ||
533 | |||
534 | /* | ||
535 | * This function retrieves a particular RA list node, matching with the | ||
536 | * given TID and RA address. | ||
537 | */ | ||
538 | static struct mwifiex_ra_list_tbl * | ||
539 | mwifiex_wmm_get_ralist_node(struct mwifiex_private *priv, u8 tid, | ||
540 | u8 *ra_addr) | ||
541 | { | ||
542 | struct mwifiex_ra_list_tbl *ra_list; | ||
543 | |||
544 | list_for_each_entry(ra_list, &priv->wmm.tid_tbl_ptr[tid].ra_list, | ||
545 | list) { | ||
546 | if (!memcmp(ra_list->ra, ra_addr, ETH_ALEN)) | ||
547 | return ra_list; | ||
548 | } | ||
549 | |||
550 | return NULL; | ||
551 | } | ||
552 | |||
553 | /* | ||
554 | * This function retrieves an RA list node for a given TID and | ||
555 | * RA address pair. | ||
556 | * | ||
557 | * If no such node is found, a new node is added first and then | ||
558 | * retrieved. | ||
559 | */ | ||
560 | static struct mwifiex_ra_list_tbl * | ||
561 | mwifiex_wmm_get_queue_raptr(struct mwifiex_private *priv, u8 tid, u8 *ra_addr) | ||
562 | { | ||
563 | struct mwifiex_ra_list_tbl *ra_list; | ||
564 | |||
565 | ra_list = mwifiex_wmm_get_ralist_node(priv, tid, ra_addr); | ||
566 | if (ra_list) | ||
567 | return ra_list; | ||
568 | mwifiex_ralist_add(priv, ra_addr); | ||
569 | |||
570 | return mwifiex_wmm_get_ralist_node(priv, tid, ra_addr); | ||
571 | } | ||
572 | |||
573 | /* | ||
574 | * This function checks if a particular RA list node exists in a given TID | ||
575 | * table index. | ||
576 | */ | ||
577 | int | ||
578 | mwifiex_is_ralist_valid(struct mwifiex_private *priv, | ||
579 | struct mwifiex_ra_list_tbl *ra_list, int ptr_index) | ||
580 | { | ||
581 | struct mwifiex_ra_list_tbl *rlist; | ||
582 | |||
583 | list_for_each_entry(rlist, &priv->wmm.tid_tbl_ptr[ptr_index].ra_list, | ||
584 | list) { | ||
585 | if (rlist == ra_list) | ||
586 | return true; | ||
587 | } | ||
588 | |||
589 | return false; | ||
590 | } | ||
591 | |||
592 | /* | ||
593 | * This function adds a packet to WMM queue. | ||
594 | * | ||
595 | * In disconnected state the packet is immediately dropped and the | ||
596 | * packet send completion callback is called with status failure. | ||
597 | * | ||
598 | * Otherwise, the correct RA list node is located and the packet | ||
599 | * is queued at the list tail. | ||
600 | */ | ||
601 | void | ||
602 | mwifiex_wmm_add_buf_txqueue(struct mwifiex_adapter *adapter, | ||
603 | struct sk_buff *skb) | ||
604 | { | ||
605 | struct mwifiex_txinfo *tx_info = MWIFIEX_SKB_TXCB(skb); | ||
606 | struct mwifiex_private *priv = adapter->priv[tx_info->bss_index]; | ||
607 | u32 tid; | ||
608 | struct mwifiex_ra_list_tbl *ra_list; | ||
609 | u8 ra[ETH_ALEN], tid_down; | ||
610 | unsigned long flags; | ||
611 | |||
612 | if (!priv->media_connected) { | ||
613 | dev_dbg(adapter->dev, "data: drop packet in disconnect\n"); | ||
614 | mwifiex_write_data_complete(adapter, skb, -1); | ||
615 | return; | ||
616 | } | ||
617 | |||
618 | tid = skb->priority; | ||
619 | |||
620 | spin_lock_irqsave(&priv->wmm.ra_list_spinlock, flags); | ||
621 | |||
622 | tid_down = mwifiex_wmm_downgrade_tid(priv, tid); | ||
623 | |||
624 | /* In case of infra as we have already created the list during | ||
625 | association we just don't have to call get_queue_raptr, we will | ||
626 | have only 1 raptr for a tid in case of infra */ | ||
627 | if (!mwifiex_queuing_ra_based(priv)) { | ||
628 | if (!list_empty(&priv->wmm.tid_tbl_ptr[tid_down].ra_list)) | ||
629 | ra_list = list_first_entry( | ||
630 | &priv->wmm.tid_tbl_ptr[tid_down].ra_list, | ||
631 | struct mwifiex_ra_list_tbl, list); | ||
632 | else | ||
633 | ra_list = NULL; | ||
634 | } else { | ||
635 | memcpy(ra, skb->data, ETH_ALEN); | ||
636 | ra_list = mwifiex_wmm_get_queue_raptr(priv, tid_down, ra); | ||
637 | } | ||
638 | |||
639 | if (!ra_list) { | ||
640 | spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, flags); | ||
641 | mwifiex_write_data_complete(adapter, skb, -1); | ||
642 | return; | ||
643 | } | ||
644 | |||
645 | skb_queue_tail(&ra_list->skb_head, skb); | ||
646 | |||
647 | ra_list->total_pkts_size += skb->len; | ||
648 | |||
649 | atomic_inc(&priv->wmm.tx_pkts_queued); | ||
650 | |||
651 | if (atomic_read(&priv->wmm.highest_queued_prio) < | ||
652 | tos_to_tid_inv[tid_down]) | ||
653 | atomic_set(&priv->wmm.highest_queued_prio, | ||
654 | tos_to_tid_inv[tid_down]); | ||
655 | |||
656 | spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, flags); | ||
657 | } | ||
658 | |||
659 | /* | ||
660 | * This function processes the get WMM status command response from firmware. | ||
661 | * | ||
662 | * The response may contain multiple TLVs - | ||
663 | * - AC Queue status TLVs | ||
664 | * - Current WMM Parameter IE TLV | ||
665 | * - Admission Control action frame TLVs | ||
666 | * | ||
667 | * This function parses the TLVs and then calls further specific functions | ||
668 | * to process any changes in the queue prioritize or state. | ||
669 | */ | ||
670 | int mwifiex_ret_wmm_get_status(struct mwifiex_private *priv, | ||
671 | const struct host_cmd_ds_command *resp) | ||
672 | { | ||
673 | u8 *curr = (u8 *) &resp->params.get_wmm_status; | ||
674 | uint16_t resp_len = le16_to_cpu(resp->size), tlv_len; | ||
675 | int valid = true; | ||
676 | |||
677 | struct mwifiex_ie_types_data *tlv_hdr; | ||
678 | struct mwifiex_ie_types_wmm_queue_status *tlv_wmm_qstatus; | ||
679 | struct ieee_types_wmm_parameter *wmm_param_ie = NULL; | ||
680 | struct mwifiex_wmm_ac_status *ac_status; | ||
681 | |||
682 | dev_dbg(priv->adapter->dev, "info: WMM: WMM_GET_STATUS cmdresp received: %d\n", | ||
683 | resp_len); | ||
684 | |||
685 | while ((resp_len >= sizeof(tlv_hdr->header)) && valid) { | ||
686 | tlv_hdr = (struct mwifiex_ie_types_data *) curr; | ||
687 | tlv_len = le16_to_cpu(tlv_hdr->header.len); | ||
688 | |||
689 | switch (le16_to_cpu(tlv_hdr->header.type)) { | ||
690 | case TLV_TYPE_WMMQSTATUS: | ||
691 | tlv_wmm_qstatus = | ||
692 | (struct mwifiex_ie_types_wmm_queue_status *) | ||
693 | tlv_hdr; | ||
694 | dev_dbg(priv->adapter->dev, | ||
695 | "info: CMD_RESP: WMM_GET_STATUS:" | ||
696 | " QSTATUS TLV: %d, %d, %d\n", | ||
697 | tlv_wmm_qstatus->queue_index, | ||
698 | tlv_wmm_qstatus->flow_required, | ||
699 | tlv_wmm_qstatus->disabled); | ||
700 | |||
701 | ac_status = &priv->wmm.ac_status[tlv_wmm_qstatus-> | ||
702 | queue_index]; | ||
703 | ac_status->disabled = tlv_wmm_qstatus->disabled; | ||
704 | ac_status->flow_required = | ||
705 | tlv_wmm_qstatus->flow_required; | ||
706 | ac_status->flow_created = tlv_wmm_qstatus->flow_created; | ||
707 | break; | ||
708 | |||
709 | case WLAN_EID_VENDOR_SPECIFIC: | ||
710 | /* | ||
711 | * Point the regular IEEE IE 2 bytes into the Marvell IE | ||
712 | * and setup the IEEE IE type and length byte fields | ||
713 | */ | ||
714 | |||
715 | wmm_param_ie = | ||
716 | (struct ieee_types_wmm_parameter *) (curr + | ||
717 | 2); | ||
718 | wmm_param_ie->vend_hdr.len = (u8) tlv_len; | ||
719 | wmm_param_ie->vend_hdr.element_id = | ||
720 | WLAN_EID_VENDOR_SPECIFIC; | ||
721 | |||
722 | dev_dbg(priv->adapter->dev, | ||
723 | "info: CMD_RESP: WMM_GET_STATUS:" | ||
724 | " WMM Parameter Set Count: %d\n", | ||
725 | wmm_param_ie->qos_info_bitmap & | ||
726 | IEEE80211_WMM_IE_AP_QOSINFO_PARAM_SET_CNT_MASK); | ||
727 | |||
728 | memcpy((u8 *) &priv->curr_bss_params.bss_descriptor. | ||
729 | wmm_ie, wmm_param_ie, | ||
730 | wmm_param_ie->vend_hdr.len + 2); | ||
731 | |||
732 | break; | ||
733 | |||
734 | default: | ||
735 | valid = false; | ||
736 | break; | ||
737 | } | ||
738 | |||
739 | curr += (tlv_len + sizeof(tlv_hdr->header)); | ||
740 | resp_len -= (tlv_len + sizeof(tlv_hdr->header)); | ||
741 | } | ||
742 | |||
743 | mwifiex_wmm_setup_queue_priorities(priv, wmm_param_ie); | ||
744 | mwifiex_wmm_setup_ac_downgrade(priv); | ||
745 | |||
746 | return 0; | ||
747 | } | ||
748 | |||
749 | /* | ||
750 | * Callback handler from the command module to allow insertion of a WMM TLV. | ||
751 | * | ||
752 | * If the BSS we are associating to supports WMM, this function adds the | ||
753 | * required WMM Information IE to the association request command buffer in | ||
754 | * the form of a Marvell extended IEEE IE. | ||
755 | */ | ||
756 | u32 | ||
757 | mwifiex_wmm_process_association_req(struct mwifiex_private *priv, | ||
758 | u8 **assoc_buf, | ||
759 | struct ieee_types_wmm_parameter *wmm_ie, | ||
760 | struct ieee80211_ht_cap *ht_cap) | ||
761 | { | ||
762 | struct mwifiex_ie_types_wmm_param_set *wmm_tlv; | ||
763 | u32 ret_len = 0; | ||
764 | |||
765 | /* Null checks */ | ||
766 | if (!assoc_buf) | ||
767 | return 0; | ||
768 | if (!(*assoc_buf)) | ||
769 | return 0; | ||
770 | |||
771 | if (!wmm_ie) | ||
772 | return 0; | ||
773 | |||
774 | dev_dbg(priv->adapter->dev, "info: WMM: process assoc req:" | ||
775 | "bss->wmmIe=0x%x\n", | ||
776 | wmm_ie->vend_hdr.element_id); | ||
777 | |||
778 | if ((priv->wmm_required | ||
779 | || (ht_cap && (priv->adapter->config_bands & BAND_GN | ||
780 | || priv->adapter->config_bands & BAND_AN)) | ||
781 | ) | ||
782 | && wmm_ie->vend_hdr.element_id == WLAN_EID_VENDOR_SPECIFIC) { | ||
783 | wmm_tlv = (struct mwifiex_ie_types_wmm_param_set *) *assoc_buf; | ||
784 | wmm_tlv->header.type = cpu_to_le16((u16) wmm_info_ie[0]); | ||
785 | wmm_tlv->header.len = cpu_to_le16((u16) wmm_info_ie[1]); | ||
786 | memcpy(wmm_tlv->wmm_ie, &wmm_info_ie[2], | ||
787 | le16_to_cpu(wmm_tlv->header.len)); | ||
788 | if (wmm_ie->qos_info_bitmap & IEEE80211_WMM_IE_AP_QOSINFO_UAPSD) | ||
789 | memcpy((u8 *) (wmm_tlv->wmm_ie | ||
790 | + le16_to_cpu(wmm_tlv->header.len) | ||
791 | - sizeof(priv->wmm_qosinfo)), | ||
792 | &priv->wmm_qosinfo, | ||
793 | sizeof(priv->wmm_qosinfo)); | ||
794 | |||
795 | ret_len = sizeof(wmm_tlv->header) | ||
796 | + le16_to_cpu(wmm_tlv->header.len); | ||
797 | |||
798 | *assoc_buf += ret_len; | ||
799 | } | ||
800 | |||
801 | return ret_len; | ||
802 | } | ||
803 | |||
804 | /* | ||
805 | * This function computes the time delay in the driver queues for a | ||
806 | * given packet. | ||
807 | * | ||
808 | * When the packet is received at the OS/Driver interface, the current | ||
809 | * time is set in the packet structure. The difference between the present | ||
810 | * time and that received time is computed in this function and limited | ||
811 | * based on pre-compiled limits in the driver. | ||
812 | */ | ||
813 | u8 | ||
814 | mwifiex_wmm_compute_drv_pkt_delay(struct mwifiex_private *priv, | ||
815 | const struct sk_buff *skb) | ||
816 | { | ||
817 | u8 ret_val; | ||
818 | struct timeval out_tstamp, in_tstamp; | ||
819 | u32 queue_delay; | ||
820 | |||
821 | do_gettimeofday(&out_tstamp); | ||
822 | in_tstamp = ktime_to_timeval(skb->tstamp); | ||
823 | |||
824 | queue_delay = (out_tstamp.tv_sec - in_tstamp.tv_sec) * 1000; | ||
825 | queue_delay += (out_tstamp.tv_usec - in_tstamp.tv_usec) / 1000; | ||
826 | |||
827 | /* | ||
828 | * Queue delay is passed as a uint8 in units of 2ms (ms shifted | ||
829 | * by 1). Min value (other than 0) is therefore 2ms, max is 510ms. | ||
830 | * | ||
831 | * Pass max value if queue_delay is beyond the uint8 range | ||
832 | */ | ||
833 | ret_val = (u8) (min(queue_delay, priv->wmm.drv_pkt_delay_max) >> 1); | ||
834 | |||
835 | dev_dbg(priv->adapter->dev, "data: WMM: Pkt Delay: %d ms," | ||
836 | " %d ms sent to FW\n", queue_delay, ret_val); | ||
837 | |||
838 | return ret_val; | ||
839 | } | ||
840 | |||
841 | /* | ||
842 | * This function retrieves the highest priority RA list table pointer. | ||
843 | */ | ||
844 | static struct mwifiex_ra_list_tbl * | ||
845 | mwifiex_wmm_get_highest_priolist_ptr(struct mwifiex_adapter *adapter, | ||
846 | struct mwifiex_private **priv, int *tid) | ||
847 | { | ||
848 | struct mwifiex_private *priv_tmp; | ||
849 | struct mwifiex_ra_list_tbl *ptr, *head; | ||
850 | struct mwifiex_bss_prio_node *bssprio_node, *bssprio_head; | ||
851 | struct mwifiex_tid_tbl *tid_ptr; | ||
852 | int is_list_empty; | ||
853 | unsigned long flags; | ||
854 | int i, j; | ||
855 | |||
856 | for (j = adapter->priv_num - 1; j >= 0; --j) { | ||
857 | spin_lock_irqsave(&adapter->bss_prio_tbl[j].bss_prio_lock, | ||
858 | flags); | ||
859 | is_list_empty = list_empty(&adapter->bss_prio_tbl[j] | ||
860 | .bss_prio_head); | ||
861 | spin_unlock_irqrestore(&adapter->bss_prio_tbl[j].bss_prio_lock, | ||
862 | flags); | ||
863 | if (is_list_empty) | ||
864 | continue; | ||
865 | |||
866 | if (adapter->bss_prio_tbl[j].bss_prio_cur == | ||
867 | (struct mwifiex_bss_prio_node *) | ||
868 | &adapter->bss_prio_tbl[j].bss_prio_head) { | ||
869 | bssprio_node = | ||
870 | list_first_entry(&adapter->bss_prio_tbl[j] | ||
871 | .bss_prio_head, | ||
872 | struct mwifiex_bss_prio_node, | ||
873 | list); | ||
874 | bssprio_head = bssprio_node; | ||
875 | } else { | ||
876 | bssprio_node = adapter->bss_prio_tbl[j].bss_prio_cur; | ||
877 | bssprio_head = bssprio_node; | ||
878 | } | ||
879 | |||
880 | do { | ||
881 | atomic_t *hqp; | ||
882 | spinlock_t *lock; | ||
883 | |||
884 | priv_tmp = bssprio_node->priv; | ||
885 | hqp = &priv_tmp->wmm.highest_queued_prio; | ||
886 | lock = &priv_tmp->wmm.ra_list_spinlock; | ||
887 | |||
888 | for (i = atomic_read(hqp); i >= LOW_PRIO_TID; --i) { | ||
889 | |||
890 | tid_ptr = &(priv_tmp)->wmm. | ||
891 | tid_tbl_ptr[tos_to_tid[i]]; | ||
892 | |||
893 | spin_lock_irqsave(&tid_ptr->tid_tbl_lock, | ||
894 | flags); | ||
895 | is_list_empty = | ||
896 | list_empty(&adapter->bss_prio_tbl[j] | ||
897 | .bss_prio_head); | ||
898 | spin_unlock_irqrestore(&tid_ptr->tid_tbl_lock, | ||
899 | flags); | ||
900 | if (is_list_empty) | ||
901 | continue; | ||
902 | |||
903 | /* | ||
904 | * Always choose the next ra we transmitted | ||
905 | * last time, this way we pick the ra's in | ||
906 | * round robin fashion. | ||
907 | */ | ||
908 | ptr = list_first_entry( | ||
909 | &tid_ptr->ra_list_curr->list, | ||
910 | struct mwifiex_ra_list_tbl, | ||
911 | list); | ||
912 | |||
913 | head = ptr; | ||
914 | if (ptr == (struct mwifiex_ra_list_tbl *) | ||
915 | &tid_ptr->ra_list) { | ||
916 | /* Get next ra */ | ||
917 | ptr = list_first_entry(&ptr->list, | ||
918 | struct mwifiex_ra_list_tbl, list); | ||
919 | head = ptr; | ||
920 | } | ||
921 | |||
922 | do { | ||
923 | is_list_empty = | ||
924 | skb_queue_empty(&ptr->skb_head); | ||
925 | if (!is_list_empty) { | ||
926 | spin_lock_irqsave(lock, flags); | ||
927 | if (atomic_read(hqp) > i) | ||
928 | atomic_set(hqp, i); | ||
929 | spin_unlock_irqrestore(lock, | ||
930 | flags); | ||
931 | *priv = priv_tmp; | ||
932 | *tid = tos_to_tid[i]; | ||
933 | return ptr; | ||
934 | } | ||
935 | /* Get next ra */ | ||
936 | ptr = list_first_entry(&ptr->list, | ||
937 | struct mwifiex_ra_list_tbl, | ||
938 | list); | ||
939 | if (ptr == | ||
940 | (struct mwifiex_ra_list_tbl *) | ||
941 | &tid_ptr->ra_list) | ||
942 | ptr = list_first_entry( | ||
943 | &ptr->list, | ||
944 | struct mwifiex_ra_list_tbl, | ||
945 | list); | ||
946 | } while (ptr != head); | ||
947 | } | ||
948 | |||
949 | /* No packet at any TID for this priv. Mark as such | ||
950 | * to skip checking TIDs for this priv (until pkt is | ||
951 | * added). | ||
952 | */ | ||
953 | atomic_set(hqp, NO_PKT_PRIO_TID); | ||
954 | |||
955 | /* Get next bss priority node */ | ||
956 | bssprio_node = list_first_entry(&bssprio_node->list, | ||
957 | struct mwifiex_bss_prio_node, | ||
958 | list); | ||
959 | |||
960 | if (bssprio_node == | ||
961 | (struct mwifiex_bss_prio_node *) | ||
962 | &adapter->bss_prio_tbl[j].bss_prio_head) | ||
963 | /* Get next bss priority node */ | ||
964 | bssprio_node = list_first_entry( | ||
965 | &bssprio_node->list, | ||
966 | struct mwifiex_bss_prio_node, | ||
967 | list); | ||
968 | } while (bssprio_node != bssprio_head); | ||
969 | } | ||
970 | return NULL; | ||
971 | } | ||
972 | |||
973 | /* | ||
974 | * This function gets the number of packets in the Tx queue of a | ||
975 | * particular RA list. | ||
976 | */ | ||
977 | static int | ||
978 | mwifiex_num_pkts_in_txq(struct mwifiex_private *priv, | ||
979 | struct mwifiex_ra_list_tbl *ptr, int max_buf_size) | ||
980 | { | ||
981 | int count = 0, total_size = 0; | ||
982 | struct sk_buff *skb, *tmp; | ||
983 | |||
984 | skb_queue_walk_safe(&ptr->skb_head, skb, tmp) { | ||
985 | total_size += skb->len; | ||
986 | if (total_size < max_buf_size) | ||
987 | ++count; | ||
988 | else | ||
989 | break; | ||
990 | } | ||
991 | |||
992 | return count; | ||
993 | } | ||
994 | |||
995 | /* | ||
996 | * This function sends a single packet to firmware for transmission. | ||
997 | */ | ||
998 | static void | ||
999 | mwifiex_send_single_packet(struct mwifiex_private *priv, | ||
1000 | struct mwifiex_ra_list_tbl *ptr, int ptr_index, | ||
1001 | unsigned long ra_list_flags) | ||
1002 | __releases(&priv->wmm.ra_list_spinlock) | ||
1003 | { | ||
1004 | struct sk_buff *skb, *skb_next; | ||
1005 | struct mwifiex_tx_param tx_param; | ||
1006 | struct mwifiex_adapter *adapter = priv->adapter; | ||
1007 | struct mwifiex_txinfo *tx_info; | ||
1008 | |||
1009 | if (skb_queue_empty(&ptr->skb_head)) { | ||
1010 | spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, | ||
1011 | ra_list_flags); | ||
1012 | dev_dbg(adapter->dev, "data: nothing to send\n"); | ||
1013 | return; | ||
1014 | } | ||
1015 | |||
1016 | skb = skb_dequeue(&ptr->skb_head); | ||
1017 | |||
1018 | tx_info = MWIFIEX_SKB_TXCB(skb); | ||
1019 | dev_dbg(adapter->dev, "data: dequeuing the packet %p %p\n", ptr, skb); | ||
1020 | |||
1021 | ptr->total_pkts_size -= skb->len; | ||
1022 | |||
1023 | if (!skb_queue_empty(&ptr->skb_head)) | ||
1024 | skb_next = skb_peek(&ptr->skb_head); | ||
1025 | else | ||
1026 | skb_next = NULL; | ||
1027 | |||
1028 | spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, ra_list_flags); | ||
1029 | |||
1030 | tx_param.next_pkt_len = ((skb_next) ? skb_next->len + | ||
1031 | sizeof(struct txpd) : 0); | ||
1032 | |||
1033 | if (mwifiex_process_tx(priv, skb, &tx_param) == -EBUSY) { | ||
1034 | /* Queue the packet back at the head */ | ||
1035 | spin_lock_irqsave(&priv->wmm.ra_list_spinlock, ra_list_flags); | ||
1036 | |||
1037 | if (!mwifiex_is_ralist_valid(priv, ptr, ptr_index)) { | ||
1038 | spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, | ||
1039 | ra_list_flags); | ||
1040 | mwifiex_write_data_complete(adapter, skb, -1); | ||
1041 | return; | ||
1042 | } | ||
1043 | |||
1044 | skb_queue_tail(&ptr->skb_head, skb); | ||
1045 | |||
1046 | ptr->total_pkts_size += skb->len; | ||
1047 | tx_info->flags |= MWIFIEX_BUF_FLAG_REQUEUED_PKT; | ||
1048 | spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, | ||
1049 | ra_list_flags); | ||
1050 | } else { | ||
1051 | spin_lock_irqsave(&priv->wmm.ra_list_spinlock, ra_list_flags); | ||
1052 | if (mwifiex_is_ralist_valid(priv, ptr, ptr_index)) { | ||
1053 | priv->wmm.packets_out[ptr_index]++; | ||
1054 | priv->wmm.tid_tbl_ptr[ptr_index].ra_list_curr = ptr; | ||
1055 | } | ||
1056 | adapter->bss_prio_tbl[priv->bss_priority].bss_prio_cur = | ||
1057 | list_first_entry( | ||
1058 | &adapter->bss_prio_tbl[priv->bss_priority] | ||
1059 | .bss_prio_cur->list, | ||
1060 | struct mwifiex_bss_prio_node, | ||
1061 | list); | ||
1062 | atomic_dec(&priv->wmm.tx_pkts_queued); | ||
1063 | spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, | ||
1064 | ra_list_flags); | ||
1065 | } | ||
1066 | } | ||
1067 | |||
1068 | /* | ||
1069 | * This function checks if the first packet in the given RA list | ||
1070 | * is already processed or not. | ||
1071 | */ | ||
1072 | static int | ||
1073 | mwifiex_is_ptr_processed(struct mwifiex_private *priv, | ||
1074 | struct mwifiex_ra_list_tbl *ptr) | ||
1075 | { | ||
1076 | struct sk_buff *skb; | ||
1077 | struct mwifiex_txinfo *tx_info; | ||
1078 | |||
1079 | if (skb_queue_empty(&ptr->skb_head)) | ||
1080 | return false; | ||
1081 | |||
1082 | skb = skb_peek(&ptr->skb_head); | ||
1083 | |||
1084 | tx_info = MWIFIEX_SKB_TXCB(skb); | ||
1085 | if (tx_info->flags & MWIFIEX_BUF_FLAG_REQUEUED_PKT) | ||
1086 | return true; | ||
1087 | |||
1088 | return false; | ||
1089 | } | ||
1090 | |||
1091 | /* | ||
1092 | * This function sends a single processed packet to firmware for | ||
1093 | * transmission. | ||
1094 | */ | ||
1095 | static void | ||
1096 | mwifiex_send_processed_packet(struct mwifiex_private *priv, | ||
1097 | struct mwifiex_ra_list_tbl *ptr, int ptr_index, | ||
1098 | unsigned long ra_list_flags) | ||
1099 | __releases(&priv->wmm.ra_list_spinlock) | ||
1100 | { | ||
1101 | struct mwifiex_tx_param tx_param; | ||
1102 | struct mwifiex_adapter *adapter = priv->adapter; | ||
1103 | int ret = -1; | ||
1104 | struct sk_buff *skb, *skb_next; | ||
1105 | struct mwifiex_txinfo *tx_info; | ||
1106 | |||
1107 | if (skb_queue_empty(&ptr->skb_head)) { | ||
1108 | spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, | ||
1109 | ra_list_flags); | ||
1110 | return; | ||
1111 | } | ||
1112 | |||
1113 | skb = skb_dequeue(&ptr->skb_head); | ||
1114 | |||
1115 | if (!skb_queue_empty(&ptr->skb_head)) | ||
1116 | skb_next = skb_peek(&ptr->skb_head); | ||
1117 | else | ||
1118 | skb_next = NULL; | ||
1119 | |||
1120 | tx_info = MWIFIEX_SKB_TXCB(skb); | ||
1121 | |||
1122 | spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, ra_list_flags); | ||
1123 | tx_param.next_pkt_len = | ||
1124 | ((skb_next) ? skb_next->len + | ||
1125 | sizeof(struct txpd) : 0); | ||
1126 | ret = adapter->if_ops.host_to_card(adapter, MWIFIEX_TYPE_DATA, | ||
1127 | skb->data, skb->len, &tx_param); | ||
1128 | switch (ret) { | ||
1129 | case -EBUSY: | ||
1130 | dev_dbg(adapter->dev, "data: -EBUSY is returned\n"); | ||
1131 | spin_lock_irqsave(&priv->wmm.ra_list_spinlock, ra_list_flags); | ||
1132 | |||
1133 | if (!mwifiex_is_ralist_valid(priv, ptr, ptr_index)) { | ||
1134 | spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, | ||
1135 | ra_list_flags); | ||
1136 | mwifiex_write_data_complete(adapter, skb, -1); | ||
1137 | return; | ||
1138 | } | ||
1139 | |||
1140 | skb_queue_tail(&ptr->skb_head, skb); | ||
1141 | |||
1142 | tx_info->flags |= MWIFIEX_BUF_FLAG_REQUEUED_PKT; | ||
1143 | spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, | ||
1144 | ra_list_flags); | ||
1145 | break; | ||
1146 | case -1: | ||
1147 | adapter->data_sent = false; | ||
1148 | dev_err(adapter->dev, "host_to_card failed: %#x\n", ret); | ||
1149 | adapter->dbg.num_tx_host_to_card_failure++; | ||
1150 | mwifiex_write_data_complete(adapter, skb, ret); | ||
1151 | break; | ||
1152 | case -EINPROGRESS: | ||
1153 | adapter->data_sent = false; | ||
1154 | default: | ||
1155 | break; | ||
1156 | } | ||
1157 | if (ret != -EBUSY) { | ||
1158 | spin_lock_irqsave(&priv->wmm.ra_list_spinlock, ra_list_flags); | ||
1159 | if (mwifiex_is_ralist_valid(priv, ptr, ptr_index)) { | ||
1160 | priv->wmm.packets_out[ptr_index]++; | ||
1161 | priv->wmm.tid_tbl_ptr[ptr_index].ra_list_curr = ptr; | ||
1162 | } | ||
1163 | adapter->bss_prio_tbl[priv->bss_priority].bss_prio_cur = | ||
1164 | list_first_entry( | ||
1165 | &adapter->bss_prio_tbl[priv->bss_priority] | ||
1166 | .bss_prio_cur->list, | ||
1167 | struct mwifiex_bss_prio_node, | ||
1168 | list); | ||
1169 | atomic_dec(&priv->wmm.tx_pkts_queued); | ||
1170 | spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, | ||
1171 | ra_list_flags); | ||
1172 | } | ||
1173 | } | ||
1174 | |||
1175 | /* | ||
1176 | * This function dequeues a packet from the highest priority list | ||
1177 | * and transmits it. | ||
1178 | */ | ||
1179 | static int | ||
1180 | mwifiex_dequeue_tx_packet(struct mwifiex_adapter *adapter) | ||
1181 | { | ||
1182 | struct mwifiex_ra_list_tbl *ptr; | ||
1183 | struct mwifiex_private *priv = NULL; | ||
1184 | int ptr_index = 0; | ||
1185 | u8 ra[ETH_ALEN]; | ||
1186 | int tid_del = 0, tid = 0; | ||
1187 | unsigned long flags; | ||
1188 | |||
1189 | ptr = mwifiex_wmm_get_highest_priolist_ptr(adapter, &priv, &ptr_index); | ||
1190 | if (!ptr) | ||
1191 | return -1; | ||
1192 | |||
1193 | tid = mwifiex_get_tid(ptr); | ||
1194 | |||
1195 | dev_dbg(adapter->dev, "data: tid=%d\n", tid); | ||
1196 | |||
1197 | spin_lock_irqsave(&priv->wmm.ra_list_spinlock, flags); | ||
1198 | if (!mwifiex_is_ralist_valid(priv, ptr, ptr_index)) { | ||
1199 | spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, flags); | ||
1200 | return -1; | ||
1201 | } | ||
1202 | |||
1203 | if (mwifiex_is_ptr_processed(priv, ptr)) { | ||
1204 | mwifiex_send_processed_packet(priv, ptr, ptr_index, flags); | ||
1205 | /* ra_list_spinlock has been freed in | ||
1206 | mwifiex_send_processed_packet() */ | ||
1207 | return 0; | ||
1208 | } | ||
1209 | |||
1210 | if (!ptr->is_11n_enabled || mwifiex_is_ba_stream_setup(priv, ptr, tid) | ||
1211 | || ((priv->sec_info.wpa_enabled | ||
1212 | || priv->sec_info.wpa2_enabled) && !priv->wpa_is_gtk_set) | ||
1213 | ) { | ||
1214 | mwifiex_send_single_packet(priv, ptr, ptr_index, flags); | ||
1215 | /* ra_list_spinlock has been freed in | ||
1216 | mwifiex_send_single_packet() */ | ||
1217 | } else { | ||
1218 | if (mwifiex_is_ampdu_allowed(priv, tid)) { | ||
1219 | if (mwifiex_space_avail_for_new_ba_stream(adapter)) { | ||
1220 | mwifiex_11n_create_tx_ba_stream_tbl(priv, | ||
1221 | ptr->ra, tid, | ||
1222 | BA_STREAM_SETUP_INPROGRESS); | ||
1223 | mwifiex_send_addba(priv, tid, ptr->ra); | ||
1224 | } else if (mwifiex_find_stream_to_delete | ||
1225 | (priv, tid, &tid_del, ra)) { | ||
1226 | mwifiex_11n_create_tx_ba_stream_tbl(priv, | ||
1227 | ptr->ra, tid, | ||
1228 | BA_STREAM_SETUP_INPROGRESS); | ||
1229 | mwifiex_send_delba(priv, tid_del, ra, 1); | ||
1230 | } | ||
1231 | } | ||
1232 | /* Minimum number of AMSDU */ | ||
1233 | #define MIN_NUM_AMSDU 2 | ||
1234 | if (mwifiex_is_amsdu_allowed(priv, tid) && | ||
1235 | (mwifiex_num_pkts_in_txq(priv, ptr, adapter->tx_buf_size) >= | ||
1236 | MIN_NUM_AMSDU)) | ||
1237 | mwifiex_11n_aggregate_pkt(priv, ptr, INTF_HEADER_LEN, | ||
1238 | ptr_index, flags); | ||
1239 | /* ra_list_spinlock has been freed in | ||
1240 | mwifiex_11n_aggregate_pkt() */ | ||
1241 | else | ||
1242 | mwifiex_send_single_packet(priv, ptr, ptr_index, flags); | ||
1243 | /* ra_list_spinlock has been freed in | ||
1244 | mwifiex_send_single_packet() */ | ||
1245 | } | ||
1246 | return 0; | ||
1247 | } | ||
1248 | |||
1249 | /* | ||
1250 | * This function transmits the highest priority packet awaiting in the | ||
1251 | * WMM Queues. | ||
1252 | */ | ||
1253 | void | ||
1254 | mwifiex_wmm_process_tx(struct mwifiex_adapter *adapter) | ||
1255 | { | ||
1256 | do { | ||
1257 | /* Check if busy */ | ||
1258 | if (adapter->data_sent || adapter->tx_lock_flag) | ||
1259 | break; | ||
1260 | |||
1261 | if (mwifiex_dequeue_tx_packet(adapter)) | ||
1262 | break; | ||
1263 | } while (!mwifiex_wmm_lists_empty(adapter)); | ||
1264 | } | ||
diff --git a/drivers/net/wireless/mwifiex/wmm.h b/drivers/net/wireless/mwifiex/wmm.h new file mode 100644 index 000000000000..fcea1f68792f --- /dev/null +++ b/drivers/net/wireless/mwifiex/wmm.h | |||
@@ -0,0 +1,110 @@ | |||
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_ra_list_tbl *ptr) | ||
39 | { | ||
40 | struct sk_buff *skb; | ||
41 | |||
42 | if (skb_queue_empty(&ptr->skb_head)) | ||
43 | return 0; | ||
44 | |||
45 | skb = skb_peek(&ptr->skb_head); | ||
46 | |||
47 | return skb->priority; | ||
48 | } | ||
49 | |||
50 | /* | ||
51 | * This function gets the length of a list. | ||
52 | */ | ||
53 | static inline int | ||
54 | mwifiex_wmm_list_len(struct list_head *head) | ||
55 | { | ||
56 | struct list_head *pos; | ||
57 | int count = 0; | ||
58 | |||
59 | list_for_each(pos, head) | ||
60 | ++count; | ||
61 | |||
62 | return count; | ||
63 | } | ||
64 | |||
65 | /* | ||
66 | * This function checks if a RA list is empty or not. | ||
67 | */ | ||
68 | static inline u8 | ||
69 | mwifiex_wmm_is_ra_list_empty(struct list_head *ra_list_hhead) | ||
70 | { | ||
71 | struct mwifiex_ra_list_tbl *ra_list; | ||
72 | int is_list_empty; | ||
73 | |||
74 | list_for_each_entry(ra_list, ra_list_hhead, list) { | ||
75 | is_list_empty = skb_queue_empty(&ra_list->skb_head); | ||
76 | if (!is_list_empty) | ||
77 | return false; | ||
78 | } | ||
79 | |||
80 | return true; | ||
81 | } | ||
82 | |||
83 | void mwifiex_wmm_add_buf_txqueue(struct mwifiex_adapter *adapter, | ||
84 | struct sk_buff *skb); | ||
85 | void mwifiex_ralist_add(struct mwifiex_private *priv, u8 *ra); | ||
86 | |||
87 | int mwifiex_wmm_lists_empty(struct mwifiex_adapter *adapter); | ||
88 | void mwifiex_wmm_process_tx(struct mwifiex_adapter *adapter); | ||
89 | int mwifiex_is_ralist_valid(struct mwifiex_private *priv, | ||
90 | struct mwifiex_ra_list_tbl *ra_list, int tid); | ||
91 | |||
92 | u8 mwifiex_wmm_compute_drv_pkt_delay(struct mwifiex_private *priv, | ||
93 | const struct sk_buff *skb); | ||
94 | void mwifiex_wmm_init(struct mwifiex_adapter *adapter); | ||
95 | |||
96 | extern u32 mwifiex_wmm_process_association_req(struct mwifiex_private *priv, | ||
97 | u8 **assoc_buf, | ||
98 | struct ieee_types_wmm_parameter | ||
99 | *wmmie, | ||
100 | struct ieee80211_ht_cap | ||
101 | *htcap); | ||
102 | |||
103 | void mwifiex_wmm_setup_queue_priorities(struct mwifiex_private *priv, | ||
104 | struct ieee_types_wmm_parameter | ||
105 | *wmm_ie); | ||
106 | void mwifiex_wmm_setup_ac_downgrade(struct mwifiex_private *priv); | ||
107 | extern int mwifiex_ret_wmm_get_status(struct mwifiex_private *priv, | ||
108 | const struct host_cmd_ds_command *resp); | ||
109 | |||
110 | #endif /* !_MWIFIEX_WMM_H_ */ | ||