diff options
Diffstat (limited to 'drivers/net/wireless/mwifiex/sta_ioctl.c')
-rw-r--r-- | drivers/net/wireless/mwifiex/sta_ioctl.c | 1593 |
1 files changed, 1593 insertions, 0 deletions
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 | } | ||