diff options
Diffstat (limited to 'drivers/net/wireless/mwifiex/sta_ioctl.c')
-rw-r--r-- | drivers/net/wireless/mwifiex/sta_ioctl.c | 2478 |
1 files changed, 2478 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 00000000000..665a519b140 --- /dev/null +++ b/drivers/net/wireless/mwifiex/sta_ioctl.c | |||
@@ -0,0 +1,2478 @@ | |||
1 | /* | ||
2 | * Marvell Wireless LAN device driver: functions for station ioctl | ||
3 | * | ||
4 | * Copyright (C) 2011, Marvell International Ltd. | ||
5 | * | ||
6 | * This software file (the "File") is distributed by Marvell International | ||
7 | * Ltd. under the terms of the GNU General Public License Version 2, June 1991 | ||
8 | * (the "License"). You may use, redistribute and/or modify this File in | ||
9 | * accordance with the terms and conditions of the License, a copy of which | ||
10 | * is available by writing to the Free Software Foundation, Inc., | ||
11 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the | ||
12 | * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. | ||
13 | * | ||
14 | * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE | ||
15 | * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE | ||
16 | * ARE EXPRESSLY DISCLAIMED. The License provides additional details about | ||
17 | * this warranty disclaimer. | ||
18 | */ | ||
19 | |||
20 | #include "decl.h" | ||
21 | #include "ioctl.h" | ||
22 | #include "util.h" | ||
23 | #include "fw.h" | ||
24 | #include "main.h" | ||
25 | #include "wmm.h" | ||
26 | #include "11n.h" | ||
27 | #include "cfg80211.h" | ||
28 | |||
29 | /* | ||
30 | * Copies the multicast address list from device to driver. | ||
31 | * | ||
32 | * This function does not validate the destination memory for | ||
33 | * size, and the calling function must ensure enough memory is | ||
34 | * available. | ||
35 | */ | ||
36 | static int | ||
37 | mwifiex_copy_mcast_addr(struct mwifiex_multicast_list *mlist, | ||
38 | struct net_device *dev) | ||
39 | { | ||
40 | int i = 0; | ||
41 | struct netdev_hw_addr *ha; | ||
42 | |||
43 | netdev_for_each_mc_addr(ha, dev) | ||
44 | memcpy(&mlist->mac_list[i++], ha->addr, ETH_ALEN); | ||
45 | |||
46 | return i; | ||
47 | } | ||
48 | |||
49 | /* | ||
50 | * Allocate and fills a wait queue with proper parameters. | ||
51 | * | ||
52 | * This function needs to be called before an IOCTL request can be made. | ||
53 | * It can handle the following wait options: | ||
54 | * MWIFIEX_NO_WAIT - Waiting is disabled | ||
55 | * MWIFIEX_IOCTL_WAIT - Waiting is done on IOCTL wait queue | ||
56 | * MWIFIEX_CMD_WAIT - Waiting is done on command wait queue | ||
57 | * MWIFIEX_WSTATS_WAIT - Waiting is done on stats wait queue | ||
58 | */ | ||
59 | struct mwifiex_wait_queue * | ||
60 | mwifiex_alloc_fill_wait_queue(struct mwifiex_private *priv, | ||
61 | u8 wait_option) | ||
62 | { | ||
63 | struct mwifiex_wait_queue *wait = NULL; | ||
64 | |||
65 | wait = (struct mwifiex_wait_queue *) | ||
66 | kzalloc(sizeof(struct mwifiex_wait_queue), GFP_ATOMIC); | ||
67 | if (!wait) { | ||
68 | dev_err(priv->adapter->dev, "%s: fail to alloc buffer\n", | ||
69 | __func__); | ||
70 | return wait; | ||
71 | } | ||
72 | |||
73 | wait->bss_index = priv->bss_index; | ||
74 | |||
75 | switch (wait_option) { | ||
76 | case MWIFIEX_NO_WAIT: | ||
77 | wait->enabled = 0; | ||
78 | break; | ||
79 | case MWIFIEX_IOCTL_WAIT: | ||
80 | priv->ioctl_wait_q_woken = false; | ||
81 | wait->start_time = jiffies; | ||
82 | wait->wait = &priv->ioctl_wait_q; | ||
83 | wait->condition = &priv->ioctl_wait_q_woken; | ||
84 | wait->enabled = 1; | ||
85 | break; | ||
86 | case MWIFIEX_CMD_WAIT: | ||
87 | priv->cmd_wait_q_woken = false; | ||
88 | wait->start_time = jiffies; | ||
89 | wait->wait = &priv->cmd_wait_q; | ||
90 | wait->condition = &priv->cmd_wait_q_woken; | ||
91 | wait->enabled = 1; | ||
92 | break; | ||
93 | case MWIFIEX_WSTATS_WAIT: | ||
94 | priv->w_stats_wait_q_woken = false; | ||
95 | wait->start_time = jiffies; | ||
96 | wait->wait = &priv->w_stats_wait_q; | ||
97 | wait->condition = &priv->w_stats_wait_q_woken; | ||
98 | wait->enabled = 1; | ||
99 | break; | ||
100 | } | ||
101 | |||
102 | return wait; | ||
103 | } | ||
104 | |||
105 | /* | ||
106 | * Wait queue completion handler. | ||
107 | * | ||
108 | * This function waits on a particular wait queue. | ||
109 | * For NO_WAIT option, it returns immediately. It also cancels the | ||
110 | * pending IOCTL request after waking up, in case of errors. | ||
111 | */ | ||
112 | static void | ||
113 | mwifiex_wait_ioctl_complete(struct mwifiex_private *priv, | ||
114 | struct mwifiex_wait_queue *wait, | ||
115 | u8 wait_option) | ||
116 | { | ||
117 | bool cancel_flag = false; | ||
118 | |||
119 | switch (wait_option) { | ||
120 | case MWIFIEX_NO_WAIT: | ||
121 | break; | ||
122 | case MWIFIEX_IOCTL_WAIT: | ||
123 | wait_event_interruptible(priv->ioctl_wait_q, | ||
124 | priv->ioctl_wait_q_woken); | ||
125 | if (!priv->ioctl_wait_q_woken) | ||
126 | cancel_flag = true; | ||
127 | break; | ||
128 | case MWIFIEX_CMD_WAIT: | ||
129 | wait_event_interruptible(priv->cmd_wait_q, | ||
130 | priv->cmd_wait_q_woken); | ||
131 | if (!priv->cmd_wait_q_woken) | ||
132 | cancel_flag = true; | ||
133 | break; | ||
134 | case MWIFIEX_WSTATS_WAIT: | ||
135 | wait_event_interruptible(priv->w_stats_wait_q, | ||
136 | priv->w_stats_wait_q_woken); | ||
137 | if (!priv->w_stats_wait_q_woken) | ||
138 | cancel_flag = true; | ||
139 | break; | ||
140 | } | ||
141 | if (cancel_flag) { | ||
142 | mwifiex_cancel_pending_ioctl(priv->adapter, wait); | ||
143 | dev_dbg(priv->adapter->dev, "cmd: IOCTL cancel: wait=%p, wait_option=%d\n", | ||
144 | wait, wait_option); | ||
145 | } | ||
146 | |||
147 | return; | ||
148 | } | ||
149 | |||
150 | /* | ||
151 | * The function waits for the request to complete and issues the | ||
152 | * completion handler, if required. | ||
153 | */ | ||
154 | int mwifiex_request_ioctl(struct mwifiex_private *priv, | ||
155 | struct mwifiex_wait_queue *wait, | ||
156 | int status, u8 wait_option) | ||
157 | { | ||
158 | switch (status) { | ||
159 | case -EINPROGRESS: | ||
160 | dev_dbg(priv->adapter->dev, "cmd: IOCTL pending: wait=%p, wait_option=%d\n", | ||
161 | wait, wait_option); | ||
162 | atomic_inc(&priv->adapter->ioctl_pending); | ||
163 | /* Status pending, wake up main process */ | ||
164 | queue_work(priv->adapter->workqueue, &priv->adapter->main_work); | ||
165 | |||
166 | /* Wait for completion */ | ||
167 | if (wait_option) { | ||
168 | mwifiex_wait_ioctl_complete(priv, wait, wait_option); | ||
169 | status = wait->status; | ||
170 | } | ||
171 | break; | ||
172 | case 0: | ||
173 | case -1: | ||
174 | case -EBUSY: | ||
175 | default: | ||
176 | break; | ||
177 | } | ||
178 | return status; | ||
179 | } | ||
180 | EXPORT_SYMBOL_GPL(mwifiex_request_ioctl); | ||
181 | |||
182 | /* | ||
183 | * IOCTL request handler to set/get MAC address. | ||
184 | * | ||
185 | * This function prepares the correct firmware command and | ||
186 | * issues it to get the extended version information. | ||
187 | */ | ||
188 | static int mwifiex_bss_ioctl_mac_address(struct mwifiex_private *priv, | ||
189 | struct mwifiex_wait_queue *wait, | ||
190 | u8 action, u8 *mac) | ||
191 | { | ||
192 | int ret = 0; | ||
193 | |||
194 | if ((action == HostCmd_ACT_GEN_GET) && mac) { | ||
195 | memcpy(mac, priv->curr_addr, ETH_ALEN); | ||
196 | return 0; | ||
197 | } | ||
198 | |||
199 | /* Send request to firmware */ | ||
200 | ret = mwifiex_prepare_cmd(priv, HostCmd_CMD_802_11_MAC_ADDRESS, | ||
201 | action, 0, wait, mac); | ||
202 | if (!ret) | ||
203 | ret = -EINPROGRESS; | ||
204 | |||
205 | return ret; | ||
206 | } | ||
207 | |||
208 | /* | ||
209 | * Sends IOCTL request to set MAC address. | ||
210 | * | ||
211 | * This function allocates the IOCTL request buffer, fills it | ||
212 | * with requisite parameters and calls the IOCTL handler. | ||
213 | */ | ||
214 | int mwifiex_request_set_mac_address(struct mwifiex_private *priv) | ||
215 | { | ||
216 | struct mwifiex_wait_queue *wait = NULL; | ||
217 | int status = 0; | ||
218 | u8 wait_option = MWIFIEX_CMD_WAIT; | ||
219 | |||
220 | /* Allocate wait buffer */ | ||
221 | wait = mwifiex_alloc_fill_wait_queue(priv, wait_option); | ||
222 | if (!wait) | ||
223 | return -ENOMEM; | ||
224 | |||
225 | status = mwifiex_bss_ioctl_mac_address(priv, wait, HostCmd_ACT_GEN_SET, | ||
226 | NULL); | ||
227 | |||
228 | status = mwifiex_request_ioctl(priv, wait, status, wait_option); | ||
229 | if (!status) | ||
230 | memcpy(priv->netdev->dev_addr, priv->curr_addr, ETH_ALEN); | ||
231 | else | ||
232 | dev_err(priv->adapter->dev, "set mac address failed: status=%d" | ||
233 | " error_code=%#x\n", status, wait->status); | ||
234 | |||
235 | kfree(wait); | ||
236 | return status; | ||
237 | } | ||
238 | |||
239 | /* | ||
240 | * IOCTL request handler to set multicast list. | ||
241 | * | ||
242 | * This function prepares the correct firmware command and | ||
243 | * issues it to set the multicast list. | ||
244 | * | ||
245 | * This function can be used to enable promiscuous mode, or enable all | ||
246 | * multicast packets, or to enable selective multicast. | ||
247 | */ | ||
248 | static int | ||
249 | mwifiex_bss_ioctl_multicast_list(struct mwifiex_private *priv, | ||
250 | struct mwifiex_wait_queue *wait, | ||
251 | u16 action, | ||
252 | struct mwifiex_multicast_list *mcast_list) | ||
253 | { | ||
254 | int ret = 0; | ||
255 | u16 old_pkt_filter; | ||
256 | |||
257 | old_pkt_filter = priv->curr_pkt_filter; | ||
258 | if (action == HostCmd_ACT_GEN_GET) | ||
259 | return -1; | ||
260 | |||
261 | if (mcast_list->mode == MWIFIEX_PROMISC_MODE) { | ||
262 | dev_dbg(priv->adapter->dev, "info: Enable Promiscuous mode\n"); | ||
263 | priv->curr_pkt_filter |= HostCmd_ACT_MAC_PROMISCUOUS_ENABLE; | ||
264 | priv->curr_pkt_filter &= | ||
265 | ~HostCmd_ACT_MAC_ALL_MULTICAST_ENABLE; | ||
266 | } else { | ||
267 | /* Multicast */ | ||
268 | priv->curr_pkt_filter &= ~HostCmd_ACT_MAC_PROMISCUOUS_ENABLE; | ||
269 | if (mcast_list->mode == MWIFIEX_MULTICAST_MODE) { | ||
270 | dev_dbg(priv->adapter->dev, | ||
271 | "info: Enabling All Multicast!\n"); | ||
272 | priv->curr_pkt_filter |= | ||
273 | HostCmd_ACT_MAC_ALL_MULTICAST_ENABLE; | ||
274 | } else { | ||
275 | priv->curr_pkt_filter &= | ||
276 | ~HostCmd_ACT_MAC_ALL_MULTICAST_ENABLE; | ||
277 | if (mcast_list->num_multicast_addr) { | ||
278 | dev_dbg(priv->adapter->dev, | ||
279 | "info: Set multicast list=%d\n", | ||
280 | mcast_list->num_multicast_addr); | ||
281 | /* Set multicast addresses to firmware */ | ||
282 | if (old_pkt_filter == priv->curr_pkt_filter) { | ||
283 | /* Send request to firmware */ | ||
284 | ret = mwifiex_prepare_cmd(priv, | ||
285 | HostCmd_CMD_MAC_MULTICAST_ADR, | ||
286 | action, 0, wait, mcast_list); | ||
287 | if (!ret) | ||
288 | ret = -EINPROGRESS; | ||
289 | } else { | ||
290 | /* Send request to firmware */ | ||
291 | ret = mwifiex_prepare_cmd(priv, | ||
292 | HostCmd_CMD_MAC_MULTICAST_ADR, | ||
293 | action, 0, NULL, | ||
294 | mcast_list); | ||
295 | } | ||
296 | } | ||
297 | } | ||
298 | } | ||
299 | dev_dbg(priv->adapter->dev, | ||
300 | "info: old_pkt_filter=%#x, curr_pkt_filter=%#x\n", | ||
301 | old_pkt_filter, priv->curr_pkt_filter); | ||
302 | if (old_pkt_filter != priv->curr_pkt_filter) { | ||
303 | ret = mwifiex_prepare_cmd(priv, HostCmd_CMD_MAC_CONTROL, action, | ||
304 | 0, wait, &priv->curr_pkt_filter); | ||
305 | if (!ret) | ||
306 | ret = -EINPROGRESS; | ||
307 | } | ||
308 | |||
309 | return ret; | ||
310 | } | ||
311 | |||
312 | /* | ||
313 | * Sends IOCTL request to set multicast list. | ||
314 | * | ||
315 | * This function allocates the IOCTL request buffer, fills it | ||
316 | * with requisite parameters and calls the IOCTL handler. | ||
317 | */ | ||
318 | void | ||
319 | mwifiex_request_set_multicast_list(struct mwifiex_private *priv, | ||
320 | struct net_device *dev) | ||
321 | { | ||
322 | struct mwifiex_wait_queue *wait = NULL; | ||
323 | struct mwifiex_multicast_list mcast_list; | ||
324 | u8 wait_option = MWIFIEX_NO_WAIT; | ||
325 | int status = 0; | ||
326 | |||
327 | /* Allocate wait buffer */ | ||
328 | wait = mwifiex_alloc_fill_wait_queue(priv, wait_option); | ||
329 | if (!wait) | ||
330 | return; | ||
331 | |||
332 | if (dev->flags & IFF_PROMISC) { | ||
333 | mcast_list.mode = MWIFIEX_PROMISC_MODE; | ||
334 | } else if (dev->flags & IFF_ALLMULTI || | ||
335 | netdev_mc_count(dev) > MWIFIEX_MAX_MULTICAST_LIST_SIZE) { | ||
336 | mcast_list.mode = MWIFIEX_ALL_MULTI_MODE; | ||
337 | } else { | ||
338 | mcast_list.mode = MWIFIEX_MULTICAST_MODE; | ||
339 | if (netdev_mc_count(dev)) | ||
340 | mcast_list.num_multicast_addr = | ||
341 | mwifiex_copy_mcast_addr(&mcast_list, dev); | ||
342 | } | ||
343 | status = mwifiex_bss_ioctl_multicast_list(priv, wait, | ||
344 | HostCmd_ACT_GEN_SET, | ||
345 | &mcast_list); | ||
346 | |||
347 | status = mwifiex_request_ioctl(priv, wait, status, wait_option); | ||
348 | if (wait && status != -EINPROGRESS) | ||
349 | kfree(wait); | ||
350 | |||
351 | return; | ||
352 | } | ||
353 | |||
354 | /* | ||
355 | * IOCTL request handler to disconnect from a BSS/IBSS. | ||
356 | */ | ||
357 | static int mwifiex_bss_ioctl_stop(struct mwifiex_private *priv, | ||
358 | struct mwifiex_wait_queue *wait, u8 *mac) | ||
359 | { | ||
360 | return mwifiex_deauthenticate(priv, wait, mac); | ||
361 | } | ||
362 | |||
363 | /* | ||
364 | * Sends IOCTL request to disconnect from a BSS. | ||
365 | * | ||
366 | * This function allocates the IOCTL request buffer, fills it | ||
367 | * with requisite parameters and calls the IOCTL handler. | ||
368 | */ | ||
369 | int mwifiex_disconnect(struct mwifiex_private *priv, u8 wait_option, u8 *mac) | ||
370 | { | ||
371 | struct mwifiex_wait_queue *wait = NULL; | ||
372 | int status = 0; | ||
373 | |||
374 | /* Allocate wait buffer */ | ||
375 | wait = mwifiex_alloc_fill_wait_queue(priv, wait_option); | ||
376 | if (!wait) | ||
377 | return -ENOMEM; | ||
378 | |||
379 | status = mwifiex_bss_ioctl_stop(priv, wait, mac); | ||
380 | |||
381 | status = mwifiex_request_ioctl(priv, wait, status, wait_option); | ||
382 | |||
383 | kfree(wait); | ||
384 | return status; | ||
385 | } | ||
386 | EXPORT_SYMBOL_GPL(mwifiex_disconnect); | ||
387 | |||
388 | /* | ||
389 | * IOCTL request handler to join a BSS/IBSS. | ||
390 | * | ||
391 | * In Ad-Hoc mode, the IBSS is created if not found in scan list. | ||
392 | * In both Ad-Hoc and infra mode, an deauthentication is performed | ||
393 | * first. | ||
394 | */ | ||
395 | static int mwifiex_bss_ioctl_start(struct mwifiex_private *priv, | ||
396 | struct mwifiex_wait_queue *wait, | ||
397 | struct mwifiex_ssid_bssid *ssid_bssid) | ||
398 | { | ||
399 | int ret = 0; | ||
400 | struct mwifiex_adapter *adapter = priv->adapter; | ||
401 | s32 i = -1; | ||
402 | |||
403 | priv->scan_block = false; | ||
404 | if (!ssid_bssid) | ||
405 | return -1; | ||
406 | |||
407 | if (priv->bss_mode == MWIFIEX_BSS_MODE_INFRA) { | ||
408 | /* Infra mode */ | ||
409 | ret = mwifiex_deauthenticate(priv, NULL, NULL); | ||
410 | if (ret) | ||
411 | return ret; | ||
412 | |||
413 | /* Search for the requested SSID in the scan table */ | ||
414 | if (ssid_bssid->ssid.ssid_len) | ||
415 | i = mwifiex_find_ssid_in_list(priv, &ssid_bssid->ssid, | ||
416 | NULL, MWIFIEX_BSS_MODE_INFRA); | ||
417 | else | ||
418 | i = mwifiex_find_bssid_in_list(priv, | ||
419 | (u8 *) &ssid_bssid->bssid, | ||
420 | MWIFIEX_BSS_MODE_INFRA); | ||
421 | if (i < 0) | ||
422 | return -1; | ||
423 | |||
424 | dev_dbg(adapter->dev, | ||
425 | "info: SSID found in scan list ... associating...\n"); | ||
426 | |||
427 | /* Clear any past association response stored for | ||
428 | * application retrieval */ | ||
429 | priv->assoc_rsp_size = 0; | ||
430 | ret = mwifiex_associate(priv, wait, &adapter->scan_table[i]); | ||
431 | if (ret) | ||
432 | return ret; | ||
433 | } else { | ||
434 | /* Adhoc mode */ | ||
435 | /* If the requested SSID matches current SSID, return */ | ||
436 | if (ssid_bssid->ssid.ssid_len && | ||
437 | (!mwifiex_ssid_cmp | ||
438 | (&priv->curr_bss_params.bss_descriptor.ssid, | ||
439 | &ssid_bssid->ssid))) | ||
440 | return 0; | ||
441 | |||
442 | /* Exit Adhoc mode first */ | ||
443 | dev_dbg(adapter->dev, "info: Sending Adhoc Stop\n"); | ||
444 | ret = mwifiex_deauthenticate(priv, NULL, NULL); | ||
445 | if (ret) | ||
446 | return ret; | ||
447 | |||
448 | priv->adhoc_is_link_sensed = false; | ||
449 | |||
450 | /* Search for the requested network in the scan table */ | ||
451 | if (ssid_bssid->ssid.ssid_len) | ||
452 | i = mwifiex_find_ssid_in_list(priv, | ||
453 | &ssid_bssid->ssid, NULL, | ||
454 | MWIFIEX_BSS_MODE_IBSS); | ||
455 | else | ||
456 | i = mwifiex_find_bssid_in_list(priv, | ||
457 | (u8 *)&ssid_bssid->bssid, | ||
458 | MWIFIEX_BSS_MODE_IBSS); | ||
459 | |||
460 | if (i >= 0) { | ||
461 | dev_dbg(adapter->dev, "info: network found in scan" | ||
462 | " list. Joining...\n"); | ||
463 | ret = mwifiex_adhoc_join(priv, wait, | ||
464 | &adapter->scan_table[i]); | ||
465 | if (ret) | ||
466 | return ret; | ||
467 | } else { /* i >= 0 */ | ||
468 | dev_dbg(adapter->dev, "info: Network not found in " | ||
469 | "the list, creating adhoc with ssid = %s\n", | ||
470 | ssid_bssid->ssid.ssid); | ||
471 | ret = mwifiex_adhoc_start(priv, wait, | ||
472 | &ssid_bssid->ssid); | ||
473 | if (ret) | ||
474 | return ret; | ||
475 | } | ||
476 | } | ||
477 | |||
478 | if (!ret) | ||
479 | ret = -EINPROGRESS; | ||
480 | |||
481 | return ret; | ||
482 | } | ||
483 | |||
484 | /* | ||
485 | * Sends IOCTL request to connect with a BSS. | ||
486 | * | ||
487 | * This function allocates the IOCTL request buffer, fills it | ||
488 | * with requisite parameters and calls the IOCTL handler. | ||
489 | */ | ||
490 | int mwifiex_bss_start(struct mwifiex_private *priv, u8 wait_option, | ||
491 | struct mwifiex_ssid_bssid *ssid_bssid) | ||
492 | { | ||
493 | struct mwifiex_wait_queue *wait = NULL; | ||
494 | struct mwifiex_ssid_bssid tmp_ssid_bssid; | ||
495 | int status = 0; | ||
496 | |||
497 | /* Stop the O.S. TX queue if needed */ | ||
498 | if (!netif_queue_stopped(priv->netdev)) | ||
499 | netif_stop_queue(priv->netdev); | ||
500 | |||
501 | /* Allocate wait buffer */ | ||
502 | wait = mwifiex_alloc_fill_wait_queue(priv, wait_option); | ||
503 | if (!wait) | ||
504 | return -ENOMEM; | ||
505 | |||
506 | if (ssid_bssid) | ||
507 | memcpy(&tmp_ssid_bssid, ssid_bssid, | ||
508 | sizeof(struct mwifiex_ssid_bssid)); | ||
509 | status = mwifiex_bss_ioctl_start(priv, wait, &tmp_ssid_bssid); | ||
510 | |||
511 | status = mwifiex_request_ioctl(priv, wait, status, wait_option); | ||
512 | |||
513 | kfree(wait); | ||
514 | return status; | ||
515 | } | ||
516 | |||
517 | /* | ||
518 | * IOCTL request handler to set host sleep configuration. | ||
519 | * | ||
520 | * This function prepares the correct firmware command and | ||
521 | * issues it. | ||
522 | */ | ||
523 | static int | ||
524 | mwifiex_pm_ioctl_hs_cfg(struct mwifiex_private *priv, | ||
525 | struct mwifiex_wait_queue *wait, | ||
526 | u16 action, struct mwifiex_ds_hs_cfg *hs_cfg) | ||
527 | { | ||
528 | struct mwifiex_adapter *adapter = priv->adapter; | ||
529 | int status = 0; | ||
530 | u32 prev_cond = 0; | ||
531 | |||
532 | switch (action) { | ||
533 | case HostCmd_ACT_GEN_SET: | ||
534 | if (adapter->pps_uapsd_mode) { | ||
535 | dev_dbg(adapter->dev, "info: Host Sleep IOCTL" | ||
536 | " is blocked in UAPSD/PPS mode\n"); | ||
537 | status = -1; | ||
538 | break; | ||
539 | } | ||
540 | if (hs_cfg->is_invoke_hostcmd) { | ||
541 | if (hs_cfg->conditions == HOST_SLEEP_CFG_CANCEL) { | ||
542 | if (!adapter->is_hs_configured) | ||
543 | /* Already cancelled */ | ||
544 | break; | ||
545 | /* Save previous condition */ | ||
546 | prev_cond = le32_to_cpu(adapter->hs_cfg | ||
547 | .conditions); | ||
548 | adapter->hs_cfg.conditions = | ||
549 | cpu_to_le32(hs_cfg->conditions); | ||
550 | } else if (hs_cfg->conditions) { | ||
551 | adapter->hs_cfg.conditions = | ||
552 | cpu_to_le32(hs_cfg->conditions); | ||
553 | adapter->hs_cfg.gpio = (u8)hs_cfg->gpio; | ||
554 | if (hs_cfg->gap) | ||
555 | adapter->hs_cfg.gap = (u8)hs_cfg->gap; | ||
556 | } else if (adapter->hs_cfg.conditions == | ||
557 | cpu_to_le32( | ||
558 | HOST_SLEEP_CFG_CANCEL)) { | ||
559 | /* Return failure if no parameters for HS | ||
560 | enable */ | ||
561 | status = -1; | ||
562 | break; | ||
563 | } | ||
564 | status = mwifiex_prepare_cmd(priv, | ||
565 | HostCmd_CMD_802_11_HS_CFG_ENH, | ||
566 | HostCmd_ACT_GEN_SET, | ||
567 | 0, wait, &adapter->hs_cfg); | ||
568 | if (!status) | ||
569 | status = -EINPROGRESS; | ||
570 | if (hs_cfg->conditions == HOST_SLEEP_CFG_CANCEL) | ||
571 | /* Restore previous condition */ | ||
572 | adapter->hs_cfg.conditions = | ||
573 | cpu_to_le32(prev_cond); | ||
574 | } else { | ||
575 | adapter->hs_cfg.conditions = | ||
576 | cpu_to_le32(hs_cfg->conditions); | ||
577 | adapter->hs_cfg.gpio = (u8)hs_cfg->gpio; | ||
578 | adapter->hs_cfg.gap = (u8)hs_cfg->gap; | ||
579 | } | ||
580 | break; | ||
581 | case HostCmd_ACT_GEN_GET: | ||
582 | hs_cfg->conditions = le32_to_cpu(adapter->hs_cfg.conditions); | ||
583 | hs_cfg->gpio = adapter->hs_cfg.gpio; | ||
584 | hs_cfg->gap = adapter->hs_cfg.gap; | ||
585 | break; | ||
586 | default: | ||
587 | status = -1; | ||
588 | break; | ||
589 | } | ||
590 | |||
591 | return status; | ||
592 | } | ||
593 | |||
594 | /* | ||
595 | * Sends IOCTL request to set Host Sleep parameters. | ||
596 | * | ||
597 | * This function allocates the IOCTL request buffer, fills it | ||
598 | * with requisite parameters and calls the IOCTL handler. | ||
599 | */ | ||
600 | int mwifiex_set_hs_params(struct mwifiex_private *priv, u16 action, | ||
601 | u8 wait_option, | ||
602 | struct mwifiex_ds_hs_cfg *hscfg) | ||
603 | { | ||
604 | int ret = 0; | ||
605 | struct mwifiex_wait_queue *wait = NULL; | ||
606 | |||
607 | if (!hscfg) | ||
608 | return -ENOMEM; | ||
609 | |||
610 | /* Allocate wait buffer */ | ||
611 | wait = mwifiex_alloc_fill_wait_queue(priv, wait_option); | ||
612 | if (!wait) | ||
613 | return -ENOMEM; | ||
614 | |||
615 | ret = mwifiex_pm_ioctl_hs_cfg(priv, wait, action, hscfg); | ||
616 | |||
617 | ret = mwifiex_request_ioctl(priv, wait, ret, wait_option); | ||
618 | |||
619 | if (wait && (ret != -EINPROGRESS)) | ||
620 | kfree(wait); | ||
621 | return ret; | ||
622 | } | ||
623 | |||
624 | /* | ||
625 | * Sends IOCTL request to cancel the existing Host Sleep configuration. | ||
626 | * | ||
627 | * This function allocates the IOCTL request buffer, fills it | ||
628 | * with requisite parameters and calls the IOCTL handler. | ||
629 | */ | ||
630 | int mwifiex_cancel_hs(struct mwifiex_private *priv, u8 wait_option) | ||
631 | { | ||
632 | int ret = 0; | ||
633 | struct mwifiex_ds_hs_cfg hscfg; | ||
634 | |||
635 | /* Cancel Host Sleep */ | ||
636 | hscfg.conditions = HOST_SLEEP_CFG_CANCEL; | ||
637 | hscfg.is_invoke_hostcmd = true; | ||
638 | ret = mwifiex_set_hs_params(priv, HostCmd_ACT_GEN_SET, | ||
639 | wait_option, &hscfg); | ||
640 | |||
641 | return ret; | ||
642 | } | ||
643 | EXPORT_SYMBOL_GPL(mwifiex_cancel_hs); | ||
644 | |||
645 | /* | ||
646 | * Sends IOCTL request to cancel the existing Host Sleep configuration. | ||
647 | * | ||
648 | * This function allocates the IOCTL request buffer, fills it | ||
649 | * with requisite parameters and calls the IOCTL handler. | ||
650 | */ | ||
651 | int mwifiex_enable_hs(struct mwifiex_adapter *adapter) | ||
652 | { | ||
653 | struct mwifiex_ds_hs_cfg hscfg; | ||
654 | |||
655 | if (adapter->hs_activated) { | ||
656 | dev_dbg(adapter->dev, "cmd: HS Already actived\n"); | ||
657 | return true; | ||
658 | } | ||
659 | |||
660 | /* Enable Host Sleep */ | ||
661 | adapter->hs_activate_wait_q_woken = false; | ||
662 | |||
663 | memset(&hscfg, 0, sizeof(struct mwifiex_hs_config_param)); | ||
664 | hscfg.is_invoke_hostcmd = true; | ||
665 | |||
666 | if (mwifiex_set_hs_params(mwifiex_get_priv(adapter, | ||
667 | MWIFIEX_BSS_ROLE_STA), | ||
668 | HostCmd_ACT_GEN_SET, | ||
669 | MWIFIEX_IOCTL_WAIT, &hscfg)) { | ||
670 | dev_err(adapter->dev, "IOCTL request HS enable failed\n"); | ||
671 | return false; | ||
672 | } | ||
673 | |||
674 | wait_event_interruptible(adapter->hs_activate_wait_q, | ||
675 | adapter->hs_activate_wait_q_woken); | ||
676 | |||
677 | return true; | ||
678 | } | ||
679 | EXPORT_SYMBOL_GPL(mwifiex_enable_hs); | ||
680 | |||
681 | /* | ||
682 | * IOCTL request handler to get signal information. | ||
683 | * | ||
684 | * This function prepares the correct firmware command and | ||
685 | * issues it to get the signal (RSSI) information. | ||
686 | * | ||
687 | * This only works in the connected mode. | ||
688 | */ | ||
689 | static int mwifiex_get_info_signal(struct mwifiex_private *priv, | ||
690 | struct mwifiex_wait_queue *wait, | ||
691 | struct mwifiex_ds_get_signal *signal) | ||
692 | { | ||
693 | int ret = 0; | ||
694 | |||
695 | if (!wait) { | ||
696 | dev_err(priv->adapter->dev, "WAIT information is not present\n"); | ||
697 | return -1; | ||
698 | } | ||
699 | |||
700 | /* Signal info can be obtained only if connected */ | ||
701 | if (!priv->media_connected) { | ||
702 | dev_dbg(priv->adapter->dev, | ||
703 | "info: Can not get signal in disconnected state\n"); | ||
704 | return -1; | ||
705 | } | ||
706 | |||
707 | /* Send request to firmware */ | ||
708 | ret = mwifiex_prepare_cmd(priv, HostCmd_CMD_RSSI_INFO, | ||
709 | HostCmd_ACT_GEN_GET, 0, wait, signal); | ||
710 | |||
711 | if (!ret) | ||
712 | ret = -EINPROGRESS; | ||
713 | |||
714 | return ret; | ||
715 | } | ||
716 | |||
717 | /* | ||
718 | * IOCTL request handler to get statistics. | ||
719 | * | ||
720 | * This function prepares the correct firmware command and | ||
721 | * issues it to get the statistics (RSSI) information. | ||
722 | */ | ||
723 | static int mwifiex_get_info_stats(struct mwifiex_private *priv, | ||
724 | struct mwifiex_wait_queue *wait, | ||
725 | struct mwifiex_ds_get_stats *log) | ||
726 | { | ||
727 | int ret = 0; | ||
728 | |||
729 | if (!wait) { | ||
730 | dev_err(priv->adapter->dev, "MWIFIEX IOCTL information is not present\n"); | ||
731 | return -1; | ||
732 | } | ||
733 | |||
734 | /* Send request to firmware */ | ||
735 | ret = mwifiex_prepare_cmd(priv, HostCmd_CMD_802_11_GET_LOG, | ||
736 | HostCmd_ACT_GEN_GET, 0, wait, log); | ||
737 | |||
738 | if (!ret) | ||
739 | ret = -EINPROGRESS; | ||
740 | |||
741 | return ret; | ||
742 | } | ||
743 | |||
744 | /* | ||
745 | * IOCTL request handler to get BSS information. | ||
746 | * | ||
747 | * This function collates the information from different driver structures | ||
748 | * to send to the user. | ||
749 | */ | ||
750 | int mwifiex_get_bss_info(struct mwifiex_private *priv, | ||
751 | struct mwifiex_bss_info *info) | ||
752 | { | ||
753 | struct mwifiex_adapter *adapter = priv->adapter; | ||
754 | struct mwifiex_bssdescriptor *bss_desc; | ||
755 | s32 tbl_idx = 0; | ||
756 | |||
757 | if (!info) | ||
758 | return -1; | ||
759 | |||
760 | /* Get current BSS info */ | ||
761 | bss_desc = &priv->curr_bss_params.bss_descriptor; | ||
762 | |||
763 | /* BSS mode */ | ||
764 | info->bss_mode = priv->bss_mode; | ||
765 | |||
766 | /* SSID */ | ||
767 | memcpy(&info->ssid, &bss_desc->ssid, | ||
768 | sizeof(struct mwifiex_802_11_ssid)); | ||
769 | |||
770 | /* BSSID */ | ||
771 | memcpy(&info->bssid, &bss_desc->mac_address, ETH_ALEN); | ||
772 | |||
773 | /* Channel */ | ||
774 | info->bss_chan = bss_desc->channel; | ||
775 | |||
776 | /* Region code */ | ||
777 | info->region_code = adapter->region_code; | ||
778 | |||
779 | /* Scan table index if connected */ | ||
780 | info->scan_table_idx = 0; | ||
781 | if (priv->media_connected) { | ||
782 | tbl_idx = | ||
783 | mwifiex_find_ssid_in_list(priv, &bss_desc->ssid, | ||
784 | bss_desc->mac_address, | ||
785 | priv->bss_mode); | ||
786 | if (tbl_idx >= 0) | ||
787 | info->scan_table_idx = tbl_idx; | ||
788 | } | ||
789 | |||
790 | /* Connection status */ | ||
791 | info->media_connected = priv->media_connected; | ||
792 | |||
793 | /* Radio status */ | ||
794 | info->radio_on = adapter->radio_on; | ||
795 | |||
796 | /* Tx power information */ | ||
797 | info->max_power_level = priv->max_tx_power_level; | ||
798 | info->min_power_level = priv->min_tx_power_level; | ||
799 | |||
800 | /* AdHoc state */ | ||
801 | info->adhoc_state = priv->adhoc_state; | ||
802 | |||
803 | /* Last beacon NF */ | ||
804 | info->bcn_nf_last = priv->bcn_nf_last; | ||
805 | |||
806 | /* wep status */ | ||
807 | if (priv->sec_info.wep_status == MWIFIEX_802_11_WEP_ENABLED) | ||
808 | info->wep_status = true; | ||
809 | else | ||
810 | info->wep_status = false; | ||
811 | |||
812 | info->is_hs_configured = adapter->is_hs_configured; | ||
813 | info->is_deep_sleep = adapter->is_deep_sleep; | ||
814 | |||
815 | return 0; | ||
816 | } | ||
817 | |||
818 | /* | ||
819 | * IOCTL request handler to get extended version information. | ||
820 | * | ||
821 | * This function prepares the correct firmware command and | ||
822 | * issues it to get the extended version information. | ||
823 | */ | ||
824 | static int mwifiex_get_info_ver_ext(struct mwifiex_private *priv, | ||
825 | struct mwifiex_wait_queue *wait, | ||
826 | struct mwifiex_ver_ext *ver_ext) | ||
827 | { | ||
828 | int ret = 0; | ||
829 | |||
830 | /* Send request to firmware */ | ||
831 | ret = mwifiex_prepare_cmd(priv, HostCmd_CMD_VERSION_EXT, | ||
832 | HostCmd_ACT_GEN_GET, 0, wait, ver_ext); | ||
833 | if (!ret) | ||
834 | ret = -EINPROGRESS; | ||
835 | |||
836 | return ret; | ||
837 | } | ||
838 | |||
839 | /* | ||
840 | * IOCTL request handler to set/get SNMP MIB parameters. | ||
841 | * | ||
842 | * This function prepares the correct firmware command and | ||
843 | * issues it. | ||
844 | * | ||
845 | * Currently the following parameters are supported - | ||
846 | * Set/get RTS Threshold | ||
847 | * Set/get fragmentation threshold | ||
848 | * Set/get retry count | ||
849 | */ | ||
850 | int mwifiex_snmp_mib_ioctl(struct mwifiex_private *priv, | ||
851 | struct mwifiex_wait_queue *wait, | ||
852 | u32 cmd_oid, u16 action, u32 *value) | ||
853 | { | ||
854 | int ret = 0; | ||
855 | |||
856 | if (!value) | ||
857 | return -1; | ||
858 | |||
859 | /* Send request to firmware */ | ||
860 | ret = mwifiex_prepare_cmd(priv, HostCmd_CMD_802_11_SNMP_MIB, | ||
861 | action, cmd_oid, wait, value); | ||
862 | |||
863 | if (!ret) | ||
864 | ret = -EINPROGRESS; | ||
865 | |||
866 | return ret; | ||
867 | } | ||
868 | |||
869 | /* | ||
870 | * IOCTL request handler to set/get band configurations. | ||
871 | * | ||
872 | * For SET operation, it performs extra checks to make sure the Ad-Hoc | ||
873 | * band and channel are compatible. Otherwise it returns an error. | ||
874 | * | ||
875 | * For GET operation, this function retrieves the following information - | ||
876 | * - Infra bands | ||
877 | * - Ad-hoc band | ||
878 | * - Ad-hoc channel | ||
879 | * - Secondary channel offset | ||
880 | */ | ||
881 | int mwifiex_radio_ioctl_band_cfg(struct mwifiex_private *priv, | ||
882 | u16 action, | ||
883 | struct mwifiex_ds_band_cfg *radio_cfg) | ||
884 | { | ||
885 | struct mwifiex_adapter *adapter = priv->adapter; | ||
886 | u8 infra_band = 0; | ||
887 | u8 adhoc_band = 0; | ||
888 | u32 adhoc_channel = 0; | ||
889 | |||
890 | if (action == HostCmd_ACT_GEN_GET) { | ||
891 | /* Infra Bands */ | ||
892 | radio_cfg->config_bands = adapter->config_bands; | ||
893 | /* Adhoc Band */ | ||
894 | radio_cfg->adhoc_start_band = adapter->adhoc_start_band; | ||
895 | /* Adhoc channel */ | ||
896 | radio_cfg->adhoc_channel = priv->adhoc_channel; | ||
897 | /* Secondary channel offset */ | ||
898 | radio_cfg->sec_chan_offset = adapter->chan_offset; | ||
899 | return 0; | ||
900 | } | ||
901 | |||
902 | /* For action = SET */ | ||
903 | infra_band = (u8) radio_cfg->config_bands; | ||
904 | adhoc_band = (u8) radio_cfg->adhoc_start_band; | ||
905 | adhoc_channel = radio_cfg->adhoc_channel; | ||
906 | |||
907 | /* SET Infra band */ | ||
908 | if ((infra_band | adapter->fw_bands) & ~adapter->fw_bands) | ||
909 | return -1; | ||
910 | |||
911 | adapter->config_bands = infra_band; | ||
912 | |||
913 | /* SET Ad-hoc Band */ | ||
914 | if ((adhoc_band | adapter->fw_bands) & ~adapter->fw_bands) | ||
915 | return -1; | ||
916 | |||
917 | if (adhoc_band) | ||
918 | adapter->adhoc_start_band = adhoc_band; | ||
919 | adapter->chan_offset = (u8) radio_cfg->sec_chan_offset; | ||
920 | /* | ||
921 | * If no adhoc_channel is supplied verify if the existing adhoc | ||
922 | * channel compiles with new adhoc_band | ||
923 | */ | ||
924 | if (!adhoc_channel) { | ||
925 | if (!mwifiex_get_cfp_by_band_and_channel_from_cfg80211 | ||
926 | (priv, adapter->adhoc_start_band, | ||
927 | priv->adhoc_channel)) { | ||
928 | /* Pass back the default channel */ | ||
929 | radio_cfg->adhoc_channel = DEFAULT_AD_HOC_CHANNEL; | ||
930 | if ((adapter->adhoc_start_band & BAND_A) | ||
931 | || (adapter->adhoc_start_band & BAND_AN)) | ||
932 | radio_cfg->adhoc_channel = | ||
933 | DEFAULT_AD_HOC_CHANNEL_A; | ||
934 | } | ||
935 | } else { /* Retrurn error if adhoc_band and | ||
936 | adhoc_channel combination is invalid */ | ||
937 | if (!mwifiex_get_cfp_by_band_and_channel_from_cfg80211 | ||
938 | (priv, adapter->adhoc_start_band, (u16) adhoc_channel)) | ||
939 | return -1; | ||
940 | priv->adhoc_channel = (u8) adhoc_channel; | ||
941 | } | ||
942 | if ((adhoc_band & BAND_GN) || (adhoc_band & BAND_AN)) | ||
943 | adapter->adhoc_11n_enabled = true; | ||
944 | else | ||
945 | adapter->adhoc_11n_enabled = false; | ||
946 | |||
947 | return 0; | ||
948 | } | ||
949 | |||
950 | /* | ||
951 | * IOCTL request handler to set/get active channel. | ||
952 | * | ||
953 | * This function performs validity checking on channel/frequency | ||
954 | * compatibility and returns failure if not valid. | ||
955 | */ | ||
956 | int mwifiex_bss_ioctl_channel(struct mwifiex_private *priv, u16 action, | ||
957 | struct mwifiex_chan_freq_power *chan) | ||
958 | { | ||
959 | struct mwifiex_adapter *adapter = priv->adapter; | ||
960 | struct mwifiex_chan_freq_power *cfp = NULL; | ||
961 | |||
962 | if (!chan) | ||
963 | return -1; | ||
964 | |||
965 | if (action == HostCmd_ACT_GEN_GET) { | ||
966 | cfp = mwifiex_get_cfp_by_band_and_channel_from_cfg80211(priv, | ||
967 | priv->curr_bss_params.band, | ||
968 | (u16) priv->curr_bss_params.bss_descriptor. | ||
969 | channel); | ||
970 | chan->channel = cfp->channel; | ||
971 | chan->freq = cfp->freq; | ||
972 | |||
973 | return 0; | ||
974 | } | ||
975 | if (!chan->channel && !chan->freq) | ||
976 | return -1; | ||
977 | if (adapter->adhoc_start_band & BAND_AN) | ||
978 | adapter->adhoc_start_band = BAND_G | BAND_B | BAND_GN; | ||
979 | else if (adapter->adhoc_start_band & BAND_A) | ||
980 | adapter->adhoc_start_band = BAND_G | BAND_B; | ||
981 | if (chan->channel) { | ||
982 | if (chan->channel <= MAX_CHANNEL_BAND_BG) | ||
983 | cfp = mwifiex_get_cfp_by_band_and_channel_from_cfg80211 | ||
984 | (priv, 0, (u16) chan->channel); | ||
985 | if (!cfp) { | ||
986 | cfp = mwifiex_get_cfp_by_band_and_channel_from_cfg80211 | ||
987 | (priv, BAND_A, (u16) chan->channel); | ||
988 | if (cfp) { | ||
989 | if (adapter->adhoc_11n_enabled) | ||
990 | adapter->adhoc_start_band = BAND_A | ||
991 | | BAND_AN; | ||
992 | else | ||
993 | adapter->adhoc_start_band = BAND_A; | ||
994 | } | ||
995 | } | ||
996 | } else { | ||
997 | if (chan->freq <= MAX_FREQUENCY_BAND_BG) | ||
998 | cfp = mwifiex_get_cfp_by_band_and_freq_from_cfg80211( | ||
999 | priv, 0, chan->freq); | ||
1000 | if (!cfp) { | ||
1001 | cfp = mwifiex_get_cfp_by_band_and_freq_from_cfg80211 | ||
1002 | (priv, BAND_A, chan->freq); | ||
1003 | if (cfp) { | ||
1004 | if (adapter->adhoc_11n_enabled) | ||
1005 | adapter->adhoc_start_band = BAND_A | ||
1006 | | BAND_AN; | ||
1007 | else | ||
1008 | adapter->adhoc_start_band = BAND_A; | ||
1009 | } | ||
1010 | } | ||
1011 | } | ||
1012 | if (!cfp || !cfp->channel) { | ||
1013 | dev_err(adapter->dev, "invalid channel/freq\n"); | ||
1014 | return -1; | ||
1015 | } | ||
1016 | priv->adhoc_channel = (u8) cfp->channel; | ||
1017 | chan->channel = cfp->channel; | ||
1018 | chan->freq = cfp->freq; | ||
1019 | |||
1020 | return 0; | ||
1021 | } | ||
1022 | |||
1023 | /* | ||
1024 | * IOCTL request handler to set/get BSS mode. | ||
1025 | * | ||
1026 | * This function prepares the correct firmware command and | ||
1027 | * issues it to set or get the BSS mode. | ||
1028 | * | ||
1029 | * In case the mode is changed, a deauthentication is performed | ||
1030 | * first by the function automatically. | ||
1031 | */ | ||
1032 | int mwifiex_bss_ioctl_mode(struct mwifiex_private *priv, | ||
1033 | struct mwifiex_wait_queue *wait, | ||
1034 | u16 action, int *mode) | ||
1035 | { | ||
1036 | int ret = 0; | ||
1037 | |||
1038 | if (!mode) | ||
1039 | return -1; | ||
1040 | |||
1041 | if (action == HostCmd_ACT_GEN_GET) { | ||
1042 | *mode = priv->bss_mode; | ||
1043 | return 0; | ||
1044 | } | ||
1045 | |||
1046 | if ((priv->bss_mode == *mode) || (*mode == MWIFIEX_BSS_MODE_AUTO)) { | ||
1047 | dev_dbg(priv->adapter->dev, | ||
1048 | "info: Already set to required mode! No change!\n"); | ||
1049 | priv->bss_mode = *mode; | ||
1050 | return 0; | ||
1051 | } | ||
1052 | |||
1053 | ret = mwifiex_deauthenticate(priv, wait, NULL); | ||
1054 | |||
1055 | priv->sec_info.authentication_mode = MWIFIEX_AUTH_MODE_OPEN; | ||
1056 | priv->bss_mode = *mode; | ||
1057 | if (priv->bss_mode != MWIFIEX_BSS_MODE_AUTO) { | ||
1058 | ret = mwifiex_prepare_cmd(priv, HostCmd_CMD_SET_BSS_MODE, | ||
1059 | HostCmd_ACT_GEN_SET, 0, wait, NULL); | ||
1060 | if (!ret) | ||
1061 | ret = -EINPROGRESS; | ||
1062 | } | ||
1063 | |||
1064 | return ret; | ||
1065 | } | ||
1066 | |||
1067 | /* | ||
1068 | * IOCTL request handler to set/get Ad-Hoc channel. | ||
1069 | * | ||
1070 | * This function prepares the correct firmware command and | ||
1071 | * issues it to set or get the ad-hoc channel. | ||
1072 | */ | ||
1073 | static int mwifiex_bss_ioctl_ibss_channel(struct mwifiex_private *priv, | ||
1074 | struct mwifiex_wait_queue *wait, | ||
1075 | u16 action, u16 *channel) | ||
1076 | { | ||
1077 | int ret = 0; | ||
1078 | |||
1079 | if (action == HostCmd_ACT_GEN_GET) { | ||
1080 | if (!priv->media_connected) { | ||
1081 | *channel = priv->adhoc_channel; | ||
1082 | return ret; | ||
1083 | } | ||
1084 | } else { | ||
1085 | priv->adhoc_channel = (u8) *channel; | ||
1086 | } | ||
1087 | |||
1088 | /* Send request to firmware */ | ||
1089 | ret = mwifiex_prepare_cmd(priv, HostCmd_CMD_802_11_RF_CHANNEL, | ||
1090 | action, 0, wait, channel); | ||
1091 | if (!ret) | ||
1092 | ret = -EINPROGRESS; | ||
1093 | |||
1094 | return ret; | ||
1095 | } | ||
1096 | |||
1097 | /* | ||
1098 | * IOCTL request handler to find a particular BSS. | ||
1099 | * | ||
1100 | * The BSS can be searched with either a BSSID or a SSID. If none of | ||
1101 | * these are provided, just the best BSS (best RSSI) is returned. | ||
1102 | */ | ||
1103 | int mwifiex_bss_ioctl_find_bss(struct mwifiex_private *priv, | ||
1104 | struct mwifiex_wait_queue *wait, | ||
1105 | struct mwifiex_ssid_bssid *ssid_bssid) | ||
1106 | { | ||
1107 | struct mwifiex_adapter *adapter = priv->adapter; | ||
1108 | int ret = 0; | ||
1109 | struct mwifiex_bssdescriptor *bss_desc; | ||
1110 | u8 zero_mac[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 }; | ||
1111 | u8 mac[ETH_ALEN]; | ||
1112 | int i = 0; | ||
1113 | |||
1114 | if (memcmp(ssid_bssid->bssid, zero_mac, sizeof(zero_mac))) { | ||
1115 | i = mwifiex_find_bssid_in_list(priv, | ||
1116 | (u8 *) ssid_bssid->bssid, | ||
1117 | priv->bss_mode); | ||
1118 | if (i < 0) { | ||
1119 | memcpy(mac, ssid_bssid->bssid, sizeof(mac)); | ||
1120 | dev_err(adapter->dev, "cannot find bssid %pM\n", mac); | ||
1121 | return -1; | ||
1122 | } | ||
1123 | bss_desc = &adapter->scan_table[i]; | ||
1124 | memcpy(&ssid_bssid->ssid, &bss_desc->ssid, | ||
1125 | sizeof(struct mwifiex_802_11_ssid)); | ||
1126 | } else if (ssid_bssid->ssid.ssid_len) { | ||
1127 | i = mwifiex_find_ssid_in_list(priv, &ssid_bssid->ssid, NULL, | ||
1128 | priv->bss_mode); | ||
1129 | if (i < 0) { | ||
1130 | dev_err(adapter->dev, "cannot find ssid %s\n", | ||
1131 | ssid_bssid->ssid.ssid); | ||
1132 | return -1; | ||
1133 | } | ||
1134 | bss_desc = &adapter->scan_table[i]; | ||
1135 | memcpy(ssid_bssid->bssid, bss_desc->mac_address, ETH_ALEN); | ||
1136 | } else { | ||
1137 | ret = mwifiex_find_best_network(priv, ssid_bssid); | ||
1138 | } | ||
1139 | |||
1140 | return ret; | ||
1141 | } | ||
1142 | |||
1143 | /* | ||
1144 | * IOCTL request handler to change Ad-Hoc channel. | ||
1145 | * | ||
1146 | * This function allocates the IOCTL request buffer, fills it | ||
1147 | * with requisite parameters and calls the IOCTL handler. | ||
1148 | * | ||
1149 | * The function follows the following steps to perform the change - | ||
1150 | * - Get current IBSS information | ||
1151 | * - Get current channel | ||
1152 | * - If no change is required, return | ||
1153 | * - If not connected, change channel and return | ||
1154 | * - If connected, | ||
1155 | * - Disconnect | ||
1156 | * - Change channel | ||
1157 | * - Perform specific SSID scan with same SSID | ||
1158 | * - Start/Join the IBSS | ||
1159 | */ | ||
1160 | int | ||
1161 | mwifiex_drv_change_adhoc_chan(struct mwifiex_private *priv, int channel) | ||
1162 | { | ||
1163 | int ret = 0; | ||
1164 | int status = 0; | ||
1165 | struct mwifiex_bss_info bss_info; | ||
1166 | struct mwifiex_wait_queue *wait = NULL; | ||
1167 | u8 wait_option = MWIFIEX_IOCTL_WAIT; | ||
1168 | struct mwifiex_ssid_bssid ssid_bssid; | ||
1169 | u16 curr_chan = 0; | ||
1170 | |||
1171 | memset(&bss_info, 0, sizeof(bss_info)); | ||
1172 | |||
1173 | /* Get BSS information */ | ||
1174 | if (mwifiex_get_bss_info(priv, &bss_info)) | ||
1175 | return -1; | ||
1176 | |||
1177 | /* Allocate wait buffer */ | ||
1178 | wait = mwifiex_alloc_fill_wait_queue(priv, wait_option); | ||
1179 | if (!wait) | ||
1180 | return -ENOMEM; | ||
1181 | |||
1182 | /* Get current channel */ | ||
1183 | status = mwifiex_bss_ioctl_ibss_channel(priv, wait, HostCmd_ACT_GEN_GET, | ||
1184 | &curr_chan); | ||
1185 | |||
1186 | if (mwifiex_request_ioctl(priv, wait, status, wait_option)) { | ||
1187 | ret = -1; | ||
1188 | goto done; | ||
1189 | } | ||
1190 | if (curr_chan == channel) { | ||
1191 | ret = 0; | ||
1192 | goto done; | ||
1193 | } | ||
1194 | dev_dbg(priv->adapter->dev, "cmd: updating channel from %d to %d\n", | ||
1195 | curr_chan, channel); | ||
1196 | |||
1197 | if (!bss_info.media_connected) { | ||
1198 | ret = 0; | ||
1199 | goto done; | ||
1200 | } | ||
1201 | |||
1202 | /* Do disonnect */ | ||
1203 | memset(&ssid_bssid, 0, ETH_ALEN); | ||
1204 | status = mwifiex_bss_ioctl_stop(priv, wait, ssid_bssid.bssid); | ||
1205 | |||
1206 | if (mwifiex_request_ioctl(priv, wait, status, wait_option)) { | ||
1207 | ret = -1; | ||
1208 | goto done; | ||
1209 | } | ||
1210 | |||
1211 | status = mwifiex_bss_ioctl_ibss_channel(priv, wait, HostCmd_ACT_GEN_SET, | ||
1212 | (u16 *) &channel); | ||
1213 | |||
1214 | if (mwifiex_request_ioctl(priv, wait, status, wait_option)) { | ||
1215 | ret = -1; | ||
1216 | goto done; | ||
1217 | } | ||
1218 | |||
1219 | /* Do specific SSID scanning */ | ||
1220 | if (mwifiex_request_scan(priv, wait_option, &bss_info.ssid)) { | ||
1221 | ret = -1; | ||
1222 | goto done; | ||
1223 | } | ||
1224 | /* Start/Join Adhoc network */ | ||
1225 | memset(&ssid_bssid, 0, sizeof(struct mwifiex_ssid_bssid)); | ||
1226 | memcpy(&ssid_bssid.ssid, &bss_info.ssid, | ||
1227 | sizeof(struct mwifiex_802_11_ssid)); | ||
1228 | |||
1229 | status = mwifiex_bss_ioctl_start(priv, wait, &ssid_bssid); | ||
1230 | |||
1231 | if (mwifiex_request_ioctl(priv, wait, status, wait_option)) | ||
1232 | ret = -1; | ||
1233 | |||
1234 | done: | ||
1235 | kfree(wait); | ||
1236 | return ret; | ||
1237 | } | ||
1238 | |||
1239 | /* | ||
1240 | * IOCTL request handler to get current driver mode. | ||
1241 | * | ||
1242 | * This function allocates the IOCTL request buffer, fills it | ||
1243 | * with requisite parameters and calls the IOCTL handler. | ||
1244 | */ | ||
1245 | int | ||
1246 | mwifiex_drv_get_mode(struct mwifiex_private *priv, u8 wait_option) | ||
1247 | { | ||
1248 | struct mwifiex_wait_queue *wait = NULL; | ||
1249 | int status = 0; | ||
1250 | int mode = -1; | ||
1251 | |||
1252 | /* Allocate wait buffer */ | ||
1253 | wait = mwifiex_alloc_fill_wait_queue(priv, wait_option); | ||
1254 | if (!wait) | ||
1255 | return -1; | ||
1256 | |||
1257 | status = mwifiex_bss_ioctl_mode(priv, wait, HostCmd_ACT_GEN_GET, &mode); | ||
1258 | |||
1259 | status = mwifiex_request_ioctl(priv, wait, status, wait_option); | ||
1260 | |||
1261 | if (wait && (status != -EINPROGRESS)) | ||
1262 | kfree(wait); | ||
1263 | return mode; | ||
1264 | } | ||
1265 | |||
1266 | /* | ||
1267 | * IOCTL request handler to get rate. | ||
1268 | * | ||
1269 | * This function prepares the correct firmware command and | ||
1270 | * issues it to get the current rate if it is connected, | ||
1271 | * otherwise, the function returns the lowest supported rate | ||
1272 | * for the band. | ||
1273 | */ | ||
1274 | static int mwifiex_rate_ioctl_get_rate_value(struct mwifiex_private *priv, | ||
1275 | struct mwifiex_wait_queue *wait, | ||
1276 | struct mwifiex_rate_cfg *rate_cfg) | ||
1277 | { | ||
1278 | struct mwifiex_adapter *adapter = priv->adapter; | ||
1279 | int ret = 0; | ||
1280 | |||
1281 | rate_cfg->is_rate_auto = priv->is_data_rate_auto; | ||
1282 | if (!priv->media_connected) { | ||
1283 | switch (adapter->config_bands) { | ||
1284 | case BAND_B: | ||
1285 | /* Return the lowest supported rate for B band */ | ||
1286 | rate_cfg->rate = supported_rates_b[0] & 0x7f; | ||
1287 | break; | ||
1288 | case BAND_G: | ||
1289 | case BAND_G | BAND_GN: | ||
1290 | /* Return the lowest supported rate for G band */ | ||
1291 | rate_cfg->rate = supported_rates_g[0] & 0x7f; | ||
1292 | break; | ||
1293 | case BAND_B | BAND_G: | ||
1294 | case BAND_A | BAND_B | BAND_G: | ||
1295 | case BAND_A | BAND_B: | ||
1296 | case BAND_A | BAND_B | BAND_G | BAND_AN | BAND_GN: | ||
1297 | case BAND_B | BAND_G | BAND_GN: | ||
1298 | /* Return the lowest supported rate for BG band */ | ||
1299 | rate_cfg->rate = supported_rates_bg[0] & 0x7f; | ||
1300 | break; | ||
1301 | case BAND_A: | ||
1302 | case BAND_A | BAND_G: | ||
1303 | case BAND_A | BAND_G | BAND_AN | BAND_GN: | ||
1304 | case BAND_A | BAND_AN: | ||
1305 | /* Return the lowest supported rate for A band */ | ||
1306 | rate_cfg->rate = supported_rates_a[0] & 0x7f; | ||
1307 | break; | ||
1308 | case BAND_GN: | ||
1309 | /* Return the lowest supported rate for N band */ | ||
1310 | rate_cfg->rate = supported_rates_n[0] & 0x7f; | ||
1311 | break; | ||
1312 | default: | ||
1313 | dev_warn(adapter->dev, "invalid band %#x\n", | ||
1314 | adapter->config_bands); | ||
1315 | break; | ||
1316 | } | ||
1317 | } else { | ||
1318 | /* Send request to firmware */ | ||
1319 | ret = mwifiex_prepare_cmd(priv, | ||
1320 | HostCmd_CMD_802_11_TX_RATE_QUERY, | ||
1321 | HostCmd_ACT_GEN_GET, 0, wait, NULL); | ||
1322 | if (!ret) | ||
1323 | ret = -EINPROGRESS; | ||
1324 | } | ||
1325 | |||
1326 | return ret; | ||
1327 | } | ||
1328 | |||
1329 | /* | ||
1330 | * IOCTL request handler to set rate. | ||
1331 | * | ||
1332 | * This function prepares the correct firmware command and | ||
1333 | * issues it to set the current rate. | ||
1334 | * | ||
1335 | * The function also performs validation checking on the supplied value. | ||
1336 | */ | ||
1337 | static int mwifiex_rate_ioctl_set_rate_value(struct mwifiex_private *priv, | ||
1338 | struct mwifiex_wait_queue *wait, | ||
1339 | struct mwifiex_rate_cfg *rate_cfg) | ||
1340 | { | ||
1341 | u8 rates[MWIFIEX_SUPPORTED_RATES]; | ||
1342 | u8 *rate = NULL; | ||
1343 | int rate_index = 0; | ||
1344 | u16 bitmap_rates[MAX_BITMAP_RATES_SIZE]; | ||
1345 | u32 i = 0; | ||
1346 | int ret = 0; | ||
1347 | struct mwifiex_adapter *adapter = priv->adapter; | ||
1348 | |||
1349 | if (rate_cfg->is_rate_auto) { | ||
1350 | memset(bitmap_rates, 0, sizeof(bitmap_rates)); | ||
1351 | /* Support all HR/DSSS rates */ | ||
1352 | bitmap_rates[0] = 0x000F; | ||
1353 | /* Support all OFDM rates */ | ||
1354 | bitmap_rates[1] = 0x00FF; | ||
1355 | /* Support all HT-MCSs rate */ | ||
1356 | for (i = 0; i < ARRAY_SIZE(priv->bitmap_rates) - 3; i++) | ||
1357 | bitmap_rates[i + 2] = 0xFFFF; | ||
1358 | bitmap_rates[9] = 0x3FFF; | ||
1359 | } else { | ||
1360 | memset(rates, 0, sizeof(rates)); | ||
1361 | mwifiex_get_active_data_rates(priv, rates); | ||
1362 | rate = rates; | ||
1363 | for (i = 0; (rate[i] && i < MWIFIEX_SUPPORTED_RATES); i++) { | ||
1364 | dev_dbg(adapter->dev, "info: rate=%#x wanted=%#x\n", | ||
1365 | rate[i], rate_cfg->rate); | ||
1366 | if ((rate[i] & 0x7f) == (rate_cfg->rate & 0x7f)) | ||
1367 | break; | ||
1368 | } | ||
1369 | if (!rate[i] || (i == MWIFIEX_SUPPORTED_RATES)) { | ||
1370 | dev_err(adapter->dev, "fixed data rate %#x is out " | ||
1371 | "of range\n", rate_cfg->rate); | ||
1372 | return -1; | ||
1373 | } | ||
1374 | memset(bitmap_rates, 0, sizeof(bitmap_rates)); | ||
1375 | |||
1376 | rate_index = | ||
1377 | mwifiex_data_rate_to_index(adapter, rate_cfg->rate); | ||
1378 | |||
1379 | /* Only allow b/g rates to be set */ | ||
1380 | if (rate_index >= MWIFIEX_RATE_INDEX_HRDSSS0 && | ||
1381 | rate_index <= MWIFIEX_RATE_INDEX_HRDSSS3) { | ||
1382 | bitmap_rates[0] = 1 << rate_index; | ||
1383 | } else { | ||
1384 | rate_index -= 1; /* There is a 0x00 in the table */ | ||
1385 | if (rate_index >= MWIFIEX_RATE_INDEX_OFDM0 && | ||
1386 | rate_index <= MWIFIEX_RATE_INDEX_OFDM7) | ||
1387 | bitmap_rates[1] = 1 << (rate_index - | ||
1388 | MWIFIEX_RATE_INDEX_OFDM0); | ||
1389 | } | ||
1390 | } | ||
1391 | |||
1392 | /* Send request to firmware */ | ||
1393 | ret = mwifiex_prepare_cmd(priv, HostCmd_CMD_TX_RATE_CFG, | ||
1394 | HostCmd_ACT_GEN_SET, 0, wait, bitmap_rates); | ||
1395 | if (!ret) | ||
1396 | ret = -EINPROGRESS; | ||
1397 | |||
1398 | return ret; | ||
1399 | } | ||
1400 | |||
1401 | /* | ||
1402 | * IOCTL request handler to set/get rate. | ||
1403 | * | ||
1404 | * This function can be used to set/get either the rate value or the | ||
1405 | * rate index. | ||
1406 | */ | ||
1407 | static int mwifiex_rate_ioctl_cfg(struct mwifiex_private *priv, | ||
1408 | struct mwifiex_wait_queue *wait, | ||
1409 | struct mwifiex_rate_cfg *rate_cfg) | ||
1410 | { | ||
1411 | int status = 0; | ||
1412 | |||
1413 | if (!rate_cfg) | ||
1414 | return -1; | ||
1415 | |||
1416 | if (rate_cfg->action == HostCmd_ACT_GEN_GET) | ||
1417 | status = mwifiex_rate_ioctl_get_rate_value( | ||
1418 | priv, wait, rate_cfg); | ||
1419 | else | ||
1420 | status = mwifiex_rate_ioctl_set_rate_value( | ||
1421 | priv, wait, rate_cfg); | ||
1422 | |||
1423 | return status; | ||
1424 | } | ||
1425 | |||
1426 | /* | ||
1427 | * Sends IOCTL request to get the data rate. | ||
1428 | * | ||
1429 | * This function allocates the IOCTL request buffer, fills it | ||
1430 | * with requisite parameters and calls the IOCTL handler. | ||
1431 | */ | ||
1432 | int mwifiex_drv_get_data_rate(struct mwifiex_private *priv, | ||
1433 | struct mwifiex_rate_cfg *rate) | ||
1434 | { | ||
1435 | int ret = 0; | ||
1436 | struct mwifiex_wait_queue *wait = NULL; | ||
1437 | u8 wait_option = MWIFIEX_IOCTL_WAIT; | ||
1438 | |||
1439 | /* Allocate wait buffer */ | ||
1440 | wait = mwifiex_alloc_fill_wait_queue(priv, wait_option); | ||
1441 | if (!wait) | ||
1442 | return -ENOMEM; | ||
1443 | |||
1444 | memset(rate, 0, sizeof(struct mwifiex_rate_cfg)); | ||
1445 | rate->action = HostCmd_ACT_GEN_GET; | ||
1446 | ret = mwifiex_rate_ioctl_cfg(priv, wait, rate); | ||
1447 | |||
1448 | ret = mwifiex_request_ioctl(priv, wait, ret, wait_option); | ||
1449 | if (!ret) { | ||
1450 | if (rate && rate->is_rate_auto) | ||
1451 | rate->rate = mwifiex_index_to_data_rate(priv->adapter, | ||
1452 | priv->tx_rate, priv->tx_htinfo); | ||
1453 | else if (rate) | ||
1454 | rate->rate = priv->data_rate; | ||
1455 | } else { | ||
1456 | ret = -1; | ||
1457 | } | ||
1458 | |||
1459 | kfree(wait); | ||
1460 | return ret; | ||
1461 | } | ||
1462 | |||
1463 | /* | ||
1464 | * IOCTL request handler to set tx power configuration. | ||
1465 | * | ||
1466 | * This function prepares the correct firmware command and | ||
1467 | * issues it. | ||
1468 | * | ||
1469 | * For non-auto power mode, all the following power groups are set - | ||
1470 | * - Modulation class HR/DSSS | ||
1471 | * - Modulation class OFDM | ||
1472 | * - Modulation class HTBW20 | ||
1473 | * - Modulation class HTBW40 | ||
1474 | */ | ||
1475 | static int mwifiex_power_ioctl_set_power(struct mwifiex_private *priv, | ||
1476 | struct mwifiex_wait_queue *wait, | ||
1477 | struct mwifiex_power_cfg *power_cfg) | ||
1478 | { | ||
1479 | int ret = 0; | ||
1480 | struct host_cmd_ds_txpwr_cfg *txp_cfg = NULL; | ||
1481 | struct mwifiex_types_power_group *pg_tlv = NULL; | ||
1482 | struct mwifiex_power_group *pg = NULL; | ||
1483 | u8 *buf = NULL; | ||
1484 | u16 dbm = 0; | ||
1485 | |||
1486 | if (!power_cfg->is_power_auto) { | ||
1487 | dbm = (u16) power_cfg->power_level; | ||
1488 | if ((dbm < priv->min_tx_power_level) || | ||
1489 | (dbm > priv->max_tx_power_level)) { | ||
1490 | dev_err(priv->adapter->dev, "txpower value %d dBm" | ||
1491 | " is out of range (%d dBm-%d dBm)\n", | ||
1492 | dbm, priv->min_tx_power_level, | ||
1493 | priv->max_tx_power_level); | ||
1494 | return -1; | ||
1495 | } | ||
1496 | } | ||
1497 | buf = kzalloc(MWIFIEX_SIZE_OF_CMD_BUFFER, GFP_KERNEL); | ||
1498 | if (!buf) { | ||
1499 | dev_err(priv->adapter->dev, "%s: failed to alloc cmd buffer\n", | ||
1500 | __func__); | ||
1501 | return -1; | ||
1502 | } | ||
1503 | |||
1504 | txp_cfg = (struct host_cmd_ds_txpwr_cfg *) buf; | ||
1505 | txp_cfg->action = cpu_to_le16(HostCmd_ACT_GEN_SET); | ||
1506 | if (!power_cfg->is_power_auto) { | ||
1507 | txp_cfg->mode = cpu_to_le32(1); | ||
1508 | pg_tlv = (struct mwifiex_types_power_group *) (buf + | ||
1509 | sizeof(struct host_cmd_ds_txpwr_cfg)); | ||
1510 | pg_tlv->type = TLV_TYPE_POWER_GROUP; | ||
1511 | pg_tlv->length = 4 * sizeof(struct mwifiex_power_group); | ||
1512 | pg = (struct mwifiex_power_group *) (buf + | ||
1513 | sizeof(struct host_cmd_ds_txpwr_cfg) + | ||
1514 | sizeof(struct mwifiex_types_power_group)); | ||
1515 | /* Power group for modulation class HR/DSSS */ | ||
1516 | pg->first_rate_code = 0x00; | ||
1517 | pg->last_rate_code = 0x03; | ||
1518 | pg->modulation_class = MOD_CLASS_HR_DSSS; | ||
1519 | pg->power_step = 0; | ||
1520 | pg->power_min = (s8) dbm; | ||
1521 | pg->power_max = (s8) dbm; | ||
1522 | pg++; | ||
1523 | /* Power group for modulation class OFDM */ | ||
1524 | pg->first_rate_code = 0x00; | ||
1525 | pg->last_rate_code = 0x07; | ||
1526 | pg->modulation_class = MOD_CLASS_OFDM; | ||
1527 | pg->power_step = 0; | ||
1528 | pg->power_min = (s8) dbm; | ||
1529 | pg->power_max = (s8) dbm; | ||
1530 | pg++; | ||
1531 | /* Power group for modulation class HTBW20 */ | ||
1532 | pg->first_rate_code = 0x00; | ||
1533 | pg->last_rate_code = 0x20; | ||
1534 | pg->modulation_class = MOD_CLASS_HT; | ||
1535 | pg->power_step = 0; | ||
1536 | pg->power_min = (s8) dbm; | ||
1537 | pg->power_max = (s8) dbm; | ||
1538 | pg->ht_bandwidth = HT_BW_20; | ||
1539 | pg++; | ||
1540 | /* Power group for modulation class HTBW40 */ | ||
1541 | pg->first_rate_code = 0x00; | ||
1542 | pg->last_rate_code = 0x20; | ||
1543 | pg->modulation_class = MOD_CLASS_HT; | ||
1544 | pg->power_step = 0; | ||
1545 | pg->power_min = (s8) dbm; | ||
1546 | pg->power_max = (s8) dbm; | ||
1547 | pg->ht_bandwidth = HT_BW_40; | ||
1548 | } | ||
1549 | /* Send request to firmware */ | ||
1550 | ret = mwifiex_prepare_cmd(priv, HostCmd_CMD_TXPWR_CFG, | ||
1551 | HostCmd_ACT_GEN_SET, 0, wait, buf); | ||
1552 | if (!ret) | ||
1553 | ret = -EINPROGRESS; | ||
1554 | kfree(buf); | ||
1555 | |||
1556 | return ret; | ||
1557 | } | ||
1558 | |||
1559 | /* | ||
1560 | * IOCTL request handler to get power save mode. | ||
1561 | * | ||
1562 | * This function prepares the correct firmware command and | ||
1563 | * issues it. | ||
1564 | */ | ||
1565 | static int mwifiex_pm_ioctl_ps_mode(struct mwifiex_private *priv, | ||
1566 | struct mwifiex_wait_queue *wait, | ||
1567 | u32 *ps_mode, u16 action) | ||
1568 | { | ||
1569 | int ret = 0; | ||
1570 | struct mwifiex_adapter *adapter = priv->adapter; | ||
1571 | u16 sub_cmd; | ||
1572 | |||
1573 | if (action == HostCmd_ACT_GEN_SET) { | ||
1574 | if (*ps_mode) | ||
1575 | adapter->ps_mode = MWIFIEX_802_11_POWER_MODE_PSP; | ||
1576 | else | ||
1577 | adapter->ps_mode = MWIFIEX_802_11_POWER_MODE_CAM; | ||
1578 | sub_cmd = (*ps_mode) ? EN_AUTO_PS : DIS_AUTO_PS; | ||
1579 | ret = mwifiex_prepare_cmd(priv, HostCmd_CMD_802_11_PS_MODE_ENH, | ||
1580 | sub_cmd, BITMAP_STA_PS, wait, NULL); | ||
1581 | if ((!ret) && (sub_cmd == DIS_AUTO_PS)) | ||
1582 | ret = mwifiex_prepare_cmd(priv, | ||
1583 | HostCmd_CMD_802_11_PS_MODE_ENH, GET_PS, | ||
1584 | 0, NULL, NULL); | ||
1585 | } else { | ||
1586 | ret = mwifiex_prepare_cmd(priv, HostCmd_CMD_802_11_PS_MODE_ENH, | ||
1587 | GET_PS, 0, wait, NULL); | ||
1588 | } | ||
1589 | |||
1590 | if (!ret) | ||
1591 | ret = -EINPROGRESS; | ||
1592 | |||
1593 | return ret; | ||
1594 | } | ||
1595 | |||
1596 | /* | ||
1597 | * IOCTL request handler to set/reset WPA IE. | ||
1598 | * | ||
1599 | * The supplied WPA IE is treated as a opaque buffer. Only the first field | ||
1600 | * is checked to determine WPA version. If buffer length is zero, the existing | ||
1601 | * WPA IE is reset. | ||
1602 | */ | ||
1603 | static int mwifiex_set_wpa_ie_helper(struct mwifiex_private *priv, | ||
1604 | u8 *ie_data_ptr, u16 ie_len) | ||
1605 | { | ||
1606 | if (ie_len) { | ||
1607 | if (ie_len > sizeof(priv->wpa_ie)) { | ||
1608 | dev_err(priv->adapter->dev, | ||
1609 | "failed to copy WPA IE, too big\n"); | ||
1610 | return -1; | ||
1611 | } | ||
1612 | memcpy(priv->wpa_ie, ie_data_ptr, ie_len); | ||
1613 | priv->wpa_ie_len = (u8) ie_len; | ||
1614 | dev_dbg(priv->adapter->dev, "cmd: Set Wpa_ie_len=%d IE=%#x\n", | ||
1615 | priv->wpa_ie_len, priv->wpa_ie[0]); | ||
1616 | |||
1617 | if (priv->wpa_ie[0] == WLAN_EID_WPA) { | ||
1618 | priv->sec_info.wpa_enabled = true; | ||
1619 | } else if (priv->wpa_ie[0] == WLAN_EID_RSN) { | ||
1620 | priv->sec_info.wpa2_enabled = true; | ||
1621 | } else { | ||
1622 | priv->sec_info.wpa_enabled = false; | ||
1623 | priv->sec_info.wpa2_enabled = false; | ||
1624 | } | ||
1625 | } else { | ||
1626 | memset(priv->wpa_ie, 0, sizeof(priv->wpa_ie)); | ||
1627 | priv->wpa_ie_len = 0; | ||
1628 | dev_dbg(priv->adapter->dev, "info: reset wpa_ie_len=%d IE=%#x\n", | ||
1629 | priv->wpa_ie_len, priv->wpa_ie[0]); | ||
1630 | priv->sec_info.wpa_enabled = false; | ||
1631 | priv->sec_info.wpa2_enabled = false; | ||
1632 | } | ||
1633 | |||
1634 | return 0; | ||
1635 | } | ||
1636 | |||
1637 | /* | ||
1638 | * IOCTL request handler to set/reset WAPI IE. | ||
1639 | * | ||
1640 | * The supplied WAPI IE is treated as a opaque buffer. Only the first field | ||
1641 | * is checked to internally enable WAPI. If buffer length is zero, the existing | ||
1642 | * WAPI IE is reset. | ||
1643 | */ | ||
1644 | static int mwifiex_set_wapi_ie(struct mwifiex_private *priv, | ||
1645 | u8 *ie_data_ptr, u16 ie_len) | ||
1646 | { | ||
1647 | if (ie_len) { | ||
1648 | if (ie_len > sizeof(priv->wapi_ie)) { | ||
1649 | dev_dbg(priv->adapter->dev, | ||
1650 | "info: failed to copy WAPI IE, too big\n"); | ||
1651 | return -1; | ||
1652 | } | ||
1653 | memcpy(priv->wapi_ie, ie_data_ptr, ie_len); | ||
1654 | priv->wapi_ie_len = ie_len; | ||
1655 | dev_dbg(priv->adapter->dev, "cmd: Set wapi_ie_len=%d IE=%#x\n", | ||
1656 | priv->wapi_ie_len, priv->wapi_ie[0]); | ||
1657 | |||
1658 | if (priv->wapi_ie[0] == WLAN_EID_BSS_AC_ACCESS_DELAY) | ||
1659 | priv->sec_info.wapi_enabled = true; | ||
1660 | } else { | ||
1661 | memset(priv->wapi_ie, 0, sizeof(priv->wapi_ie)); | ||
1662 | priv->wapi_ie_len = ie_len; | ||
1663 | dev_dbg(priv->adapter->dev, | ||
1664 | "info: Reset wapi_ie_len=%d IE=%#x\n", | ||
1665 | priv->wapi_ie_len, priv->wapi_ie[0]); | ||
1666 | priv->sec_info.wapi_enabled = false; | ||
1667 | } | ||
1668 | return 0; | ||
1669 | } | ||
1670 | |||
1671 | /* | ||
1672 | * IOCTL request handler to set WAPI key. | ||
1673 | * | ||
1674 | * This function prepares the correct firmware command and | ||
1675 | * issues it. | ||
1676 | */ | ||
1677 | static int mwifiex_sec_ioctl_set_wapi_key(struct mwifiex_adapter *adapter, | ||
1678 | struct mwifiex_wait_queue *wait, | ||
1679 | struct mwifiex_ds_encrypt_key *encrypt_key) | ||
1680 | { | ||
1681 | int ret = 0; | ||
1682 | struct mwifiex_private *priv = adapter->priv[wait->bss_index]; | ||
1683 | |||
1684 | ret = mwifiex_prepare_cmd(priv, HostCmd_CMD_802_11_KEY_MATERIAL, | ||
1685 | HostCmd_ACT_GEN_SET, KEY_INFO_ENABLED, | ||
1686 | wait, encrypt_key); | ||
1687 | if (!ret) | ||
1688 | ret = -EINPROGRESS; | ||
1689 | |||
1690 | return ret; | ||
1691 | } | ||
1692 | |||
1693 | /* | ||
1694 | * IOCTL request handler to set/get authentication mode. | ||
1695 | */ | ||
1696 | static int mwifiex_set_auth_mode(struct mwifiex_private *priv, u32 auth_mode) | ||
1697 | { | ||
1698 | int ret = 0; | ||
1699 | |||
1700 | priv->sec_info.authentication_mode = auth_mode; | ||
1701 | if (priv->sec_info.authentication_mode == MWIFIEX_AUTH_MODE_NETWORKEAP) | ||
1702 | ret = mwifiex_set_wpa_ie_helper(priv, NULL, 0); | ||
1703 | |||
1704 | return ret; | ||
1705 | } | ||
1706 | |||
1707 | /* | ||
1708 | * IOCTL request handler to set WEP network key. | ||
1709 | * | ||
1710 | * This function prepares the correct firmware command and | ||
1711 | * issues it, after validation checks. | ||
1712 | */ | ||
1713 | static int mwifiex_sec_ioctl_set_wep_key(struct mwifiex_adapter *adapter, | ||
1714 | struct mwifiex_wait_queue *wait, | ||
1715 | struct mwifiex_ds_encrypt_key *encrypt_key) | ||
1716 | { | ||
1717 | int ret = 0; | ||
1718 | struct mwifiex_private *priv = adapter->priv[wait->bss_index]; | ||
1719 | struct mwifiex_wep_key *wep_key = NULL; | ||
1720 | int index; | ||
1721 | |||
1722 | if (priv->wep_key_curr_index >= NUM_WEP_KEYS) | ||
1723 | priv->wep_key_curr_index = 0; | ||
1724 | wep_key = &priv->wep_key[priv->wep_key_curr_index]; | ||
1725 | index = encrypt_key->key_index; | ||
1726 | if (encrypt_key->key_disable) { | ||
1727 | priv->sec_info.wep_status = MWIFIEX_802_11_WEP_DISABLED; | ||
1728 | } else if (!encrypt_key->key_len) { | ||
1729 | /* Copy the required key as the current key */ | ||
1730 | wep_key = &priv->wep_key[index]; | ||
1731 | if (!wep_key->key_length) { | ||
1732 | dev_err(adapter->dev, | ||
1733 | "key not set, so cannot enable it\n"); | ||
1734 | return -1; | ||
1735 | } | ||
1736 | priv->wep_key_curr_index = (u16) index; | ||
1737 | priv->sec_info.wep_status = MWIFIEX_802_11_WEP_ENABLED; | ||
1738 | } else { | ||
1739 | wep_key = &priv->wep_key[index]; | ||
1740 | /* Cleanup */ | ||
1741 | memset(wep_key, 0, sizeof(struct mwifiex_wep_key)); | ||
1742 | /* Copy the key in the driver */ | ||
1743 | memcpy(wep_key->key_material, | ||
1744 | encrypt_key->key_material, | ||
1745 | encrypt_key->key_len); | ||
1746 | wep_key->key_index = index; | ||
1747 | wep_key->key_length = encrypt_key->key_len; | ||
1748 | priv->sec_info.wep_status = MWIFIEX_802_11_WEP_ENABLED; | ||
1749 | } | ||
1750 | if (wep_key->key_length) { | ||
1751 | /* Send request to firmware */ | ||
1752 | ret = mwifiex_prepare_cmd(priv, HostCmd_CMD_802_11_KEY_MATERIAL, | ||
1753 | HostCmd_ACT_GEN_SET, 0, NULL, NULL); | ||
1754 | if (ret) | ||
1755 | return ret; | ||
1756 | } | ||
1757 | if (priv->sec_info.wep_status == MWIFIEX_802_11_WEP_ENABLED) | ||
1758 | priv->curr_pkt_filter |= HostCmd_ACT_MAC_WEP_ENABLE; | ||
1759 | else | ||
1760 | priv->curr_pkt_filter &= ~HostCmd_ACT_MAC_WEP_ENABLE; | ||
1761 | |||
1762 | /* Send request to firmware */ | ||
1763 | ret = mwifiex_prepare_cmd(priv, HostCmd_CMD_MAC_CONTROL, | ||
1764 | HostCmd_ACT_GEN_SET, 0, wait, | ||
1765 | &priv->curr_pkt_filter); | ||
1766 | if (!ret) | ||
1767 | ret = -EINPROGRESS; | ||
1768 | |||
1769 | return ret; | ||
1770 | } | ||
1771 | |||
1772 | /* | ||
1773 | * IOCTL request handler to set WPA key. | ||
1774 | * | ||
1775 | * This function prepares the correct firmware command and | ||
1776 | * issues it, after validation checks. | ||
1777 | * | ||
1778 | * Current driver only supports key length of up to 32 bytes. | ||
1779 | * | ||
1780 | * This function can also be used to disable a currently set key. | ||
1781 | */ | ||
1782 | static int mwifiex_sec_ioctl_set_wpa_key(struct mwifiex_adapter *adapter, | ||
1783 | struct mwifiex_wait_queue *wait, | ||
1784 | struct mwifiex_ds_encrypt_key *encrypt_key) | ||
1785 | { | ||
1786 | int ret = 0; | ||
1787 | struct mwifiex_private *priv = adapter->priv[wait->bss_index]; | ||
1788 | u8 remove_key = false; | ||
1789 | struct host_cmd_ds_802_11_key_material *ibss_key; | ||
1790 | |||
1791 | /* Current driver only supports key length of up to 32 bytes */ | ||
1792 | if (encrypt_key->key_len > MWIFIEX_MAX_KEY_LENGTH) { | ||
1793 | dev_err(adapter->dev, "key length too long\n"); | ||
1794 | return -1; | ||
1795 | } | ||
1796 | |||
1797 | if (priv->bss_mode == MWIFIEX_BSS_MODE_IBSS) { | ||
1798 | /* | ||
1799 | * IBSS/WPA-None uses only one key (Group) for both receiving | ||
1800 | * and sending unicast and multicast packets. | ||
1801 | */ | ||
1802 | /* Send the key as PTK to firmware */ | ||
1803 | encrypt_key->key_index = MWIFIEX_KEY_INDEX_UNICAST; | ||
1804 | ret = mwifiex_prepare_cmd(priv, HostCmd_CMD_802_11_KEY_MATERIAL, | ||
1805 | HostCmd_ACT_GEN_SET, KEY_INFO_ENABLED, | ||
1806 | NULL, encrypt_key); | ||
1807 | if (ret) | ||
1808 | return ret; | ||
1809 | |||
1810 | ibss_key = &priv->aes_key; | ||
1811 | memset(ibss_key, 0, | ||
1812 | sizeof(struct host_cmd_ds_802_11_key_material)); | ||
1813 | /* Copy the key in the driver */ | ||
1814 | memcpy(ibss_key->key_param_set.key, encrypt_key->key_material, | ||
1815 | encrypt_key->key_len); | ||
1816 | memcpy(&ibss_key->key_param_set.key_len, &encrypt_key->key_len, | ||
1817 | sizeof(ibss_key->key_param_set.key_len)); | ||
1818 | ibss_key->key_param_set.key_type_id | ||
1819 | = cpu_to_le16(KEY_TYPE_ID_TKIP); | ||
1820 | ibss_key->key_param_set.key_info | ||
1821 | = cpu_to_le16(KEY_INFO_TKIP_ENABLED); | ||
1822 | |||
1823 | /* Send the key as GTK to firmware */ | ||
1824 | encrypt_key->key_index = ~MWIFIEX_KEY_INDEX_UNICAST; | ||
1825 | } | ||
1826 | |||
1827 | if (!encrypt_key->key_index) | ||
1828 | encrypt_key->key_index = MWIFIEX_KEY_INDEX_UNICAST; | ||
1829 | |||
1830 | if (remove_key) | ||
1831 | /* Send request to firmware */ | ||
1832 | ret = mwifiex_prepare_cmd(priv, HostCmd_CMD_802_11_KEY_MATERIAL, | ||
1833 | HostCmd_ACT_GEN_SET, | ||
1834 | !(KEY_INFO_ENABLED), | ||
1835 | wait, encrypt_key); | ||
1836 | else | ||
1837 | /* Send request to firmware */ | ||
1838 | ret = mwifiex_prepare_cmd(priv, HostCmd_CMD_802_11_KEY_MATERIAL, | ||
1839 | HostCmd_ACT_GEN_SET, KEY_INFO_ENABLED, | ||
1840 | wait, encrypt_key); | ||
1841 | |||
1842 | if (!ret) | ||
1843 | ret = -EINPROGRESS; | ||
1844 | |||
1845 | return ret; | ||
1846 | } | ||
1847 | |||
1848 | /* | ||
1849 | * IOCTL request handler to set/get network keys. | ||
1850 | * | ||
1851 | * This is a generic key handling function which supports WEP, WPA | ||
1852 | * and WAPI. | ||
1853 | */ | ||
1854 | static int | ||
1855 | mwifiex_sec_ioctl_encrypt_key(struct mwifiex_private *priv, | ||
1856 | struct mwifiex_wait_queue *wait, | ||
1857 | struct mwifiex_ds_encrypt_key *encrypt_key) | ||
1858 | { | ||
1859 | int status = 0; | ||
1860 | struct mwifiex_adapter *adapter = priv->adapter; | ||
1861 | |||
1862 | if (encrypt_key->is_wapi_key) | ||
1863 | status = mwifiex_sec_ioctl_set_wapi_key(adapter, wait, | ||
1864 | encrypt_key); | ||
1865 | else if (encrypt_key->key_len > WLAN_KEY_LEN_WEP104) | ||
1866 | status = mwifiex_sec_ioctl_set_wpa_key(adapter, wait, | ||
1867 | encrypt_key); | ||
1868 | else | ||
1869 | status = mwifiex_sec_ioctl_set_wep_key(adapter, wait, | ||
1870 | encrypt_key); | ||
1871 | return status; | ||
1872 | } | ||
1873 | |||
1874 | /* | ||
1875 | * This function returns the driver version. | ||
1876 | */ | ||
1877 | int | ||
1878 | mwifiex_drv_get_driver_version(struct mwifiex_adapter *adapter, char *version, | ||
1879 | int max_len) | ||
1880 | { | ||
1881 | union { | ||
1882 | u32 l; | ||
1883 | u8 c[4]; | ||
1884 | } ver; | ||
1885 | char fw_ver[32]; | ||
1886 | |||
1887 | ver.l = adapter->fw_release_number; | ||
1888 | sprintf(fw_ver, "%u.%u.%u.p%u", ver.c[2], ver.c[1], ver.c[0], ver.c[3]); | ||
1889 | |||
1890 | snprintf(version, max_len, driver_version, fw_ver); | ||
1891 | |||
1892 | dev_dbg(adapter->dev, "info: MWIFIEX VERSION: %s\n", version); | ||
1893 | |||
1894 | return 0; | ||
1895 | } | ||
1896 | |||
1897 | /* | ||
1898 | * Sends IOCTL request to set Tx power. It can be set to either auto | ||
1899 | * or a fixed value. | ||
1900 | * | ||
1901 | * This function allocates the IOCTL request buffer, fills it | ||
1902 | * with requisite parameters and calls the IOCTL handler. | ||
1903 | */ | ||
1904 | int | ||
1905 | mwifiex_set_tx_power(struct mwifiex_private *priv, int type, int dbm) | ||
1906 | { | ||
1907 | struct mwifiex_power_cfg power_cfg; | ||
1908 | struct mwifiex_wait_queue *wait = NULL; | ||
1909 | int status = 0; | ||
1910 | int ret = 0; | ||
1911 | |||
1912 | wait = mwifiex_alloc_fill_wait_queue(priv, MWIFIEX_IOCTL_WAIT); | ||
1913 | if (!wait) | ||
1914 | return -ENOMEM; | ||
1915 | |||
1916 | if (type == NL80211_TX_POWER_FIXED) { | ||
1917 | power_cfg.is_power_auto = 0; | ||
1918 | power_cfg.power_level = dbm; | ||
1919 | } else { | ||
1920 | power_cfg.is_power_auto = 1; | ||
1921 | } | ||
1922 | status = mwifiex_power_ioctl_set_power(priv, wait, &power_cfg); | ||
1923 | |||
1924 | ret = mwifiex_request_ioctl(priv, wait, status, MWIFIEX_IOCTL_WAIT); | ||
1925 | |||
1926 | kfree(wait); | ||
1927 | return ret; | ||
1928 | } | ||
1929 | |||
1930 | /* | ||
1931 | * Sends IOCTL request to get scan table. | ||
1932 | * | ||
1933 | * This function allocates the IOCTL request buffer, fills it | ||
1934 | * with requisite parameters and calls the IOCTL handler. | ||
1935 | */ | ||
1936 | int mwifiex_get_scan_table(struct mwifiex_private *priv, u8 wait_option, | ||
1937 | struct mwifiex_scan_resp *scan_resp) | ||
1938 | { | ||
1939 | struct mwifiex_wait_queue *wait = NULL; | ||
1940 | struct mwifiex_scan_resp scan; | ||
1941 | int status = 0; | ||
1942 | |||
1943 | /* Allocate wait buffer */ | ||
1944 | wait = mwifiex_alloc_fill_wait_queue(priv, wait_option); | ||
1945 | if (!wait) | ||
1946 | return -ENOMEM; | ||
1947 | |||
1948 | status = mwifiex_scan_networks(priv, wait, HostCmd_ACT_GEN_GET, | ||
1949 | NULL, &scan); | ||
1950 | |||
1951 | status = mwifiex_request_ioctl(priv, wait, status, wait_option); | ||
1952 | if (!status) { | ||
1953 | if (scan_resp) | ||
1954 | memcpy(scan_resp, &scan, | ||
1955 | sizeof(struct mwifiex_scan_resp)); | ||
1956 | } | ||
1957 | |||
1958 | if (wait && (status != -EINPROGRESS)) | ||
1959 | kfree(wait); | ||
1960 | return status; | ||
1961 | } | ||
1962 | |||
1963 | /* | ||
1964 | * Sends IOCTL request to get signal information. | ||
1965 | * | ||
1966 | * This function allocates the IOCTL request buffer, fills it | ||
1967 | * with requisite parameters and calls the IOCTL handler. | ||
1968 | */ | ||
1969 | int mwifiex_get_signal_info(struct mwifiex_private *priv, u8 wait_option, | ||
1970 | struct mwifiex_ds_get_signal *signal) | ||
1971 | { | ||
1972 | struct mwifiex_ds_get_signal info; | ||
1973 | struct mwifiex_wait_queue *wait = NULL; | ||
1974 | int status = 0; | ||
1975 | |||
1976 | /* Allocate wait buffer */ | ||
1977 | wait = mwifiex_alloc_fill_wait_queue(priv, wait_option); | ||
1978 | if (!wait) | ||
1979 | return -ENOMEM; | ||
1980 | |||
1981 | info.selector = ALL_RSSI_INFO_MASK; | ||
1982 | |||
1983 | status = mwifiex_get_info_signal(priv, wait, &info); | ||
1984 | |||
1985 | status = mwifiex_request_ioctl(priv, wait, status, wait_option); | ||
1986 | if (!status) { | ||
1987 | if (signal) | ||
1988 | memcpy(signal, &info, | ||
1989 | sizeof(struct mwifiex_ds_get_signal)); | ||
1990 | if (info.selector & BCN_RSSI_AVG_MASK) | ||
1991 | priv->w_stats.qual.level = info.bcn_rssi_avg; | ||
1992 | if (info.selector & BCN_NF_AVG_MASK) | ||
1993 | priv->w_stats.qual.noise = info.bcn_nf_avg; | ||
1994 | } | ||
1995 | |||
1996 | if (wait && (status != -EINPROGRESS)) | ||
1997 | kfree(wait); | ||
1998 | return status; | ||
1999 | } | ||
2000 | |||
2001 | /* | ||
2002 | * Sends IOCTL request to set encryption mode. | ||
2003 | * | ||
2004 | * This function allocates the IOCTL request buffer, fills it | ||
2005 | * with requisite parameters and calls the IOCTL handler. | ||
2006 | */ | ||
2007 | static int mwifiex_set_encrypt_mode(struct mwifiex_private *priv, | ||
2008 | u8 wait_option, u32 encrypt_mode) | ||
2009 | { | ||
2010 | priv->sec_info.encryption_mode = encrypt_mode; | ||
2011 | return 0; | ||
2012 | } | ||
2013 | |||
2014 | /* | ||
2015 | * This function set the authentication parameters. It sets both encryption | ||
2016 | * mode and authentication mode, and also enables WPA if required. | ||
2017 | */ | ||
2018 | int | ||
2019 | mwifiex_set_auth(struct mwifiex_private *priv, int encrypt_mode, | ||
2020 | int auth_mode, int wpa_enabled) | ||
2021 | { | ||
2022 | if (mwifiex_set_encrypt_mode(priv, MWIFIEX_IOCTL_WAIT, encrypt_mode)) | ||
2023 | return -EFAULT; | ||
2024 | |||
2025 | if (mwifiex_set_auth_mode(priv, auth_mode)) | ||
2026 | return -EFAULT; | ||
2027 | |||
2028 | return 0; | ||
2029 | } | ||
2030 | |||
2031 | /* | ||
2032 | * Sends IOCTL request to set encoding parameters. | ||
2033 | * | ||
2034 | * This function allocates the IOCTL request buffer, fills it | ||
2035 | * with requisite parameters and calls the IOCTL handler. | ||
2036 | */ | ||
2037 | int mwifiex_set_encode(struct mwifiex_private *priv, const u8 *key, | ||
2038 | int key_len, u8 key_index, int disable) | ||
2039 | { | ||
2040 | struct mwifiex_wait_queue *wait = NULL; | ||
2041 | struct mwifiex_ds_encrypt_key encrypt_key; | ||
2042 | int status = 0; | ||
2043 | int ret = 0; | ||
2044 | |||
2045 | wait = mwifiex_alloc_fill_wait_queue(priv, MWIFIEX_IOCTL_WAIT); | ||
2046 | if (!wait) | ||
2047 | return -ENOMEM; | ||
2048 | |||
2049 | memset(&encrypt_key, 0, sizeof(struct mwifiex_ds_encrypt_key)); | ||
2050 | encrypt_key.key_len = key_len; | ||
2051 | if (!disable) { | ||
2052 | encrypt_key.key_index = key_index; | ||
2053 | if (key_len) | ||
2054 | memcpy(encrypt_key.key_material, key, key_len); | ||
2055 | } else { | ||
2056 | encrypt_key.key_disable = true; | ||
2057 | } | ||
2058 | |||
2059 | status = mwifiex_sec_ioctl_encrypt_key(priv, wait, &encrypt_key); | ||
2060 | |||
2061 | if (mwifiex_request_ioctl(priv, wait, status, MWIFIEX_IOCTL_WAIT)) | ||
2062 | ret = -EFAULT; | ||
2063 | |||
2064 | kfree(wait); | ||
2065 | return ret; | ||
2066 | } | ||
2067 | |||
2068 | /* | ||
2069 | * Sends IOCTL request to set power management parameters. | ||
2070 | * | ||
2071 | * This function allocates the IOCTL request buffer, fills it | ||
2072 | * with requisite parameters and calls the IOCTL handler. | ||
2073 | */ | ||
2074 | int | ||
2075 | mwifiex_drv_set_power(struct mwifiex_private *priv, bool power_on) | ||
2076 | { | ||
2077 | int ret = 0; | ||
2078 | int status = 0; | ||
2079 | struct mwifiex_wait_queue *wait = NULL; | ||
2080 | u32 ps_mode; | ||
2081 | |||
2082 | wait = mwifiex_alloc_fill_wait_queue(priv, MWIFIEX_IOCTL_WAIT); | ||
2083 | if (!wait) | ||
2084 | return -ENOMEM; | ||
2085 | |||
2086 | ps_mode = power_on; | ||
2087 | status = mwifiex_pm_ioctl_ps_mode(priv, wait, &ps_mode, | ||
2088 | HostCmd_ACT_GEN_SET); | ||
2089 | |||
2090 | ret = mwifiex_request_ioctl(priv, wait, status, MWIFIEX_IOCTL_WAIT); | ||
2091 | |||
2092 | kfree(wait); | ||
2093 | return ret; | ||
2094 | } | ||
2095 | |||
2096 | /* | ||
2097 | * Sends IOCTL request to get extended version. | ||
2098 | * | ||
2099 | * This function allocates the IOCTL request buffer, fills it | ||
2100 | * with requisite parameters and calls the IOCTL handler. | ||
2101 | */ | ||
2102 | int | ||
2103 | mwifiex_get_ver_ext(struct mwifiex_private *priv) | ||
2104 | { | ||
2105 | struct mwifiex_ver_ext ver_ext; | ||
2106 | struct mwifiex_wait_queue *wait = NULL; | ||
2107 | int status = 0; | ||
2108 | int ret = 0; | ||
2109 | u8 wait_option = MWIFIEX_IOCTL_WAIT; | ||
2110 | |||
2111 | /* Allocate wait buffer */ | ||
2112 | wait = mwifiex_alloc_fill_wait_queue(priv, wait_option); | ||
2113 | if (!wait) | ||
2114 | return -ENOMEM; | ||
2115 | |||
2116 | /* get fw version */ | ||
2117 | memset(&ver_ext, 0, sizeof(struct host_cmd_ds_version_ext)); | ||
2118 | status = mwifiex_get_info_ver_ext(priv, wait, &ver_ext); | ||
2119 | |||
2120 | ret = mwifiex_request_ioctl(priv, wait, status, wait_option); | ||
2121 | |||
2122 | if (ret) | ||
2123 | ret = -1; | ||
2124 | |||
2125 | kfree(wait); | ||
2126 | return ret; | ||
2127 | } | ||
2128 | |||
2129 | /* | ||
2130 | * Sends IOCTL request to get statistics information. | ||
2131 | * | ||
2132 | * This function allocates the IOCTL request buffer, fills it | ||
2133 | * with requisite parameters and calls the IOCTL handler. | ||
2134 | */ | ||
2135 | int | ||
2136 | mwifiex_get_stats_info(struct mwifiex_private *priv, | ||
2137 | struct mwifiex_ds_get_stats *log) | ||
2138 | { | ||
2139 | int ret = 0; | ||
2140 | int status = 0; | ||
2141 | struct mwifiex_wait_queue *wait = NULL; | ||
2142 | struct mwifiex_ds_get_stats get_log; | ||
2143 | u8 wait_option = MWIFIEX_IOCTL_WAIT; | ||
2144 | |||
2145 | /* Allocate wait buffer */ | ||
2146 | wait = mwifiex_alloc_fill_wait_queue(priv, wait_option); | ||
2147 | if (!wait) | ||
2148 | return -ENOMEM; | ||
2149 | |||
2150 | memset(&get_log, 0, sizeof(struct mwifiex_ds_get_stats)); | ||
2151 | status = mwifiex_get_info_stats(priv, wait, &get_log); | ||
2152 | |||
2153 | /* Send IOCTL request to MWIFIEX */ | ||
2154 | ret = mwifiex_request_ioctl(priv, wait, status, wait_option); | ||
2155 | if (!ret) { | ||
2156 | if (log) | ||
2157 | memcpy(log, &get_log, sizeof(struct | ||
2158 | mwifiex_ds_get_stats)); | ||
2159 | priv->w_stats.discard.fragment = get_log.fcs_error; | ||
2160 | priv->w_stats.discard.retries = get_log.retry; | ||
2161 | priv->w_stats.discard.misc = get_log.ack_failure; | ||
2162 | } | ||
2163 | |||
2164 | kfree(wait); | ||
2165 | return ret; | ||
2166 | } | ||
2167 | |||
2168 | /* | ||
2169 | * IOCTL request handler to read/write register. | ||
2170 | * | ||
2171 | * This function prepares the correct firmware command and | ||
2172 | * issues it. | ||
2173 | * | ||
2174 | * Access to the following registers are supported - | ||
2175 | * - MAC | ||
2176 | * - BBP | ||
2177 | * - RF | ||
2178 | * - PMIC | ||
2179 | * - CAU | ||
2180 | */ | ||
2181 | static int mwifiex_reg_mem_ioctl_reg_rw(struct mwifiex_private *priv, | ||
2182 | struct mwifiex_wait_queue *wait, | ||
2183 | struct mwifiex_ds_reg_rw *reg_rw, | ||
2184 | u16 action) | ||
2185 | { | ||
2186 | int ret = 0; | ||
2187 | u16 cmd_no; | ||
2188 | |||
2189 | switch (le32_to_cpu(reg_rw->type)) { | ||
2190 | case MWIFIEX_REG_MAC: | ||
2191 | cmd_no = HostCmd_CMD_MAC_REG_ACCESS; | ||
2192 | break; | ||
2193 | case MWIFIEX_REG_BBP: | ||
2194 | cmd_no = HostCmd_CMD_BBP_REG_ACCESS; | ||
2195 | break; | ||
2196 | case MWIFIEX_REG_RF: | ||
2197 | cmd_no = HostCmd_CMD_RF_REG_ACCESS; | ||
2198 | break; | ||
2199 | case MWIFIEX_REG_PMIC: | ||
2200 | cmd_no = HostCmd_CMD_PMIC_REG_ACCESS; | ||
2201 | break; | ||
2202 | case MWIFIEX_REG_CAU: | ||
2203 | cmd_no = HostCmd_CMD_CAU_REG_ACCESS; | ||
2204 | break; | ||
2205 | default: | ||
2206 | return -1; | ||
2207 | } | ||
2208 | |||
2209 | /* Send request to firmware */ | ||
2210 | ret = mwifiex_prepare_cmd(priv, cmd_no, action, 0, wait, reg_rw); | ||
2211 | |||
2212 | if (!ret) | ||
2213 | ret = -EINPROGRESS; | ||
2214 | |||
2215 | return ret; | ||
2216 | } | ||
2217 | |||
2218 | /* | ||
2219 | * Sends IOCTL request to write to a register. | ||
2220 | * | ||
2221 | * This function allocates the IOCTL request buffer, fills it | ||
2222 | * with requisite parameters and calls the IOCTL handler. | ||
2223 | */ | ||
2224 | int | ||
2225 | mwifiex_reg_write(struct mwifiex_private *priv, u32 reg_type, | ||
2226 | u32 reg_offset, u32 reg_value) | ||
2227 | { | ||
2228 | int ret = 0; | ||
2229 | int status = 0; | ||
2230 | struct mwifiex_wait_queue *wait = NULL; | ||
2231 | struct mwifiex_ds_reg_rw reg_rw; | ||
2232 | |||
2233 | wait = mwifiex_alloc_fill_wait_queue(priv, MWIFIEX_IOCTL_WAIT); | ||
2234 | if (!wait) | ||
2235 | return -ENOMEM; | ||
2236 | |||
2237 | reg_rw.type = cpu_to_le32(reg_type); | ||
2238 | reg_rw.offset = cpu_to_le32(reg_offset); | ||
2239 | reg_rw.value = cpu_to_le32(reg_value); | ||
2240 | status = mwifiex_reg_mem_ioctl_reg_rw(priv, wait, ®_rw, | ||
2241 | HostCmd_ACT_GEN_SET); | ||
2242 | |||
2243 | ret = mwifiex_request_ioctl(priv, wait, status, MWIFIEX_IOCTL_WAIT); | ||
2244 | |||
2245 | kfree(wait); | ||
2246 | return ret; | ||
2247 | } | ||
2248 | |||
2249 | /* | ||
2250 | * Sends IOCTL request to read from a register. | ||
2251 | * | ||
2252 | * This function allocates the IOCTL request buffer, fills it | ||
2253 | * with requisite parameters and calls the IOCTL handler. | ||
2254 | */ | ||
2255 | int | ||
2256 | mwifiex_reg_read(struct mwifiex_private *priv, u32 reg_type, | ||
2257 | u32 reg_offset, u32 *value) | ||
2258 | { | ||
2259 | int ret = 0; | ||
2260 | int status = 0; | ||
2261 | struct mwifiex_wait_queue *wait = NULL; | ||
2262 | struct mwifiex_ds_reg_rw reg_rw; | ||
2263 | |||
2264 | wait = mwifiex_alloc_fill_wait_queue(priv, MWIFIEX_IOCTL_WAIT); | ||
2265 | if (!wait) | ||
2266 | return -ENOMEM; | ||
2267 | |||
2268 | reg_rw.type = cpu_to_le32(reg_type); | ||
2269 | reg_rw.offset = cpu_to_le32(reg_offset); | ||
2270 | status = mwifiex_reg_mem_ioctl_reg_rw(priv, wait, ®_rw, | ||
2271 | HostCmd_ACT_GEN_GET); | ||
2272 | |||
2273 | ret = mwifiex_request_ioctl(priv, wait, status, MWIFIEX_IOCTL_WAIT); | ||
2274 | if (ret) | ||
2275 | goto done; | ||
2276 | |||
2277 | *value = le32_to_cpu(reg_rw.value); | ||
2278 | |||
2279 | done: | ||
2280 | kfree(wait); | ||
2281 | return ret; | ||
2282 | } | ||
2283 | |||
2284 | /* | ||
2285 | * IOCTL request handler to read EEPROM. | ||
2286 | * | ||
2287 | * This function prepares the correct firmware command and | ||
2288 | * issues it. | ||
2289 | */ | ||
2290 | static int | ||
2291 | mwifiex_reg_mem_ioctl_read_eeprom(struct mwifiex_private *priv, | ||
2292 | struct mwifiex_wait_queue *wait, | ||
2293 | struct mwifiex_ds_read_eeprom *rd_eeprom) | ||
2294 | { | ||
2295 | int ret = 0; | ||
2296 | |||
2297 | /* Send request to firmware */ | ||
2298 | ret = mwifiex_prepare_cmd(priv, HostCmd_CMD_802_11_EEPROM_ACCESS, | ||
2299 | HostCmd_ACT_GEN_GET, 0, wait, rd_eeprom); | ||
2300 | |||
2301 | if (!ret) | ||
2302 | ret = -EINPROGRESS; | ||
2303 | |||
2304 | return ret; | ||
2305 | } | ||
2306 | |||
2307 | /* | ||
2308 | * Sends IOCTL request to read from EEPROM. | ||
2309 | * | ||
2310 | * This function allocates the IOCTL request buffer, fills it | ||
2311 | * with requisite parameters and calls the IOCTL handler. | ||
2312 | */ | ||
2313 | int | ||
2314 | mwifiex_eeprom_read(struct mwifiex_private *priv, u16 offset, u16 bytes, | ||
2315 | u8 *value) | ||
2316 | { | ||
2317 | int ret = 0; | ||
2318 | int status = 0; | ||
2319 | struct mwifiex_wait_queue *wait = NULL; | ||
2320 | struct mwifiex_ds_read_eeprom rd_eeprom; | ||
2321 | |||
2322 | wait = mwifiex_alloc_fill_wait_queue(priv, MWIFIEX_IOCTL_WAIT); | ||
2323 | if (!wait) | ||
2324 | return -ENOMEM; | ||
2325 | |||
2326 | rd_eeprom.offset = cpu_to_le16((u16) offset); | ||
2327 | rd_eeprom.byte_count = cpu_to_le16((u16) bytes); | ||
2328 | status = mwifiex_reg_mem_ioctl_read_eeprom(priv, wait, &rd_eeprom); | ||
2329 | |||
2330 | ret = mwifiex_request_ioctl(priv, wait, status, MWIFIEX_IOCTL_WAIT); | ||
2331 | if (ret) | ||
2332 | goto done; | ||
2333 | |||
2334 | memcpy(value, rd_eeprom.value, MAX_EEPROM_DATA); | ||
2335 | done: | ||
2336 | kfree(wait); | ||
2337 | return ret; | ||
2338 | } | ||
2339 | |||
2340 | /* | ||
2341 | * This function sets a generic IE. In addition to generic IE, it can | ||
2342 | * also handle WPA, WPA2 and WAPI IEs. | ||
2343 | */ | ||
2344 | static int | ||
2345 | mwifiex_set_gen_ie_helper(struct mwifiex_private *priv, u8 *ie_data_ptr, | ||
2346 | u16 ie_len) | ||
2347 | { | ||
2348 | int ret = 0; | ||
2349 | struct ieee_types_vendor_header *pvendor_ie; | ||
2350 | const u8 wpa_oui[] = { 0x00, 0x50, 0xf2, 0x01 }; | ||
2351 | const u8 wps_oui[] = { 0x00, 0x50, 0xf2, 0x04 }; | ||
2352 | |||
2353 | /* If the passed length is zero, reset the buffer */ | ||
2354 | if (!ie_len) { | ||
2355 | priv->gen_ie_buf_len = 0; | ||
2356 | priv->wps.session_enable = false; | ||
2357 | |||
2358 | return 0; | ||
2359 | } else if (!ie_data_ptr) { | ||
2360 | return -1; | ||
2361 | } | ||
2362 | pvendor_ie = (struct ieee_types_vendor_header *) ie_data_ptr; | ||
2363 | /* Test to see if it is a WPA IE, if not, then it is a gen IE */ | ||
2364 | if (((pvendor_ie->element_id == WLAN_EID_WPA) | ||
2365 | && (!memcmp(pvendor_ie->oui, wpa_oui, sizeof(wpa_oui)))) | ||
2366 | || (pvendor_ie->element_id == WLAN_EID_RSN)) { | ||
2367 | |||
2368 | /* IE is a WPA/WPA2 IE so call set_wpa function */ | ||
2369 | ret = mwifiex_set_wpa_ie_helper(priv, ie_data_ptr, ie_len); | ||
2370 | priv->wps.session_enable = false; | ||
2371 | |||
2372 | return ret; | ||
2373 | } else if (pvendor_ie->element_id == WLAN_EID_BSS_AC_ACCESS_DELAY) { | ||
2374 | /* IE is a WAPI IE so call set_wapi function */ | ||
2375 | ret = mwifiex_set_wapi_ie(priv, ie_data_ptr, ie_len); | ||
2376 | |||
2377 | return ret; | ||
2378 | } | ||
2379 | /* | ||
2380 | * Verify that the passed length is not larger than the | ||
2381 | * available space remaining in the buffer | ||
2382 | */ | ||
2383 | if (ie_len < (sizeof(priv->gen_ie_buf) - priv->gen_ie_buf_len)) { | ||
2384 | |||
2385 | /* Test to see if it is a WPS IE, if so, enable | ||
2386 | * wps session flag | ||
2387 | */ | ||
2388 | pvendor_ie = (struct ieee_types_vendor_header *) ie_data_ptr; | ||
2389 | if ((pvendor_ie->element_id == WLAN_EID_VENDOR_SPECIFIC) | ||
2390 | && (!memcmp(pvendor_ie->oui, wps_oui, | ||
2391 | sizeof(wps_oui)))) { | ||
2392 | priv->wps.session_enable = true; | ||
2393 | dev_dbg(priv->adapter->dev, | ||
2394 | "info: WPS Session Enabled.\n"); | ||
2395 | } | ||
2396 | |||
2397 | /* Append the passed data to the end of the | ||
2398 | genIeBuffer */ | ||
2399 | memcpy(priv->gen_ie_buf + priv->gen_ie_buf_len, ie_data_ptr, | ||
2400 | ie_len); | ||
2401 | /* Increment the stored buffer length by the | ||
2402 | size passed */ | ||
2403 | priv->gen_ie_buf_len += ie_len; | ||
2404 | } else { | ||
2405 | /* Passed data does not fit in the remaining | ||
2406 | buffer space */ | ||
2407 | ret = -1; | ||
2408 | } | ||
2409 | |||
2410 | /* Return 0, or -1 for error case */ | ||
2411 | return ret; | ||
2412 | } | ||
2413 | |||
2414 | /* | ||
2415 | * IOCTL request handler to set/get generic IE. | ||
2416 | * | ||
2417 | * In addition to various generic IEs, this function can also be | ||
2418 | * used to set the ARP filter. | ||
2419 | */ | ||
2420 | static int mwifiex_misc_ioctl_gen_ie(struct mwifiex_private *priv, | ||
2421 | struct mwifiex_ds_misc_gen_ie *gen_ie, | ||
2422 | u16 action) | ||
2423 | { | ||
2424 | struct mwifiex_adapter *adapter = priv->adapter; | ||
2425 | |||
2426 | switch (gen_ie->type) { | ||
2427 | case MWIFIEX_IE_TYPE_GEN_IE: | ||
2428 | if (action == HostCmd_ACT_GEN_GET) { | ||
2429 | gen_ie->len = priv->wpa_ie_len; | ||
2430 | memcpy(gen_ie->ie_data, priv->wpa_ie, gen_ie->len); | ||
2431 | } else { | ||
2432 | mwifiex_set_gen_ie_helper(priv, gen_ie->ie_data, | ||
2433 | (u16) gen_ie->len); | ||
2434 | } | ||
2435 | break; | ||
2436 | case MWIFIEX_IE_TYPE_ARP_FILTER: | ||
2437 | memset(adapter->arp_filter, 0, sizeof(adapter->arp_filter)); | ||
2438 | if (gen_ie->len > ARP_FILTER_MAX_BUF_SIZE) { | ||
2439 | adapter->arp_filter_size = 0; | ||
2440 | dev_err(adapter->dev, "invalid ARP filter size\n"); | ||
2441 | return -1; | ||
2442 | } else { | ||
2443 | memcpy(adapter->arp_filter, gen_ie->ie_data, | ||
2444 | gen_ie->len); | ||
2445 | adapter->arp_filter_size = gen_ie->len; | ||
2446 | } | ||
2447 | break; | ||
2448 | default: | ||
2449 | dev_err(adapter->dev, "invalid IE type\n"); | ||
2450 | return -1; | ||
2451 | } | ||
2452 | return 0; | ||
2453 | } | ||
2454 | |||
2455 | /* | ||
2456 | * Sends IOCTL request to set a generic IE. | ||
2457 | * | ||
2458 | * This function allocates the IOCTL request buffer, fills it | ||
2459 | * with requisite parameters and calls the IOCTL handler. | ||
2460 | */ | ||
2461 | int | ||
2462 | mwifiex_set_gen_ie(struct mwifiex_private *priv, u8 *ie, int ie_len) | ||
2463 | { | ||
2464 | struct mwifiex_ds_misc_gen_ie gen_ie; | ||
2465 | int status = 0; | ||
2466 | |||
2467 | if (ie_len > IW_CUSTOM_MAX) | ||
2468 | return -EFAULT; | ||
2469 | |||
2470 | gen_ie.type = MWIFIEX_IE_TYPE_GEN_IE; | ||
2471 | gen_ie.len = ie_len; | ||
2472 | memcpy(gen_ie.ie_data, ie, ie_len); | ||
2473 | status = mwifiex_misc_ioctl_gen_ie(priv, &gen_ie, HostCmd_ACT_GEN_SET); | ||
2474 | if (status) | ||
2475 | return -EFAULT; | ||
2476 | |||
2477 | return 0; | ||
2478 | } | ||