diff options
Diffstat (limited to 'drivers/net/wireless/libertas/assoc.c')
-rw-r--r-- | drivers/net/wireless/libertas/assoc.c | 2264 |
1 files changed, 0 insertions, 2264 deletions
diff --git a/drivers/net/wireless/libertas/assoc.c b/drivers/net/wireless/libertas/assoc.c deleted file mode 100644 index aa06070e5ea..00000000000 --- a/drivers/net/wireless/libertas/assoc.c +++ /dev/null | |||
@@ -1,2264 +0,0 @@ | |||
1 | /* Copyright (C) 2006, Red Hat, Inc. */ | ||
2 | |||
3 | #include <linux/types.h> | ||
4 | #include <linux/etherdevice.h> | ||
5 | #include <linux/ieee80211.h> | ||
6 | #include <linux/if_arp.h> | ||
7 | #include <linux/slab.h> | ||
8 | #include <net/lib80211.h> | ||
9 | |||
10 | #include "assoc.h" | ||
11 | #include "decl.h" | ||
12 | #include "host.h" | ||
13 | #include "scan.h" | ||
14 | #include "cmd.h" | ||
15 | |||
16 | static const u8 bssid_any[ETH_ALEN] __attribute__ ((aligned (2))) = | ||
17 | { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; | ||
18 | static const u8 bssid_off[ETH_ALEN] __attribute__ ((aligned (2))) = | ||
19 | { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; | ||
20 | |||
21 | /* The firmware needs the following bits masked out of the beacon-derived | ||
22 | * capability field when associating/joining to a BSS: | ||
23 | * 9 (QoS), 11 (APSD), 12 (unused), 14 (unused), 15 (unused) | ||
24 | */ | ||
25 | #define CAPINFO_MASK (~(0xda00)) | ||
26 | |||
27 | /** | ||
28 | * 802.11b/g supported bitrates (in 500Kb/s units) | ||
29 | */ | ||
30 | u8 lbs_bg_rates[MAX_RATES] = | ||
31 | { 0x02, 0x04, 0x0b, 0x16, 0x0c, 0x12, 0x18, 0x24, 0x30, 0x48, 0x60, 0x6c, | ||
32 | 0x00, 0x00 }; | ||
33 | |||
34 | |||
35 | static int assoc_helper_wep_keys(struct lbs_private *priv, | ||
36 | struct assoc_request *assoc_req); | ||
37 | |||
38 | /** | ||
39 | * @brief This function finds common rates between rates and card rates. | ||
40 | * | ||
41 | * It will fill common rates in rates as output if found. | ||
42 | * | ||
43 | * NOTE: Setting the MSB of the basic rates need to be taken | ||
44 | * care, either before or after calling this function | ||
45 | * | ||
46 | * @param priv A pointer to struct lbs_private structure | ||
47 | * @param rates the buffer which keeps input and output | ||
48 | * @param rates_size the size of rates buffer; new size of buffer on return, | ||
49 | * which will be less than or equal to original rates_size | ||
50 | * | ||
51 | * @return 0 on success, or -1 on error | ||
52 | */ | ||
53 | static int get_common_rates(struct lbs_private *priv, | ||
54 | u8 *rates, | ||
55 | u16 *rates_size) | ||
56 | { | ||
57 | int i, j; | ||
58 | u8 intersection[MAX_RATES]; | ||
59 | u16 intersection_size; | ||
60 | u16 num_rates = 0; | ||
61 | |||
62 | intersection_size = min_t(u16, *rates_size, ARRAY_SIZE(intersection)); | ||
63 | |||
64 | /* Allow each rate from 'rates' that is supported by the hardware */ | ||
65 | for (i = 0; i < ARRAY_SIZE(lbs_bg_rates) && lbs_bg_rates[i]; i++) { | ||
66 | for (j = 0; j < intersection_size && rates[j]; j++) { | ||
67 | if (rates[j] == lbs_bg_rates[i]) | ||
68 | intersection[num_rates++] = rates[j]; | ||
69 | } | ||
70 | } | ||
71 | |||
72 | lbs_deb_hex(LBS_DEB_JOIN, "AP rates ", rates, *rates_size); | ||
73 | lbs_deb_hex(LBS_DEB_JOIN, "card rates ", lbs_bg_rates, | ||
74 | ARRAY_SIZE(lbs_bg_rates)); | ||
75 | lbs_deb_hex(LBS_DEB_JOIN, "common rates", intersection, num_rates); | ||
76 | lbs_deb_join("TX data rate 0x%02x\n", priv->cur_rate); | ||
77 | |||
78 | if (!priv->enablehwauto) { | ||
79 | for (i = 0; i < num_rates; i++) { | ||
80 | if (intersection[i] == priv->cur_rate) | ||
81 | goto done; | ||
82 | } | ||
83 | lbs_pr_alert("Previously set fixed data rate %#x isn't " | ||
84 | "compatible with the network.\n", priv->cur_rate); | ||
85 | return -1; | ||
86 | } | ||
87 | |||
88 | done: | ||
89 | memset(rates, 0, *rates_size); | ||
90 | *rates_size = num_rates; | ||
91 | memcpy(rates, intersection, num_rates); | ||
92 | return 0; | ||
93 | } | ||
94 | |||
95 | |||
96 | /** | ||
97 | * @brief Sets the MSB on basic rates as the firmware requires | ||
98 | * | ||
99 | * Scan through an array and set the MSB for basic data rates. | ||
100 | * | ||
101 | * @param rates buffer of data rates | ||
102 | * @param len size of buffer | ||
103 | */ | ||
104 | static void lbs_set_basic_rate_flags(u8 *rates, size_t len) | ||
105 | { | ||
106 | int i; | ||
107 | |||
108 | for (i = 0; i < len; i++) { | ||
109 | if (rates[i] == 0x02 || rates[i] == 0x04 || | ||
110 | rates[i] == 0x0b || rates[i] == 0x16) | ||
111 | rates[i] |= 0x80; | ||
112 | } | ||
113 | } | ||
114 | |||
115 | |||
116 | static u8 iw_auth_to_ieee_auth(u8 auth) | ||
117 | { | ||
118 | if (auth == IW_AUTH_ALG_OPEN_SYSTEM) | ||
119 | return 0x00; | ||
120 | else if (auth == IW_AUTH_ALG_SHARED_KEY) | ||
121 | return 0x01; | ||
122 | else if (auth == IW_AUTH_ALG_LEAP) | ||
123 | return 0x80; | ||
124 | |||
125 | lbs_deb_join("%s: invalid auth alg 0x%X\n", __func__, auth); | ||
126 | return 0; | ||
127 | } | ||
128 | |||
129 | /** | ||
130 | * @brief This function prepares the authenticate command. AUTHENTICATE only | ||
131 | * sets the authentication suite for future associations, as the firmware | ||
132 | * handles authentication internally during the ASSOCIATE command. | ||
133 | * | ||
134 | * @param priv A pointer to struct lbs_private structure | ||
135 | * @param bssid The peer BSSID with which to authenticate | ||
136 | * @param auth The authentication mode to use (from wireless.h) | ||
137 | * | ||
138 | * @return 0 or -1 | ||
139 | */ | ||
140 | static int lbs_set_authentication(struct lbs_private *priv, u8 bssid[6], u8 auth) | ||
141 | { | ||
142 | struct cmd_ds_802_11_authenticate cmd; | ||
143 | int ret = -1; | ||
144 | |||
145 | lbs_deb_enter(LBS_DEB_JOIN); | ||
146 | |||
147 | cmd.hdr.size = cpu_to_le16(sizeof(cmd)); | ||
148 | memcpy(cmd.bssid, bssid, ETH_ALEN); | ||
149 | |||
150 | cmd.authtype = iw_auth_to_ieee_auth(auth); | ||
151 | |||
152 | lbs_deb_join("AUTH_CMD: BSSID %pM, auth 0x%x\n", bssid, cmd.authtype); | ||
153 | |||
154 | ret = lbs_cmd_with_response(priv, CMD_802_11_AUTHENTICATE, &cmd); | ||
155 | |||
156 | lbs_deb_leave_args(LBS_DEB_JOIN, "ret %d", ret); | ||
157 | return ret; | ||
158 | } | ||
159 | |||
160 | |||
161 | int lbs_cmd_802_11_set_wep(struct lbs_private *priv, uint16_t cmd_action, | ||
162 | struct assoc_request *assoc) | ||
163 | { | ||
164 | struct cmd_ds_802_11_set_wep cmd; | ||
165 | int ret = 0; | ||
166 | |||
167 | lbs_deb_enter(LBS_DEB_CMD); | ||
168 | |||
169 | memset(&cmd, 0, sizeof(cmd)); | ||
170 | cmd.hdr.command = cpu_to_le16(CMD_802_11_SET_WEP); | ||
171 | cmd.hdr.size = cpu_to_le16(sizeof(cmd)); | ||
172 | |||
173 | cmd.action = cpu_to_le16(cmd_action); | ||
174 | |||
175 | if (cmd_action == CMD_ACT_ADD) { | ||
176 | int i; | ||
177 | |||
178 | /* default tx key index */ | ||
179 | cmd.keyindex = cpu_to_le16(assoc->wep_tx_keyidx & | ||
180 | CMD_WEP_KEY_INDEX_MASK); | ||
181 | |||
182 | /* Copy key types and material to host command structure */ | ||
183 | for (i = 0; i < 4; i++) { | ||
184 | struct enc_key *pkey = &assoc->wep_keys[i]; | ||
185 | |||
186 | switch (pkey->len) { | ||
187 | case KEY_LEN_WEP_40: | ||
188 | cmd.keytype[i] = CMD_TYPE_WEP_40_BIT; | ||
189 | memmove(cmd.keymaterial[i], pkey->key, pkey->len); | ||
190 | lbs_deb_cmd("SET_WEP: add key %d (40 bit)\n", i); | ||
191 | break; | ||
192 | case KEY_LEN_WEP_104: | ||
193 | cmd.keytype[i] = CMD_TYPE_WEP_104_BIT; | ||
194 | memmove(cmd.keymaterial[i], pkey->key, pkey->len); | ||
195 | lbs_deb_cmd("SET_WEP: add key %d (104 bit)\n", i); | ||
196 | break; | ||
197 | case 0: | ||
198 | break; | ||
199 | default: | ||
200 | lbs_deb_cmd("SET_WEP: invalid key %d, length %d\n", | ||
201 | i, pkey->len); | ||
202 | ret = -1; | ||
203 | goto done; | ||
204 | break; | ||
205 | } | ||
206 | } | ||
207 | } else if (cmd_action == CMD_ACT_REMOVE) { | ||
208 | /* ACT_REMOVE clears _all_ WEP keys */ | ||
209 | |||
210 | /* default tx key index */ | ||
211 | cmd.keyindex = cpu_to_le16(priv->wep_tx_keyidx & | ||
212 | CMD_WEP_KEY_INDEX_MASK); | ||
213 | lbs_deb_cmd("SET_WEP: remove key %d\n", priv->wep_tx_keyidx); | ||
214 | } | ||
215 | |||
216 | ret = lbs_cmd_with_response(priv, CMD_802_11_SET_WEP, &cmd); | ||
217 | done: | ||
218 | lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret); | ||
219 | return ret; | ||
220 | } | ||
221 | |||
222 | int lbs_cmd_802_11_enable_rsn(struct lbs_private *priv, uint16_t cmd_action, | ||
223 | uint16_t *enable) | ||
224 | { | ||
225 | struct cmd_ds_802_11_enable_rsn cmd; | ||
226 | int ret; | ||
227 | |||
228 | lbs_deb_enter(LBS_DEB_CMD); | ||
229 | |||
230 | cmd.hdr.size = cpu_to_le16(sizeof(cmd)); | ||
231 | cmd.action = cpu_to_le16(cmd_action); | ||
232 | |||
233 | if (cmd_action == CMD_ACT_GET) | ||
234 | cmd.enable = 0; | ||
235 | else { | ||
236 | if (*enable) | ||
237 | cmd.enable = cpu_to_le16(CMD_ENABLE_RSN); | ||
238 | else | ||
239 | cmd.enable = cpu_to_le16(CMD_DISABLE_RSN); | ||
240 | lbs_deb_cmd("ENABLE_RSN: %d\n", *enable); | ||
241 | } | ||
242 | |||
243 | ret = lbs_cmd_with_response(priv, CMD_802_11_ENABLE_RSN, &cmd); | ||
244 | if (!ret && cmd_action == CMD_ACT_GET) | ||
245 | *enable = le16_to_cpu(cmd.enable); | ||
246 | |||
247 | lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret); | ||
248 | return ret; | ||
249 | } | ||
250 | |||
251 | static void set_one_wpa_key(struct MrvlIEtype_keyParamSet *keyparam, | ||
252 | struct enc_key *key) | ||
253 | { | ||
254 | lbs_deb_enter(LBS_DEB_CMD); | ||
255 | |||
256 | if (key->flags & KEY_INFO_WPA_ENABLED) | ||
257 | keyparam->keyinfo |= cpu_to_le16(KEY_INFO_WPA_ENABLED); | ||
258 | if (key->flags & KEY_INFO_WPA_UNICAST) | ||
259 | keyparam->keyinfo |= cpu_to_le16(KEY_INFO_WPA_UNICAST); | ||
260 | if (key->flags & KEY_INFO_WPA_MCAST) | ||
261 | keyparam->keyinfo |= cpu_to_le16(KEY_INFO_WPA_MCAST); | ||
262 | |||
263 | keyparam->type = cpu_to_le16(TLV_TYPE_KEY_MATERIAL); | ||
264 | keyparam->keytypeid = cpu_to_le16(key->type); | ||
265 | keyparam->keylen = cpu_to_le16(key->len); | ||
266 | memcpy(keyparam->key, key->key, key->len); | ||
267 | |||
268 | /* Length field doesn't include the {type,length} header */ | ||
269 | keyparam->length = cpu_to_le16(sizeof(*keyparam) - 4); | ||
270 | lbs_deb_leave(LBS_DEB_CMD); | ||
271 | } | ||
272 | |||
273 | int lbs_cmd_802_11_key_material(struct lbs_private *priv, uint16_t cmd_action, | ||
274 | struct assoc_request *assoc) | ||
275 | { | ||
276 | struct cmd_ds_802_11_key_material cmd; | ||
277 | int ret = 0; | ||
278 | int index = 0; | ||
279 | |||
280 | lbs_deb_enter(LBS_DEB_CMD); | ||
281 | |||
282 | cmd.action = cpu_to_le16(cmd_action); | ||
283 | cmd.hdr.size = cpu_to_le16(sizeof(cmd)); | ||
284 | |||
285 | if (cmd_action == CMD_ACT_GET) { | ||
286 | cmd.hdr.size = cpu_to_le16(sizeof(struct cmd_header) + 2); | ||
287 | } else { | ||
288 | memset(cmd.keyParamSet, 0, sizeof(cmd.keyParamSet)); | ||
289 | |||
290 | if (test_bit(ASSOC_FLAG_WPA_UCAST_KEY, &assoc->flags)) { | ||
291 | set_one_wpa_key(&cmd.keyParamSet[index], | ||
292 | &assoc->wpa_unicast_key); | ||
293 | index++; | ||
294 | } | ||
295 | |||
296 | if (test_bit(ASSOC_FLAG_WPA_MCAST_KEY, &assoc->flags)) { | ||
297 | set_one_wpa_key(&cmd.keyParamSet[index], | ||
298 | &assoc->wpa_mcast_key); | ||
299 | index++; | ||
300 | } | ||
301 | |||
302 | /* The common header and as many keys as we included */ | ||
303 | cmd.hdr.size = cpu_to_le16(offsetof(typeof(cmd), | ||
304 | keyParamSet[index])); | ||
305 | } | ||
306 | ret = lbs_cmd_with_response(priv, CMD_802_11_KEY_MATERIAL, &cmd); | ||
307 | /* Copy the returned key to driver private data */ | ||
308 | if (!ret && cmd_action == CMD_ACT_GET) { | ||
309 | void *buf_ptr = cmd.keyParamSet; | ||
310 | void *resp_end = &(&cmd)[1]; | ||
311 | |||
312 | while (buf_ptr < resp_end) { | ||
313 | struct MrvlIEtype_keyParamSet *keyparam = buf_ptr; | ||
314 | struct enc_key *key; | ||
315 | uint16_t param_set_len = le16_to_cpu(keyparam->length); | ||
316 | uint16_t key_len = le16_to_cpu(keyparam->keylen); | ||
317 | uint16_t key_flags = le16_to_cpu(keyparam->keyinfo); | ||
318 | uint16_t key_type = le16_to_cpu(keyparam->keytypeid); | ||
319 | void *end; | ||
320 | |||
321 | end = (void *)keyparam + sizeof(keyparam->type) | ||
322 | + sizeof(keyparam->length) + param_set_len; | ||
323 | |||
324 | /* Make sure we don't access past the end of the IEs */ | ||
325 | if (end > resp_end) | ||
326 | break; | ||
327 | |||
328 | if (key_flags & KEY_INFO_WPA_UNICAST) | ||
329 | key = &priv->wpa_unicast_key; | ||
330 | else if (key_flags & KEY_INFO_WPA_MCAST) | ||
331 | key = &priv->wpa_mcast_key; | ||
332 | else | ||
333 | break; | ||
334 | |||
335 | /* Copy returned key into driver */ | ||
336 | memset(key, 0, sizeof(struct enc_key)); | ||
337 | if (key_len > sizeof(key->key)) | ||
338 | break; | ||
339 | key->type = key_type; | ||
340 | key->flags = key_flags; | ||
341 | key->len = key_len; | ||
342 | memcpy(key->key, keyparam->key, key->len); | ||
343 | |||
344 | buf_ptr = end + 1; | ||
345 | } | ||
346 | } | ||
347 | |||
348 | lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret); | ||
349 | return ret; | ||
350 | } | ||
351 | |||
352 | static __le16 lbs_rate_to_fw_bitmap(int rate, int lower_rates_ok) | ||
353 | { | ||
354 | /* Bit Rate | ||
355 | * 15:13 Reserved | ||
356 | * 12 54 Mbps | ||
357 | * 11 48 Mbps | ||
358 | * 10 36 Mbps | ||
359 | * 9 24 Mbps | ||
360 | * 8 18 Mbps | ||
361 | * 7 12 Mbps | ||
362 | * 6 9 Mbps | ||
363 | * 5 6 Mbps | ||
364 | * 4 Reserved | ||
365 | * 3 11 Mbps | ||
366 | * 2 5.5 Mbps | ||
367 | * 1 2 Mbps | ||
368 | * 0 1 Mbps | ||
369 | **/ | ||
370 | |||
371 | uint16_t ratemask; | ||
372 | int i = lbs_data_rate_to_fw_index(rate); | ||
373 | if (lower_rates_ok) | ||
374 | ratemask = (0x1fef >> (12 - i)); | ||
375 | else | ||
376 | ratemask = (1 << i); | ||
377 | return cpu_to_le16(ratemask); | ||
378 | } | ||
379 | |||
380 | int lbs_cmd_802_11_rate_adapt_rateset(struct lbs_private *priv, | ||
381 | uint16_t cmd_action) | ||
382 | { | ||
383 | struct cmd_ds_802_11_rate_adapt_rateset cmd; | ||
384 | int ret; | ||
385 | |||
386 | lbs_deb_enter(LBS_DEB_CMD); | ||
387 | |||
388 | if (!priv->cur_rate && !priv->enablehwauto) | ||
389 | return -EINVAL; | ||
390 | |||
391 | cmd.hdr.size = cpu_to_le16(sizeof(cmd)); | ||
392 | |||
393 | cmd.action = cpu_to_le16(cmd_action); | ||
394 | cmd.enablehwauto = cpu_to_le16(priv->enablehwauto); | ||
395 | cmd.bitmap = lbs_rate_to_fw_bitmap(priv->cur_rate, priv->enablehwauto); | ||
396 | ret = lbs_cmd_with_response(priv, CMD_802_11_RATE_ADAPT_RATESET, &cmd); | ||
397 | if (!ret && cmd_action == CMD_ACT_GET) | ||
398 | priv->enablehwauto = le16_to_cpu(cmd.enablehwauto); | ||
399 | |||
400 | lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret); | ||
401 | return ret; | ||
402 | } | ||
403 | |||
404 | /** | ||
405 | * @brief Set the data rate | ||
406 | * | ||
407 | * @param priv A pointer to struct lbs_private structure | ||
408 | * @param rate The desired data rate, or 0 to clear a locked rate | ||
409 | * | ||
410 | * @return 0 on success, error on failure | ||
411 | */ | ||
412 | int lbs_set_data_rate(struct lbs_private *priv, u8 rate) | ||
413 | { | ||
414 | struct cmd_ds_802_11_data_rate cmd; | ||
415 | int ret = 0; | ||
416 | |||
417 | lbs_deb_enter(LBS_DEB_CMD); | ||
418 | |||
419 | memset(&cmd, 0, sizeof(cmd)); | ||
420 | cmd.hdr.size = cpu_to_le16(sizeof(cmd)); | ||
421 | |||
422 | if (rate > 0) { | ||
423 | cmd.action = cpu_to_le16(CMD_ACT_SET_TX_FIX_RATE); | ||
424 | cmd.rates[0] = lbs_data_rate_to_fw_index(rate); | ||
425 | if (cmd.rates[0] == 0) { | ||
426 | lbs_deb_cmd("DATA_RATE: invalid requested rate of" | ||
427 | " 0x%02X\n", rate); | ||
428 | ret = 0; | ||
429 | goto out; | ||
430 | } | ||
431 | lbs_deb_cmd("DATA_RATE: set fixed 0x%02X\n", cmd.rates[0]); | ||
432 | } else { | ||
433 | cmd.action = cpu_to_le16(CMD_ACT_SET_TX_AUTO); | ||
434 | lbs_deb_cmd("DATA_RATE: setting auto\n"); | ||
435 | } | ||
436 | |||
437 | ret = lbs_cmd_with_response(priv, CMD_802_11_DATA_RATE, &cmd); | ||
438 | if (ret) | ||
439 | goto out; | ||
440 | |||
441 | lbs_deb_hex(LBS_DEB_CMD, "DATA_RATE_RESP", (u8 *) &cmd, sizeof(cmd)); | ||
442 | |||
443 | /* FIXME: get actual rates FW can do if this command actually returns | ||
444 | * all data rates supported. | ||
445 | */ | ||
446 | priv->cur_rate = lbs_fw_index_to_data_rate(cmd.rates[0]); | ||
447 | lbs_deb_cmd("DATA_RATE: current rate is 0x%02x\n", priv->cur_rate); | ||
448 | |||
449 | out: | ||
450 | lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret); | ||
451 | return ret; | ||
452 | } | ||
453 | |||
454 | |||
455 | int lbs_cmd_802_11_rssi(struct lbs_private *priv, | ||
456 | struct cmd_ds_command *cmd) | ||
457 | { | ||
458 | |||
459 | lbs_deb_enter(LBS_DEB_CMD); | ||
460 | cmd->command = cpu_to_le16(CMD_802_11_RSSI); | ||
461 | cmd->size = cpu_to_le16(sizeof(struct cmd_ds_802_11_rssi) + | ||
462 | sizeof(struct cmd_header)); | ||
463 | cmd->params.rssi.N = cpu_to_le16(DEFAULT_BCN_AVG_FACTOR); | ||
464 | |||
465 | /* reset Beacon SNR/NF/RSSI values */ | ||
466 | priv->SNR[TYPE_BEACON][TYPE_NOAVG] = 0; | ||
467 | priv->SNR[TYPE_BEACON][TYPE_AVG] = 0; | ||
468 | priv->NF[TYPE_BEACON][TYPE_NOAVG] = 0; | ||
469 | priv->NF[TYPE_BEACON][TYPE_AVG] = 0; | ||
470 | priv->RSSI[TYPE_BEACON][TYPE_NOAVG] = 0; | ||
471 | priv->RSSI[TYPE_BEACON][TYPE_AVG] = 0; | ||
472 | |||
473 | lbs_deb_leave(LBS_DEB_CMD); | ||
474 | return 0; | ||
475 | } | ||
476 | |||
477 | int lbs_ret_802_11_rssi(struct lbs_private *priv, | ||
478 | struct cmd_ds_command *resp) | ||
479 | { | ||
480 | struct cmd_ds_802_11_rssi_rsp *rssirsp = &resp->params.rssirsp; | ||
481 | |||
482 | lbs_deb_enter(LBS_DEB_CMD); | ||
483 | |||
484 | /* store the non average value */ | ||
485 | priv->SNR[TYPE_BEACON][TYPE_NOAVG] = get_unaligned_le16(&rssirsp->SNR); | ||
486 | priv->NF[TYPE_BEACON][TYPE_NOAVG] = | ||
487 | get_unaligned_le16(&rssirsp->noisefloor); | ||
488 | |||
489 | priv->SNR[TYPE_BEACON][TYPE_AVG] = get_unaligned_le16(&rssirsp->avgSNR); | ||
490 | priv->NF[TYPE_BEACON][TYPE_AVG] = | ||
491 | get_unaligned_le16(&rssirsp->avgnoisefloor); | ||
492 | |||
493 | priv->RSSI[TYPE_BEACON][TYPE_NOAVG] = | ||
494 | CAL_RSSI(priv->SNR[TYPE_BEACON][TYPE_NOAVG], | ||
495 | priv->NF[TYPE_BEACON][TYPE_NOAVG]); | ||
496 | |||
497 | priv->RSSI[TYPE_BEACON][TYPE_AVG] = | ||
498 | CAL_RSSI(priv->SNR[TYPE_BEACON][TYPE_AVG] / AVG_SCALE, | ||
499 | priv->NF[TYPE_BEACON][TYPE_AVG] / AVG_SCALE); | ||
500 | |||
501 | lbs_deb_cmd("RSSI: beacon %d, avg %d\n", | ||
502 | priv->RSSI[TYPE_BEACON][TYPE_NOAVG], | ||
503 | priv->RSSI[TYPE_BEACON][TYPE_AVG]); | ||
504 | |||
505 | lbs_deb_leave(LBS_DEB_CMD); | ||
506 | return 0; | ||
507 | } | ||
508 | |||
509 | |||
510 | int lbs_cmd_bcn_ctrl(struct lbs_private *priv, | ||
511 | struct cmd_ds_command *cmd, | ||
512 | u16 cmd_action) | ||
513 | { | ||
514 | struct cmd_ds_802_11_beacon_control | ||
515 | *bcn_ctrl = &cmd->params.bcn_ctrl; | ||
516 | |||
517 | lbs_deb_enter(LBS_DEB_CMD); | ||
518 | cmd->size = | ||
519 | cpu_to_le16(sizeof(struct cmd_ds_802_11_beacon_control) | ||
520 | + sizeof(struct cmd_header)); | ||
521 | cmd->command = cpu_to_le16(CMD_802_11_BEACON_CTRL); | ||
522 | |||
523 | bcn_ctrl->action = cpu_to_le16(cmd_action); | ||
524 | bcn_ctrl->beacon_enable = cpu_to_le16(priv->beacon_enable); | ||
525 | bcn_ctrl->beacon_period = cpu_to_le16(priv->beacon_period); | ||
526 | |||
527 | lbs_deb_leave(LBS_DEB_CMD); | ||
528 | return 0; | ||
529 | } | ||
530 | |||
531 | int lbs_ret_802_11_bcn_ctrl(struct lbs_private *priv, | ||
532 | struct cmd_ds_command *resp) | ||
533 | { | ||
534 | struct cmd_ds_802_11_beacon_control *bcn_ctrl = | ||
535 | &resp->params.bcn_ctrl; | ||
536 | |||
537 | lbs_deb_enter(LBS_DEB_CMD); | ||
538 | |||
539 | if (bcn_ctrl->action == CMD_ACT_GET) { | ||
540 | priv->beacon_enable = (u8) le16_to_cpu(bcn_ctrl->beacon_enable); | ||
541 | priv->beacon_period = le16_to_cpu(bcn_ctrl->beacon_period); | ||
542 | } | ||
543 | |||
544 | lbs_deb_enter(LBS_DEB_CMD); | ||
545 | return 0; | ||
546 | } | ||
547 | |||
548 | |||
549 | |||
550 | static int lbs_assoc_post(struct lbs_private *priv, | ||
551 | struct cmd_ds_802_11_associate_response *resp) | ||
552 | { | ||
553 | int ret = 0; | ||
554 | union iwreq_data wrqu; | ||
555 | struct bss_descriptor *bss; | ||
556 | u16 status_code; | ||
557 | |||
558 | lbs_deb_enter(LBS_DEB_ASSOC); | ||
559 | |||
560 | if (!priv->in_progress_assoc_req) { | ||
561 | lbs_deb_assoc("ASSOC_RESP: no in-progress assoc request\n"); | ||
562 | ret = -1; | ||
563 | goto done; | ||
564 | } | ||
565 | bss = &priv->in_progress_assoc_req->bss; | ||
566 | |||
567 | /* | ||
568 | * Older FW versions map the IEEE 802.11 Status Code in the association | ||
569 | * response to the following values returned in resp->statuscode: | ||
570 | * | ||
571 | * IEEE Status Code Marvell Status Code | ||
572 | * 0 -> 0x0000 ASSOC_RESULT_SUCCESS | ||
573 | * 13 -> 0x0004 ASSOC_RESULT_AUTH_REFUSED | ||
574 | * 14 -> 0x0004 ASSOC_RESULT_AUTH_REFUSED | ||
575 | * 15 -> 0x0004 ASSOC_RESULT_AUTH_REFUSED | ||
576 | * 16 -> 0x0004 ASSOC_RESULT_AUTH_REFUSED | ||
577 | * others -> 0x0003 ASSOC_RESULT_REFUSED | ||
578 | * | ||
579 | * Other response codes: | ||
580 | * 0x0001 -> ASSOC_RESULT_INVALID_PARAMETERS (unused) | ||
581 | * 0x0002 -> ASSOC_RESULT_TIMEOUT (internal timer expired waiting for | ||
582 | * association response from the AP) | ||
583 | */ | ||
584 | |||
585 | status_code = le16_to_cpu(resp->statuscode); | ||
586 | if (priv->fwrelease < 0x09000000) { | ||
587 | switch (status_code) { | ||
588 | case 0x00: | ||
589 | break; | ||
590 | case 0x01: | ||
591 | lbs_deb_assoc("ASSOC_RESP: invalid parameters\n"); | ||
592 | break; | ||
593 | case 0x02: | ||
594 | lbs_deb_assoc("ASSOC_RESP: internal timer " | ||
595 | "expired while waiting for the AP\n"); | ||
596 | break; | ||
597 | case 0x03: | ||
598 | lbs_deb_assoc("ASSOC_RESP: association " | ||
599 | "refused by AP\n"); | ||
600 | break; | ||
601 | case 0x04: | ||
602 | lbs_deb_assoc("ASSOC_RESP: authentication " | ||
603 | "refused by AP\n"); | ||
604 | break; | ||
605 | default: | ||
606 | lbs_deb_assoc("ASSOC_RESP: failure reason 0x%02x " | ||
607 | " unknown\n", status_code); | ||
608 | break; | ||
609 | } | ||
610 | } else { | ||
611 | /* v9+ returns the AP's association response */ | ||
612 | lbs_deb_assoc("ASSOC_RESP: failure reason 0x%02x\n", status_code); | ||
613 | } | ||
614 | |||
615 | if (status_code) { | ||
616 | lbs_mac_event_disconnected(priv); | ||
617 | ret = status_code; | ||
618 | goto done; | ||
619 | } | ||
620 | |||
621 | lbs_deb_hex(LBS_DEB_ASSOC, "ASSOC_RESP", | ||
622 | (void *) (resp + sizeof (resp->hdr)), | ||
623 | le16_to_cpu(resp->hdr.size) - sizeof (resp->hdr)); | ||
624 | |||
625 | /* Send a Media Connected event, according to the Spec */ | ||
626 | priv->connect_status = LBS_CONNECTED; | ||
627 | |||
628 | /* Update current SSID and BSSID */ | ||
629 | memcpy(&priv->curbssparams.ssid, &bss->ssid, IEEE80211_MAX_SSID_LEN); | ||
630 | priv->curbssparams.ssid_len = bss->ssid_len; | ||
631 | memcpy(priv->curbssparams.bssid, bss->bssid, ETH_ALEN); | ||
632 | |||
633 | priv->SNR[TYPE_RXPD][TYPE_AVG] = 0; | ||
634 | priv->NF[TYPE_RXPD][TYPE_AVG] = 0; | ||
635 | |||
636 | memset(priv->rawSNR, 0x00, sizeof(priv->rawSNR)); | ||
637 | memset(priv->rawNF, 0x00, sizeof(priv->rawNF)); | ||
638 | priv->nextSNRNF = 0; | ||
639 | priv->numSNRNF = 0; | ||
640 | |||
641 | netif_carrier_on(priv->dev); | ||
642 | if (!priv->tx_pending_len) | ||
643 | netif_wake_queue(priv->dev); | ||
644 | |||
645 | memcpy(wrqu.ap_addr.sa_data, priv->curbssparams.bssid, ETH_ALEN); | ||
646 | wrqu.ap_addr.sa_family = ARPHRD_ETHER; | ||
647 | wireless_send_event(priv->dev, SIOCGIWAP, &wrqu, NULL); | ||
648 | |||
649 | done: | ||
650 | lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret); | ||
651 | return ret; | ||
652 | } | ||
653 | |||
654 | /** | ||
655 | * @brief This function prepares an association-class command. | ||
656 | * | ||
657 | * @param priv A pointer to struct lbs_private structure | ||
658 | * @param assoc_req The association request describing the BSS to associate | ||
659 | * or reassociate with | ||
660 | * @param command The actual command, either CMD_802_11_ASSOCIATE or | ||
661 | * CMD_802_11_REASSOCIATE | ||
662 | * | ||
663 | * @return 0 or -1 | ||
664 | */ | ||
665 | static int lbs_associate(struct lbs_private *priv, | ||
666 | struct assoc_request *assoc_req, | ||
667 | u16 command) | ||
668 | { | ||
669 | struct cmd_ds_802_11_associate cmd; | ||
670 | int ret = 0; | ||
671 | struct bss_descriptor *bss = &assoc_req->bss; | ||
672 | u8 *pos = &(cmd.iebuf[0]); | ||
673 | u16 tmpcap, tmplen, tmpauth; | ||
674 | struct mrvl_ie_ssid_param_set *ssid; | ||
675 | struct mrvl_ie_ds_param_set *ds; | ||
676 | struct mrvl_ie_cf_param_set *cf; | ||
677 | struct mrvl_ie_rates_param_set *rates; | ||
678 | struct mrvl_ie_rsn_param_set *rsn; | ||
679 | struct mrvl_ie_auth_type *auth; | ||
680 | |||
681 | lbs_deb_enter(LBS_DEB_ASSOC); | ||
682 | |||
683 | BUG_ON((command != CMD_802_11_ASSOCIATE) && | ||
684 | (command != CMD_802_11_REASSOCIATE)); | ||
685 | |||
686 | memset(&cmd, 0, sizeof(cmd)); | ||
687 | cmd.hdr.command = cpu_to_le16(command); | ||
688 | |||
689 | /* Fill in static fields */ | ||
690 | memcpy(cmd.bssid, bss->bssid, ETH_ALEN); | ||
691 | cmd.listeninterval = cpu_to_le16(MRVDRV_DEFAULT_LISTEN_INTERVAL); | ||
692 | |||
693 | /* Capability info */ | ||
694 | tmpcap = (bss->capability & CAPINFO_MASK); | ||
695 | if (bss->mode == IW_MODE_INFRA) | ||
696 | tmpcap |= WLAN_CAPABILITY_ESS; | ||
697 | cmd.capability = cpu_to_le16(tmpcap); | ||
698 | lbs_deb_assoc("ASSOC_CMD: capability 0x%04x\n", tmpcap); | ||
699 | |||
700 | /* SSID */ | ||
701 | ssid = (struct mrvl_ie_ssid_param_set *) pos; | ||
702 | ssid->header.type = cpu_to_le16(TLV_TYPE_SSID); | ||
703 | tmplen = bss->ssid_len; | ||
704 | ssid->header.len = cpu_to_le16(tmplen); | ||
705 | memcpy(ssid->ssid, bss->ssid, tmplen); | ||
706 | pos += sizeof(ssid->header) + tmplen; | ||
707 | |||
708 | ds = (struct mrvl_ie_ds_param_set *) pos; | ||
709 | ds->header.type = cpu_to_le16(TLV_TYPE_PHY_DS); | ||
710 | ds->header.len = cpu_to_le16(1); | ||
711 | ds->channel = bss->phy.ds.channel; | ||
712 | pos += sizeof(ds->header) + 1; | ||
713 | |||
714 | cf = (struct mrvl_ie_cf_param_set *) pos; | ||
715 | cf->header.type = cpu_to_le16(TLV_TYPE_CF); | ||
716 | tmplen = sizeof(*cf) - sizeof (cf->header); | ||
717 | cf->header.len = cpu_to_le16(tmplen); | ||
718 | /* IE payload should be zeroed, firmware fills it in for us */ | ||
719 | pos += sizeof(*cf); | ||
720 | |||
721 | rates = (struct mrvl_ie_rates_param_set *) pos; | ||
722 | rates->header.type = cpu_to_le16(TLV_TYPE_RATES); | ||
723 | tmplen = min_t(u16, ARRAY_SIZE(bss->rates), MAX_RATES); | ||
724 | memcpy(&rates->rates, &bss->rates, tmplen); | ||
725 | if (get_common_rates(priv, rates->rates, &tmplen)) { | ||
726 | ret = -1; | ||
727 | goto done; | ||
728 | } | ||
729 | pos += sizeof(rates->header) + tmplen; | ||
730 | rates->header.len = cpu_to_le16(tmplen); | ||
731 | lbs_deb_assoc("ASSOC_CMD: num rates %u\n", tmplen); | ||
732 | |||
733 | /* Copy the infra. association rates into Current BSS state structure */ | ||
734 | memset(&priv->curbssparams.rates, 0, sizeof(priv->curbssparams.rates)); | ||
735 | memcpy(&priv->curbssparams.rates, &rates->rates, tmplen); | ||
736 | |||
737 | /* Set MSB on basic rates as the firmware requires, but _after_ | ||
738 | * copying to current bss rates. | ||
739 | */ | ||
740 | lbs_set_basic_rate_flags(rates->rates, tmplen); | ||
741 | |||
742 | /* Firmware v9+ indicate authentication suites as a TLV */ | ||
743 | if (priv->fwrelease >= 0x09000000) { | ||
744 | auth = (struct mrvl_ie_auth_type *) pos; | ||
745 | auth->header.type = cpu_to_le16(TLV_TYPE_AUTH_TYPE); | ||
746 | auth->header.len = cpu_to_le16(2); | ||
747 | tmpauth = iw_auth_to_ieee_auth(priv->secinfo.auth_mode); | ||
748 | auth->auth = cpu_to_le16(tmpauth); | ||
749 | pos += sizeof(auth->header) + 2; | ||
750 | |||
751 | lbs_deb_join("AUTH_CMD: BSSID %pM, auth 0x%x\n", | ||
752 | bss->bssid, priv->secinfo.auth_mode); | ||
753 | } | ||
754 | |||
755 | /* WPA/WPA2 IEs */ | ||
756 | if (assoc_req->secinfo.WPAenabled || assoc_req->secinfo.WPA2enabled) { | ||
757 | rsn = (struct mrvl_ie_rsn_param_set *) pos; | ||
758 | /* WPA_IE or WPA2_IE */ | ||
759 | rsn->header.type = cpu_to_le16((u16) assoc_req->wpa_ie[0]); | ||
760 | tmplen = (u16) assoc_req->wpa_ie[1]; | ||
761 | rsn->header.len = cpu_to_le16(tmplen); | ||
762 | memcpy(rsn->rsnie, &assoc_req->wpa_ie[2], tmplen); | ||
763 | lbs_deb_hex(LBS_DEB_JOIN, "ASSOC_CMD: WPA/RSN IE", (u8 *) rsn, | ||
764 | sizeof(rsn->header) + tmplen); | ||
765 | pos += sizeof(rsn->header) + tmplen; | ||
766 | } | ||
767 | |||
768 | cmd.hdr.size = cpu_to_le16((sizeof(cmd) - sizeof(cmd.iebuf)) + | ||
769 | (u16)(pos - (u8 *) &cmd.iebuf)); | ||
770 | |||
771 | /* update curbssparams */ | ||
772 | priv->channel = bss->phy.ds.channel; | ||
773 | |||
774 | ret = lbs_cmd_with_response(priv, command, &cmd); | ||
775 | if (ret == 0) { | ||
776 | ret = lbs_assoc_post(priv, | ||
777 | (struct cmd_ds_802_11_associate_response *) &cmd); | ||
778 | } | ||
779 | |||
780 | done: | ||
781 | lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret); | ||
782 | return ret; | ||
783 | } | ||
784 | |||
785 | /** | ||
786 | * @brief Associate to a specific BSS discovered in a scan | ||
787 | * | ||
788 | * @param priv A pointer to struct lbs_private structure | ||
789 | * @param assoc_req The association request describing the BSS to associate with | ||
790 | * | ||
791 | * @return 0-success, otherwise fail | ||
792 | */ | ||
793 | static int lbs_try_associate(struct lbs_private *priv, | ||
794 | struct assoc_request *assoc_req) | ||
795 | { | ||
796 | int ret; | ||
797 | u8 preamble = RADIO_PREAMBLE_LONG; | ||
798 | |||
799 | lbs_deb_enter(LBS_DEB_ASSOC); | ||
800 | |||
801 | /* FW v9 and higher indicate authentication suites as a TLV in the | ||
802 | * association command, not as a separate authentication command. | ||
803 | */ | ||
804 | if (priv->fwrelease < 0x09000000) { | ||
805 | ret = lbs_set_authentication(priv, assoc_req->bss.bssid, | ||
806 | priv->secinfo.auth_mode); | ||
807 | if (ret) | ||
808 | goto out; | ||
809 | } | ||
810 | |||
811 | /* Use short preamble only when both the BSS and firmware support it */ | ||
812 | if (assoc_req->bss.capability & WLAN_CAPABILITY_SHORT_PREAMBLE) | ||
813 | preamble = RADIO_PREAMBLE_SHORT; | ||
814 | |||
815 | ret = lbs_set_radio(priv, preamble, 1); | ||
816 | if (ret) | ||
817 | goto out; | ||
818 | |||
819 | ret = lbs_associate(priv, assoc_req, CMD_802_11_ASSOCIATE); | ||
820 | /* If the association fails with current auth mode, let's | ||
821 | * try by changing the auth mode | ||
822 | */ | ||
823 | if ((priv->authtype_auto) && | ||
824 | (ret == WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG) && | ||
825 | (assoc_req->secinfo.wep_enabled) && | ||
826 | (priv->connect_status != LBS_CONNECTED)) { | ||
827 | if (priv->secinfo.auth_mode == IW_AUTH_ALG_OPEN_SYSTEM) | ||
828 | priv->secinfo.auth_mode = IW_AUTH_ALG_SHARED_KEY; | ||
829 | else | ||
830 | priv->secinfo.auth_mode = IW_AUTH_ALG_OPEN_SYSTEM; | ||
831 | if (!assoc_helper_wep_keys(priv, assoc_req)) | ||
832 | ret = lbs_associate(priv, assoc_req, | ||
833 | CMD_802_11_ASSOCIATE); | ||
834 | } | ||
835 | |||
836 | if (ret) | ||
837 | ret = -1; | ||
838 | out: | ||
839 | lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret); | ||
840 | return ret; | ||
841 | } | ||
842 | |||
843 | static int lbs_adhoc_post(struct lbs_private *priv, | ||
844 | struct cmd_ds_802_11_ad_hoc_result *resp) | ||
845 | { | ||
846 | int ret = 0; | ||
847 | u16 command = le16_to_cpu(resp->hdr.command); | ||
848 | u16 result = le16_to_cpu(resp->hdr.result); | ||
849 | union iwreq_data wrqu; | ||
850 | struct bss_descriptor *bss; | ||
851 | DECLARE_SSID_BUF(ssid); | ||
852 | |||
853 | lbs_deb_enter(LBS_DEB_JOIN); | ||
854 | |||
855 | if (!priv->in_progress_assoc_req) { | ||
856 | lbs_deb_join("ADHOC_RESP: no in-progress association " | ||
857 | "request\n"); | ||
858 | ret = -1; | ||
859 | goto done; | ||
860 | } | ||
861 | bss = &priv->in_progress_assoc_req->bss; | ||
862 | |||
863 | /* | ||
864 | * Join result code 0 --> SUCCESS | ||
865 | */ | ||
866 | if (result) { | ||
867 | lbs_deb_join("ADHOC_RESP: failed (result 0x%X)\n", result); | ||
868 | if (priv->connect_status == LBS_CONNECTED) | ||
869 | lbs_mac_event_disconnected(priv); | ||
870 | ret = -1; | ||
871 | goto done; | ||
872 | } | ||
873 | |||
874 | /* Send a Media Connected event, according to the Spec */ | ||
875 | priv->connect_status = LBS_CONNECTED; | ||
876 | |||
877 | if (command == CMD_RET(CMD_802_11_AD_HOC_START)) { | ||
878 | /* Update the created network descriptor with the new BSSID */ | ||
879 | memcpy(bss->bssid, resp->bssid, ETH_ALEN); | ||
880 | } | ||
881 | |||
882 | /* Set the BSSID from the joined/started descriptor */ | ||
883 | memcpy(&priv->curbssparams.bssid, bss->bssid, ETH_ALEN); | ||
884 | |||
885 | /* Set the new SSID to current SSID */ | ||
886 | memcpy(&priv->curbssparams.ssid, &bss->ssid, IEEE80211_MAX_SSID_LEN); | ||
887 | priv->curbssparams.ssid_len = bss->ssid_len; | ||
888 | |||
889 | netif_carrier_on(priv->dev); | ||
890 | if (!priv->tx_pending_len) | ||
891 | netif_wake_queue(priv->dev); | ||
892 | |||
893 | memset(&wrqu, 0, sizeof(wrqu)); | ||
894 | memcpy(wrqu.ap_addr.sa_data, priv->curbssparams.bssid, ETH_ALEN); | ||
895 | wrqu.ap_addr.sa_family = ARPHRD_ETHER; | ||
896 | wireless_send_event(priv->dev, SIOCGIWAP, &wrqu, NULL); | ||
897 | |||
898 | lbs_deb_join("ADHOC_RESP: Joined/started '%s', BSSID %pM, channel %d\n", | ||
899 | print_ssid(ssid, bss->ssid, bss->ssid_len), | ||
900 | priv->curbssparams.bssid, | ||
901 | priv->channel); | ||
902 | |||
903 | done: | ||
904 | lbs_deb_leave_args(LBS_DEB_JOIN, "ret %d", ret); | ||
905 | return ret; | ||
906 | } | ||
907 | |||
908 | /** | ||
909 | * @brief Join an adhoc network found in a previous scan | ||
910 | * | ||
911 | * @param priv A pointer to struct lbs_private structure | ||
912 | * @param assoc_req The association request describing the BSS to join | ||
913 | * | ||
914 | * @return 0 on success, error on failure | ||
915 | */ | ||
916 | static int lbs_adhoc_join(struct lbs_private *priv, | ||
917 | struct assoc_request *assoc_req) | ||
918 | { | ||
919 | struct cmd_ds_802_11_ad_hoc_join cmd; | ||
920 | struct bss_descriptor *bss = &assoc_req->bss; | ||
921 | u8 preamble = RADIO_PREAMBLE_LONG; | ||
922 | DECLARE_SSID_BUF(ssid); | ||
923 | u16 ratesize = 0; | ||
924 | int ret = 0; | ||
925 | |||
926 | lbs_deb_enter(LBS_DEB_ASSOC); | ||
927 | |||
928 | lbs_deb_join("current SSID '%s', ssid length %u\n", | ||
929 | print_ssid(ssid, priv->curbssparams.ssid, | ||
930 | priv->curbssparams.ssid_len), | ||
931 | priv->curbssparams.ssid_len); | ||
932 | lbs_deb_join("requested ssid '%s', ssid length %u\n", | ||
933 | print_ssid(ssid, bss->ssid, bss->ssid_len), | ||
934 | bss->ssid_len); | ||
935 | |||
936 | /* check if the requested SSID is already joined */ | ||
937 | if (priv->curbssparams.ssid_len && | ||
938 | !lbs_ssid_cmp(priv->curbssparams.ssid, | ||
939 | priv->curbssparams.ssid_len, | ||
940 | bss->ssid, bss->ssid_len) && | ||
941 | (priv->mode == IW_MODE_ADHOC) && | ||
942 | (priv->connect_status == LBS_CONNECTED)) { | ||
943 | union iwreq_data wrqu; | ||
944 | |||
945 | lbs_deb_join("ADHOC_J_CMD: New ad-hoc SSID is the same as " | ||
946 | "current, not attempting to re-join"); | ||
947 | |||
948 | /* Send the re-association event though, because the association | ||
949 | * request really was successful, even if just a null-op. | ||
950 | */ | ||
951 | memset(&wrqu, 0, sizeof(wrqu)); | ||
952 | memcpy(wrqu.ap_addr.sa_data, priv->curbssparams.bssid, | ||
953 | ETH_ALEN); | ||
954 | wrqu.ap_addr.sa_family = ARPHRD_ETHER; | ||
955 | wireless_send_event(priv->dev, SIOCGIWAP, &wrqu, NULL); | ||
956 | goto out; | ||
957 | } | ||
958 | |||
959 | /* Use short preamble only when both the BSS and firmware support it */ | ||
960 | if (bss->capability & WLAN_CAPABILITY_SHORT_PREAMBLE) { | ||
961 | lbs_deb_join("AdhocJoin: Short preamble\n"); | ||
962 | preamble = RADIO_PREAMBLE_SHORT; | ||
963 | } | ||
964 | |||
965 | ret = lbs_set_radio(priv, preamble, 1); | ||
966 | if (ret) | ||
967 | goto out; | ||
968 | |||
969 | lbs_deb_join("AdhocJoin: channel = %d\n", assoc_req->channel); | ||
970 | lbs_deb_join("AdhocJoin: band = %c\n", assoc_req->band); | ||
971 | |||
972 | priv->adhoccreate = 0; | ||
973 | priv->channel = bss->channel; | ||
974 | |||
975 | /* Build the join command */ | ||
976 | memset(&cmd, 0, sizeof(cmd)); | ||
977 | cmd.hdr.size = cpu_to_le16(sizeof(cmd)); | ||
978 | |||
979 | cmd.bss.type = CMD_BSS_TYPE_IBSS; | ||
980 | cmd.bss.beaconperiod = cpu_to_le16(bss->beaconperiod); | ||
981 | |||
982 | memcpy(&cmd.bss.bssid, &bss->bssid, ETH_ALEN); | ||
983 | memcpy(&cmd.bss.ssid, &bss->ssid, bss->ssid_len); | ||
984 | |||
985 | memcpy(&cmd.bss.ds, &bss->phy.ds, sizeof(struct ieee_ie_ds_param_set)); | ||
986 | |||
987 | memcpy(&cmd.bss.ibss, &bss->ss.ibss, | ||
988 | sizeof(struct ieee_ie_ibss_param_set)); | ||
989 | |||
990 | cmd.bss.capability = cpu_to_le16(bss->capability & CAPINFO_MASK); | ||
991 | lbs_deb_join("ADHOC_J_CMD: tmpcap=%4X CAPINFO_MASK=%4X\n", | ||
992 | bss->capability, CAPINFO_MASK); | ||
993 | |||
994 | /* information on BSSID descriptor passed to FW */ | ||
995 | lbs_deb_join("ADHOC_J_CMD: BSSID = %pM, SSID = '%s'\n", | ||
996 | cmd.bss.bssid, cmd.bss.ssid); | ||
997 | |||
998 | /* Only v8 and below support setting these */ | ||
999 | if (priv->fwrelease < 0x09000000) { | ||
1000 | /* failtimeout */ | ||
1001 | cmd.failtimeout = cpu_to_le16(MRVDRV_ASSOCIATION_TIME_OUT); | ||
1002 | /* probedelay */ | ||
1003 | cmd.probedelay = cpu_to_le16(CMD_SCAN_PROBE_DELAY_TIME); | ||
1004 | } | ||
1005 | |||
1006 | /* Copy Data rates from the rates recorded in scan response */ | ||
1007 | memset(cmd.bss.rates, 0, sizeof(cmd.bss.rates)); | ||
1008 | ratesize = min_t(u16, ARRAY_SIZE(cmd.bss.rates), ARRAY_SIZE (bss->rates)); | ||
1009 | memcpy(cmd.bss.rates, bss->rates, ratesize); | ||
1010 | if (get_common_rates(priv, cmd.bss.rates, &ratesize)) { | ||
1011 | lbs_deb_join("ADHOC_JOIN: get_common_rates returned error.\n"); | ||
1012 | ret = -1; | ||
1013 | goto out; | ||
1014 | } | ||
1015 | |||
1016 | /* Copy the ad-hoc creation rates into Current BSS state structure */ | ||
1017 | memset(&priv->curbssparams.rates, 0, sizeof(priv->curbssparams.rates)); | ||
1018 | memcpy(&priv->curbssparams.rates, cmd.bss.rates, ratesize); | ||
1019 | |||
1020 | /* Set MSB on basic rates as the firmware requires, but _after_ | ||
1021 | * copying to current bss rates. | ||
1022 | */ | ||
1023 | lbs_set_basic_rate_flags(cmd.bss.rates, ratesize); | ||
1024 | |||
1025 | cmd.bss.ibss.atimwindow = bss->atimwindow; | ||
1026 | |||
1027 | if (assoc_req->secinfo.wep_enabled) { | ||
1028 | u16 tmp = le16_to_cpu(cmd.bss.capability); | ||
1029 | tmp |= WLAN_CAPABILITY_PRIVACY; | ||
1030 | cmd.bss.capability = cpu_to_le16(tmp); | ||
1031 | } | ||
1032 | |||
1033 | if (priv->psmode == LBS802_11POWERMODEMAX_PSP) { | ||
1034 | __le32 local_ps_mode = cpu_to_le32(LBS802_11POWERMODECAM); | ||
1035 | |||
1036 | /* wake up first */ | ||
1037 | ret = lbs_prepare_and_send_command(priv, CMD_802_11_PS_MODE, | ||
1038 | CMD_ACT_SET, 0, 0, | ||
1039 | &local_ps_mode); | ||
1040 | if (ret) { | ||
1041 | ret = -1; | ||
1042 | goto out; | ||
1043 | } | ||
1044 | } | ||
1045 | |||
1046 | ret = lbs_cmd_with_response(priv, CMD_802_11_AD_HOC_JOIN, &cmd); | ||
1047 | if (ret == 0) { | ||
1048 | ret = lbs_adhoc_post(priv, | ||
1049 | (struct cmd_ds_802_11_ad_hoc_result *)&cmd); | ||
1050 | } | ||
1051 | |||
1052 | out: | ||
1053 | lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret); | ||
1054 | return ret; | ||
1055 | } | ||
1056 | |||
1057 | /** | ||
1058 | * @brief Start an Adhoc Network | ||
1059 | * | ||
1060 | * @param priv A pointer to struct lbs_private structure | ||
1061 | * @param assoc_req The association request describing the BSS to start | ||
1062 | * | ||
1063 | * @return 0 on success, error on failure | ||
1064 | */ | ||
1065 | static int lbs_adhoc_start(struct lbs_private *priv, | ||
1066 | struct assoc_request *assoc_req) | ||
1067 | { | ||
1068 | struct cmd_ds_802_11_ad_hoc_start cmd; | ||
1069 | u8 preamble = RADIO_PREAMBLE_SHORT; | ||
1070 | size_t ratesize = 0; | ||
1071 | u16 tmpcap = 0; | ||
1072 | int ret = 0; | ||
1073 | DECLARE_SSID_BUF(ssid); | ||
1074 | |||
1075 | lbs_deb_enter(LBS_DEB_ASSOC); | ||
1076 | |||
1077 | ret = lbs_set_radio(priv, preamble, 1); | ||
1078 | if (ret) | ||
1079 | goto out; | ||
1080 | |||
1081 | /* Build the start command */ | ||
1082 | memset(&cmd, 0, sizeof(cmd)); | ||
1083 | cmd.hdr.size = cpu_to_le16(sizeof(cmd)); | ||
1084 | |||
1085 | memcpy(cmd.ssid, assoc_req->ssid, assoc_req->ssid_len); | ||
1086 | |||
1087 | lbs_deb_join("ADHOC_START: SSID '%s', ssid length %u\n", | ||
1088 | print_ssid(ssid, assoc_req->ssid, assoc_req->ssid_len), | ||
1089 | assoc_req->ssid_len); | ||
1090 | |||
1091 | cmd.bsstype = CMD_BSS_TYPE_IBSS; | ||
1092 | |||
1093 | if (priv->beacon_period == 0) | ||
1094 | priv->beacon_period = MRVDRV_BEACON_INTERVAL; | ||
1095 | cmd.beaconperiod = cpu_to_le16(priv->beacon_period); | ||
1096 | |||
1097 | WARN_ON(!assoc_req->channel); | ||
1098 | |||
1099 | /* set Physical parameter set */ | ||
1100 | cmd.ds.header.id = WLAN_EID_DS_PARAMS; | ||
1101 | cmd.ds.header.len = 1; | ||
1102 | cmd.ds.channel = assoc_req->channel; | ||
1103 | |||
1104 | /* set IBSS parameter set */ | ||
1105 | cmd.ibss.header.id = WLAN_EID_IBSS_PARAMS; | ||
1106 | cmd.ibss.header.len = 2; | ||
1107 | cmd.ibss.atimwindow = cpu_to_le16(0); | ||
1108 | |||
1109 | /* set capability info */ | ||
1110 | tmpcap = WLAN_CAPABILITY_IBSS; | ||
1111 | if (assoc_req->secinfo.wep_enabled || | ||
1112 | assoc_req->secinfo.WPAenabled || | ||
1113 | assoc_req->secinfo.WPA2enabled) { | ||
1114 | lbs_deb_join("ADHOC_START: WEP/WPA enabled, privacy on\n"); | ||
1115 | tmpcap |= WLAN_CAPABILITY_PRIVACY; | ||
1116 | } else | ||
1117 | lbs_deb_join("ADHOC_START: WEP disabled, privacy off\n"); | ||
1118 | |||
1119 | cmd.capability = cpu_to_le16(tmpcap); | ||
1120 | |||
1121 | /* Only v8 and below support setting probe delay */ | ||
1122 | if (priv->fwrelease < 0x09000000) | ||
1123 | cmd.probedelay = cpu_to_le16(CMD_SCAN_PROBE_DELAY_TIME); | ||
1124 | |||
1125 | ratesize = min(sizeof(cmd.rates), sizeof(lbs_bg_rates)); | ||
1126 | memcpy(cmd.rates, lbs_bg_rates, ratesize); | ||
1127 | |||
1128 | /* Copy the ad-hoc creating rates into Current BSS state structure */ | ||
1129 | memset(&priv->curbssparams.rates, 0, sizeof(priv->curbssparams.rates)); | ||
1130 | memcpy(&priv->curbssparams.rates, &cmd.rates, ratesize); | ||
1131 | |||
1132 | /* Set MSB on basic rates as the firmware requires, but _after_ | ||
1133 | * copying to current bss rates. | ||
1134 | */ | ||
1135 | lbs_set_basic_rate_flags(cmd.rates, ratesize); | ||
1136 | |||
1137 | lbs_deb_join("ADHOC_START: rates=%02x %02x %02x %02x\n", | ||
1138 | cmd.rates[0], cmd.rates[1], cmd.rates[2], cmd.rates[3]); | ||
1139 | |||
1140 | lbs_deb_join("ADHOC_START: Starting Ad-Hoc BSS on channel %d, band %d\n", | ||
1141 | assoc_req->channel, assoc_req->band); | ||
1142 | |||
1143 | priv->adhoccreate = 1; | ||
1144 | priv->mode = IW_MODE_ADHOC; | ||
1145 | |||
1146 | ret = lbs_cmd_with_response(priv, CMD_802_11_AD_HOC_START, &cmd); | ||
1147 | if (ret == 0) | ||
1148 | ret = lbs_adhoc_post(priv, | ||
1149 | (struct cmd_ds_802_11_ad_hoc_result *)&cmd); | ||
1150 | |||
1151 | out: | ||
1152 | lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret); | ||
1153 | return ret; | ||
1154 | } | ||
1155 | |||
1156 | /** | ||
1157 | * @brief Stop and Ad-Hoc network and exit Ad-Hoc mode | ||
1158 | * | ||
1159 | * @param priv A pointer to struct lbs_private structure | ||
1160 | * @return 0 on success, or an error | ||
1161 | */ | ||
1162 | int lbs_adhoc_stop(struct lbs_private *priv) | ||
1163 | { | ||
1164 | struct cmd_ds_802_11_ad_hoc_stop cmd; | ||
1165 | int ret; | ||
1166 | |||
1167 | lbs_deb_enter(LBS_DEB_JOIN); | ||
1168 | |||
1169 | memset(&cmd, 0, sizeof (cmd)); | ||
1170 | cmd.hdr.size = cpu_to_le16 (sizeof (cmd)); | ||
1171 | |||
1172 | ret = lbs_cmd_with_response(priv, CMD_802_11_AD_HOC_STOP, &cmd); | ||
1173 | |||
1174 | /* Clean up everything even if there was an error */ | ||
1175 | lbs_mac_event_disconnected(priv); | ||
1176 | |||
1177 | lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret); | ||
1178 | return ret; | ||
1179 | } | ||
1180 | |||
1181 | static inline int match_bss_no_security(struct lbs_802_11_security *secinfo, | ||
1182 | struct bss_descriptor *match_bss) | ||
1183 | { | ||
1184 | if (!secinfo->wep_enabled && | ||
1185 | !secinfo->WPAenabled && !secinfo->WPA2enabled && | ||
1186 | match_bss->wpa_ie[0] != WLAN_EID_GENERIC && | ||
1187 | match_bss->rsn_ie[0] != WLAN_EID_RSN && | ||
1188 | !(match_bss->capability & WLAN_CAPABILITY_PRIVACY)) | ||
1189 | return 1; | ||
1190 | else | ||
1191 | return 0; | ||
1192 | } | ||
1193 | |||
1194 | static inline int match_bss_static_wep(struct lbs_802_11_security *secinfo, | ||
1195 | struct bss_descriptor *match_bss) | ||
1196 | { | ||
1197 | if (secinfo->wep_enabled && | ||
1198 | !secinfo->WPAenabled && !secinfo->WPA2enabled && | ||
1199 | (match_bss->capability & WLAN_CAPABILITY_PRIVACY)) | ||
1200 | return 1; | ||
1201 | else | ||
1202 | return 0; | ||
1203 | } | ||
1204 | |||
1205 | static inline int match_bss_wpa(struct lbs_802_11_security *secinfo, | ||
1206 | struct bss_descriptor *match_bss) | ||
1207 | { | ||
1208 | if (!secinfo->wep_enabled && secinfo->WPAenabled && | ||
1209 | (match_bss->wpa_ie[0] == WLAN_EID_GENERIC) | ||
1210 | /* privacy bit may NOT be set in some APs like LinkSys WRT54G | ||
1211 | && (match_bss->capability & WLAN_CAPABILITY_PRIVACY) */ | ||
1212 | ) | ||
1213 | return 1; | ||
1214 | else | ||
1215 | return 0; | ||
1216 | } | ||
1217 | |||
1218 | static inline int match_bss_wpa2(struct lbs_802_11_security *secinfo, | ||
1219 | struct bss_descriptor *match_bss) | ||
1220 | { | ||
1221 | if (!secinfo->wep_enabled && secinfo->WPA2enabled && | ||
1222 | (match_bss->rsn_ie[0] == WLAN_EID_RSN) | ||
1223 | /* privacy bit may NOT be set in some APs like LinkSys WRT54G | ||
1224 | (match_bss->capability & WLAN_CAPABILITY_PRIVACY) */ | ||
1225 | ) | ||
1226 | return 1; | ||
1227 | else | ||
1228 | return 0; | ||
1229 | } | ||
1230 | |||
1231 | static inline int match_bss_dynamic_wep(struct lbs_802_11_security *secinfo, | ||
1232 | struct bss_descriptor *match_bss) | ||
1233 | { | ||
1234 | if (!secinfo->wep_enabled && | ||
1235 | !secinfo->WPAenabled && !secinfo->WPA2enabled && | ||
1236 | (match_bss->wpa_ie[0] != WLAN_EID_GENERIC) && | ||
1237 | (match_bss->rsn_ie[0] != WLAN_EID_RSN) && | ||
1238 | (match_bss->capability & WLAN_CAPABILITY_PRIVACY)) | ||
1239 | return 1; | ||
1240 | else | ||
1241 | return 0; | ||
1242 | } | ||
1243 | |||
1244 | /** | ||
1245 | * @brief Check if a scanned network compatible with the driver settings | ||
1246 | * | ||
1247 | * WEP WPA WPA2 ad-hoc encrypt Network | ||
1248 | * enabled enabled enabled AES mode privacy WPA WPA2 Compatible | ||
1249 | * 0 0 0 0 NONE 0 0 0 yes No security | ||
1250 | * 1 0 0 0 NONE 1 0 0 yes Static WEP | ||
1251 | * 0 1 0 0 x 1x 1 x yes WPA | ||
1252 | * 0 0 1 0 x 1x x 1 yes WPA2 | ||
1253 | * 0 0 0 1 NONE 1 0 0 yes Ad-hoc AES | ||
1254 | * 0 0 0 0 !=NONE 1 0 0 yes Dynamic WEP | ||
1255 | * | ||
1256 | * | ||
1257 | * @param priv A pointer to struct lbs_private | ||
1258 | * @param index Index in scantable to check against current driver settings | ||
1259 | * @param mode Network mode: Infrastructure or IBSS | ||
1260 | * | ||
1261 | * @return Index in scantable, or error code if negative | ||
1262 | */ | ||
1263 | static int is_network_compatible(struct lbs_private *priv, | ||
1264 | struct bss_descriptor *bss, uint8_t mode) | ||
1265 | { | ||
1266 | int matched = 0; | ||
1267 | |||
1268 | lbs_deb_enter(LBS_DEB_SCAN); | ||
1269 | |||
1270 | if (bss->mode != mode) | ||
1271 | goto done; | ||
1272 | |||
1273 | matched = match_bss_no_security(&priv->secinfo, bss); | ||
1274 | if (matched) | ||
1275 | goto done; | ||
1276 | matched = match_bss_static_wep(&priv->secinfo, bss); | ||
1277 | if (matched) | ||
1278 | goto done; | ||
1279 | matched = match_bss_wpa(&priv->secinfo, bss); | ||
1280 | if (matched) { | ||
1281 | lbs_deb_scan("is_network_compatible() WPA: wpa_ie 0x%x " | ||
1282 | "wpa2_ie 0x%x WEP %s WPA %s WPA2 %s " | ||
1283 | "privacy 0x%x\n", bss->wpa_ie[0], bss->rsn_ie[0], | ||
1284 | priv->secinfo.wep_enabled ? "e" : "d", | ||
1285 | priv->secinfo.WPAenabled ? "e" : "d", | ||
1286 | priv->secinfo.WPA2enabled ? "e" : "d", | ||
1287 | (bss->capability & WLAN_CAPABILITY_PRIVACY)); | ||
1288 | goto done; | ||
1289 | } | ||
1290 | matched = match_bss_wpa2(&priv->secinfo, bss); | ||
1291 | if (matched) { | ||
1292 | lbs_deb_scan("is_network_compatible() WPA2: wpa_ie 0x%x " | ||
1293 | "wpa2_ie 0x%x WEP %s WPA %s WPA2 %s " | ||
1294 | "privacy 0x%x\n", bss->wpa_ie[0], bss->rsn_ie[0], | ||
1295 | priv->secinfo.wep_enabled ? "e" : "d", | ||
1296 | priv->secinfo.WPAenabled ? "e" : "d", | ||
1297 | priv->secinfo.WPA2enabled ? "e" : "d", | ||
1298 | (bss->capability & WLAN_CAPABILITY_PRIVACY)); | ||
1299 | goto done; | ||
1300 | } | ||
1301 | matched = match_bss_dynamic_wep(&priv->secinfo, bss); | ||
1302 | if (matched) { | ||
1303 | lbs_deb_scan("is_network_compatible() dynamic WEP: " | ||
1304 | "wpa_ie 0x%x wpa2_ie 0x%x privacy 0x%x\n", | ||
1305 | bss->wpa_ie[0], bss->rsn_ie[0], | ||
1306 | (bss->capability & WLAN_CAPABILITY_PRIVACY)); | ||
1307 | goto done; | ||
1308 | } | ||
1309 | |||
1310 | /* bss security settings don't match those configured on card */ | ||
1311 | lbs_deb_scan("is_network_compatible() FAILED: wpa_ie 0x%x " | ||
1312 | "wpa2_ie 0x%x WEP %s WPA %s WPA2 %s privacy 0x%x\n", | ||
1313 | bss->wpa_ie[0], bss->rsn_ie[0], | ||
1314 | priv->secinfo.wep_enabled ? "e" : "d", | ||
1315 | priv->secinfo.WPAenabled ? "e" : "d", | ||
1316 | priv->secinfo.WPA2enabled ? "e" : "d", | ||
1317 | (bss->capability & WLAN_CAPABILITY_PRIVACY)); | ||
1318 | |||
1319 | done: | ||
1320 | lbs_deb_leave_args(LBS_DEB_SCAN, "matched: %d", matched); | ||
1321 | return matched; | ||
1322 | } | ||
1323 | |||
1324 | /** | ||
1325 | * @brief This function finds a specific compatible BSSID in the scan list | ||
1326 | * | ||
1327 | * Used in association code | ||
1328 | * | ||
1329 | * @param priv A pointer to struct lbs_private | ||
1330 | * @param bssid BSSID to find in the scan list | ||
1331 | * @param mode Network mode: Infrastructure or IBSS | ||
1332 | * | ||
1333 | * @return index in BSSID list, or error return code (< 0) | ||
1334 | */ | ||
1335 | static struct bss_descriptor *lbs_find_bssid_in_list(struct lbs_private *priv, | ||
1336 | uint8_t *bssid, uint8_t mode) | ||
1337 | { | ||
1338 | struct bss_descriptor *iter_bss; | ||
1339 | struct bss_descriptor *found_bss = NULL; | ||
1340 | |||
1341 | lbs_deb_enter(LBS_DEB_SCAN); | ||
1342 | |||
1343 | if (!bssid) | ||
1344 | goto out; | ||
1345 | |||
1346 | lbs_deb_hex(LBS_DEB_SCAN, "looking for", bssid, ETH_ALEN); | ||
1347 | |||
1348 | /* Look through the scan table for a compatible match. The loop will | ||
1349 | * continue past a matched bssid that is not compatible in case there | ||
1350 | * is an AP with multiple SSIDs assigned to the same BSSID | ||
1351 | */ | ||
1352 | mutex_lock(&priv->lock); | ||
1353 | list_for_each_entry(iter_bss, &priv->network_list, list) { | ||
1354 | if (compare_ether_addr(iter_bss->bssid, bssid)) | ||
1355 | continue; /* bssid doesn't match */ | ||
1356 | switch (mode) { | ||
1357 | case IW_MODE_INFRA: | ||
1358 | case IW_MODE_ADHOC: | ||
1359 | if (!is_network_compatible(priv, iter_bss, mode)) | ||
1360 | break; | ||
1361 | found_bss = iter_bss; | ||
1362 | break; | ||
1363 | default: | ||
1364 | found_bss = iter_bss; | ||
1365 | break; | ||
1366 | } | ||
1367 | } | ||
1368 | mutex_unlock(&priv->lock); | ||
1369 | |||
1370 | out: | ||
1371 | lbs_deb_leave_args(LBS_DEB_SCAN, "found_bss %p", found_bss); | ||
1372 | return found_bss; | ||
1373 | } | ||
1374 | |||
1375 | /** | ||
1376 | * @brief This function finds ssid in ssid list. | ||
1377 | * | ||
1378 | * Used in association code | ||
1379 | * | ||
1380 | * @param priv A pointer to struct lbs_private | ||
1381 | * @param ssid SSID to find in the list | ||
1382 | * @param bssid BSSID to qualify the SSID selection (if provided) | ||
1383 | * @param mode Network mode: Infrastructure or IBSS | ||
1384 | * | ||
1385 | * @return index in BSSID list | ||
1386 | */ | ||
1387 | static struct bss_descriptor *lbs_find_ssid_in_list(struct lbs_private *priv, | ||
1388 | uint8_t *ssid, uint8_t ssid_len, | ||
1389 | uint8_t *bssid, uint8_t mode, | ||
1390 | int channel) | ||
1391 | { | ||
1392 | u32 bestrssi = 0; | ||
1393 | struct bss_descriptor *iter_bss = NULL; | ||
1394 | struct bss_descriptor *found_bss = NULL; | ||
1395 | struct bss_descriptor *tmp_oldest = NULL; | ||
1396 | |||
1397 | lbs_deb_enter(LBS_DEB_SCAN); | ||
1398 | |||
1399 | mutex_lock(&priv->lock); | ||
1400 | |||
1401 | list_for_each_entry(iter_bss, &priv->network_list, list) { | ||
1402 | if (!tmp_oldest || | ||
1403 | (iter_bss->last_scanned < tmp_oldest->last_scanned)) | ||
1404 | tmp_oldest = iter_bss; | ||
1405 | |||
1406 | if (lbs_ssid_cmp(iter_bss->ssid, iter_bss->ssid_len, | ||
1407 | ssid, ssid_len) != 0) | ||
1408 | continue; /* ssid doesn't match */ | ||
1409 | if (bssid && compare_ether_addr(iter_bss->bssid, bssid) != 0) | ||
1410 | continue; /* bssid doesn't match */ | ||
1411 | if ((channel > 0) && (iter_bss->channel != channel)) | ||
1412 | continue; /* channel doesn't match */ | ||
1413 | |||
1414 | switch (mode) { | ||
1415 | case IW_MODE_INFRA: | ||
1416 | case IW_MODE_ADHOC: | ||
1417 | if (!is_network_compatible(priv, iter_bss, mode)) | ||
1418 | break; | ||
1419 | |||
1420 | if (bssid) { | ||
1421 | /* Found requested BSSID */ | ||
1422 | found_bss = iter_bss; | ||
1423 | goto out; | ||
1424 | } | ||
1425 | |||
1426 | if (SCAN_RSSI(iter_bss->rssi) > bestrssi) { | ||
1427 | bestrssi = SCAN_RSSI(iter_bss->rssi); | ||
1428 | found_bss = iter_bss; | ||
1429 | } | ||
1430 | break; | ||
1431 | case IW_MODE_AUTO: | ||
1432 | default: | ||
1433 | if (SCAN_RSSI(iter_bss->rssi) > bestrssi) { | ||
1434 | bestrssi = SCAN_RSSI(iter_bss->rssi); | ||
1435 | found_bss = iter_bss; | ||
1436 | } | ||
1437 | break; | ||
1438 | } | ||
1439 | } | ||
1440 | |||
1441 | out: | ||
1442 | mutex_unlock(&priv->lock); | ||
1443 | lbs_deb_leave_args(LBS_DEB_SCAN, "found_bss %p", found_bss); | ||
1444 | return found_bss; | ||
1445 | } | ||
1446 | |||
1447 | static int assoc_helper_essid(struct lbs_private *priv, | ||
1448 | struct assoc_request * assoc_req) | ||
1449 | { | ||
1450 | int ret = 0; | ||
1451 | struct bss_descriptor * bss; | ||
1452 | int channel = -1; | ||
1453 | DECLARE_SSID_BUF(ssid); | ||
1454 | |||
1455 | lbs_deb_enter(LBS_DEB_ASSOC); | ||
1456 | |||
1457 | /* FIXME: take channel into account when picking SSIDs if a channel | ||
1458 | * is set. | ||
1459 | */ | ||
1460 | |||
1461 | if (test_bit(ASSOC_FLAG_CHANNEL, &assoc_req->flags)) | ||
1462 | channel = assoc_req->channel; | ||
1463 | |||
1464 | lbs_deb_assoc("SSID '%s' requested\n", | ||
1465 | print_ssid(ssid, assoc_req->ssid, assoc_req->ssid_len)); | ||
1466 | if (assoc_req->mode == IW_MODE_INFRA) { | ||
1467 | lbs_send_specific_ssid_scan(priv, assoc_req->ssid, | ||
1468 | assoc_req->ssid_len); | ||
1469 | |||
1470 | bss = lbs_find_ssid_in_list(priv, assoc_req->ssid, | ||
1471 | assoc_req->ssid_len, NULL, IW_MODE_INFRA, channel); | ||
1472 | if (bss != NULL) { | ||
1473 | memcpy(&assoc_req->bss, bss, sizeof(struct bss_descriptor)); | ||
1474 | ret = lbs_try_associate(priv, assoc_req); | ||
1475 | } else { | ||
1476 | lbs_deb_assoc("SSID not found; cannot associate\n"); | ||
1477 | } | ||
1478 | } else if (assoc_req->mode == IW_MODE_ADHOC) { | ||
1479 | /* Scan for the network, do not save previous results. Stale | ||
1480 | * scan data will cause us to join a non-existant adhoc network | ||
1481 | */ | ||
1482 | lbs_send_specific_ssid_scan(priv, assoc_req->ssid, | ||
1483 | assoc_req->ssid_len); | ||
1484 | |||
1485 | /* Search for the requested SSID in the scan table */ | ||
1486 | bss = lbs_find_ssid_in_list(priv, assoc_req->ssid, | ||
1487 | assoc_req->ssid_len, NULL, IW_MODE_ADHOC, channel); | ||
1488 | if (bss != NULL) { | ||
1489 | lbs_deb_assoc("SSID found, will join\n"); | ||
1490 | memcpy(&assoc_req->bss, bss, sizeof(struct bss_descriptor)); | ||
1491 | lbs_adhoc_join(priv, assoc_req); | ||
1492 | } else { | ||
1493 | /* else send START command */ | ||
1494 | lbs_deb_assoc("SSID not found, creating adhoc network\n"); | ||
1495 | memcpy(&assoc_req->bss.ssid, &assoc_req->ssid, | ||
1496 | IEEE80211_MAX_SSID_LEN); | ||
1497 | assoc_req->bss.ssid_len = assoc_req->ssid_len; | ||
1498 | lbs_adhoc_start(priv, assoc_req); | ||
1499 | } | ||
1500 | } | ||
1501 | |||
1502 | lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret); | ||
1503 | return ret; | ||
1504 | } | ||
1505 | |||
1506 | |||
1507 | static int assoc_helper_bssid(struct lbs_private *priv, | ||
1508 | struct assoc_request * assoc_req) | ||
1509 | { | ||
1510 | int ret = 0; | ||
1511 | struct bss_descriptor * bss; | ||
1512 | |||
1513 | lbs_deb_enter_args(LBS_DEB_ASSOC, "BSSID %pM", assoc_req->bssid); | ||
1514 | |||
1515 | /* Search for index position in list for requested MAC */ | ||
1516 | bss = lbs_find_bssid_in_list(priv, assoc_req->bssid, | ||
1517 | assoc_req->mode); | ||
1518 | if (bss == NULL) { | ||
1519 | lbs_deb_assoc("ASSOC: WAP: BSSID %pM not found, " | ||
1520 | "cannot associate.\n", assoc_req->bssid); | ||
1521 | goto out; | ||
1522 | } | ||
1523 | |||
1524 | memcpy(&assoc_req->bss, bss, sizeof(struct bss_descriptor)); | ||
1525 | if (assoc_req->mode == IW_MODE_INFRA) { | ||
1526 | ret = lbs_try_associate(priv, assoc_req); | ||
1527 | lbs_deb_assoc("ASSOC: lbs_try_associate(bssid) returned %d\n", | ||
1528 | ret); | ||
1529 | } else if (assoc_req->mode == IW_MODE_ADHOC) { | ||
1530 | lbs_adhoc_join(priv, assoc_req); | ||
1531 | } | ||
1532 | |||
1533 | out: | ||
1534 | lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret); | ||
1535 | return ret; | ||
1536 | } | ||
1537 | |||
1538 | |||
1539 | static int assoc_helper_associate(struct lbs_private *priv, | ||
1540 | struct assoc_request * assoc_req) | ||
1541 | { | ||
1542 | int ret = 0, done = 0; | ||
1543 | |||
1544 | lbs_deb_enter(LBS_DEB_ASSOC); | ||
1545 | |||
1546 | /* If we're given and 'any' BSSID, try associating based on SSID */ | ||
1547 | |||
1548 | if (test_bit(ASSOC_FLAG_BSSID, &assoc_req->flags)) { | ||
1549 | if (compare_ether_addr(bssid_any, assoc_req->bssid) && | ||
1550 | compare_ether_addr(bssid_off, assoc_req->bssid)) { | ||
1551 | ret = assoc_helper_bssid(priv, assoc_req); | ||
1552 | done = 1; | ||
1553 | } | ||
1554 | } | ||
1555 | |||
1556 | if (!done && test_bit(ASSOC_FLAG_SSID, &assoc_req->flags)) { | ||
1557 | ret = assoc_helper_essid(priv, assoc_req); | ||
1558 | } | ||
1559 | |||
1560 | lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret); | ||
1561 | return ret; | ||
1562 | } | ||
1563 | |||
1564 | |||
1565 | static int assoc_helper_mode(struct lbs_private *priv, | ||
1566 | struct assoc_request * assoc_req) | ||
1567 | { | ||
1568 | int ret = 0; | ||
1569 | |||
1570 | lbs_deb_enter(LBS_DEB_ASSOC); | ||
1571 | |||
1572 | if (assoc_req->mode == priv->mode) | ||
1573 | goto done; | ||
1574 | |||
1575 | if (assoc_req->mode == IW_MODE_INFRA) { | ||
1576 | if (priv->psstate != PS_STATE_FULL_POWER) | ||
1577 | lbs_ps_wakeup(priv, CMD_OPTION_WAITFORRSP); | ||
1578 | priv->psmode = LBS802_11POWERMODECAM; | ||
1579 | } | ||
1580 | |||
1581 | priv->mode = assoc_req->mode; | ||
1582 | ret = lbs_set_snmp_mib(priv, SNMP_MIB_OID_BSS_TYPE, | ||
1583 | assoc_req->mode == IW_MODE_ADHOC ? 2 : 1); | ||
1584 | |||
1585 | done: | ||
1586 | lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret); | ||
1587 | return ret; | ||
1588 | } | ||
1589 | |||
1590 | static int assoc_helper_channel(struct lbs_private *priv, | ||
1591 | struct assoc_request * assoc_req) | ||
1592 | { | ||
1593 | int ret = 0; | ||
1594 | |||
1595 | lbs_deb_enter(LBS_DEB_ASSOC); | ||
1596 | |||
1597 | ret = lbs_update_channel(priv); | ||
1598 | if (ret) { | ||
1599 | lbs_deb_assoc("ASSOC: channel: error getting channel.\n"); | ||
1600 | goto done; | ||
1601 | } | ||
1602 | |||
1603 | if (assoc_req->channel == priv->channel) | ||
1604 | goto done; | ||
1605 | |||
1606 | if (priv->mesh_dev) { | ||
1607 | /* Change mesh channel first; 21.p21 firmware won't let | ||
1608 | you change channel otherwise (even though it'll return | ||
1609 | an error to this */ | ||
1610 | lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_STOP, | ||
1611 | assoc_req->channel); | ||
1612 | } | ||
1613 | |||
1614 | lbs_deb_assoc("ASSOC: channel: %d -> %d\n", | ||
1615 | priv->channel, assoc_req->channel); | ||
1616 | |||
1617 | ret = lbs_set_channel(priv, assoc_req->channel); | ||
1618 | if (ret < 0) | ||
1619 | lbs_deb_assoc("ASSOC: channel: error setting channel.\n"); | ||
1620 | |||
1621 | /* FIXME: shouldn't need to grab the channel _again_ after setting | ||
1622 | * it since the firmware is supposed to return the new channel, but | ||
1623 | * whatever... */ | ||
1624 | ret = lbs_update_channel(priv); | ||
1625 | if (ret) { | ||
1626 | lbs_deb_assoc("ASSOC: channel: error getting channel.\n"); | ||
1627 | goto done; | ||
1628 | } | ||
1629 | |||
1630 | if (assoc_req->channel != priv->channel) { | ||
1631 | lbs_deb_assoc("ASSOC: channel: failed to update channel to %d\n", | ||
1632 | assoc_req->channel); | ||
1633 | goto restore_mesh; | ||
1634 | } | ||
1635 | |||
1636 | if (assoc_req->secinfo.wep_enabled && | ||
1637 | (assoc_req->wep_keys[0].len || assoc_req->wep_keys[1].len || | ||
1638 | assoc_req->wep_keys[2].len || assoc_req->wep_keys[3].len)) { | ||
1639 | /* Make sure WEP keys are re-sent to firmware */ | ||
1640 | set_bit(ASSOC_FLAG_WEP_KEYS, &assoc_req->flags); | ||
1641 | } | ||
1642 | |||
1643 | /* Must restart/rejoin adhoc networks after channel change */ | ||
1644 | set_bit(ASSOC_FLAG_SSID, &assoc_req->flags); | ||
1645 | |||
1646 | restore_mesh: | ||
1647 | if (priv->mesh_dev) | ||
1648 | lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_START, | ||
1649 | priv->channel); | ||
1650 | |||
1651 | done: | ||
1652 | lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret); | ||
1653 | return ret; | ||
1654 | } | ||
1655 | |||
1656 | |||
1657 | static int assoc_helper_wep_keys(struct lbs_private *priv, | ||
1658 | struct assoc_request *assoc_req) | ||
1659 | { | ||
1660 | int i; | ||
1661 | int ret = 0; | ||
1662 | |||
1663 | lbs_deb_enter(LBS_DEB_ASSOC); | ||
1664 | |||
1665 | /* Set or remove WEP keys */ | ||
1666 | if (assoc_req->wep_keys[0].len || assoc_req->wep_keys[1].len || | ||
1667 | assoc_req->wep_keys[2].len || assoc_req->wep_keys[3].len) | ||
1668 | ret = lbs_cmd_802_11_set_wep(priv, CMD_ACT_ADD, assoc_req); | ||
1669 | else | ||
1670 | ret = lbs_cmd_802_11_set_wep(priv, CMD_ACT_REMOVE, assoc_req); | ||
1671 | |||
1672 | if (ret) | ||
1673 | goto out; | ||
1674 | |||
1675 | /* enable/disable the MAC's WEP packet filter */ | ||
1676 | if (assoc_req->secinfo.wep_enabled) | ||
1677 | priv->mac_control |= CMD_ACT_MAC_WEP_ENABLE; | ||
1678 | else | ||
1679 | priv->mac_control &= ~CMD_ACT_MAC_WEP_ENABLE; | ||
1680 | |||
1681 | lbs_set_mac_control(priv); | ||
1682 | |||
1683 | mutex_lock(&priv->lock); | ||
1684 | |||
1685 | /* Copy WEP keys into priv wep key fields */ | ||
1686 | for (i = 0; i < 4; i++) { | ||
1687 | memcpy(&priv->wep_keys[i], &assoc_req->wep_keys[i], | ||
1688 | sizeof(struct enc_key)); | ||
1689 | } | ||
1690 | priv->wep_tx_keyidx = assoc_req->wep_tx_keyidx; | ||
1691 | |||
1692 | mutex_unlock(&priv->lock); | ||
1693 | |||
1694 | out: | ||
1695 | lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret); | ||
1696 | return ret; | ||
1697 | } | ||
1698 | |||
1699 | static int assoc_helper_secinfo(struct lbs_private *priv, | ||
1700 | struct assoc_request * assoc_req) | ||
1701 | { | ||
1702 | int ret = 0; | ||
1703 | uint16_t do_wpa; | ||
1704 | uint16_t rsn = 0; | ||
1705 | |||
1706 | lbs_deb_enter(LBS_DEB_ASSOC); | ||
1707 | |||
1708 | memcpy(&priv->secinfo, &assoc_req->secinfo, | ||
1709 | sizeof(struct lbs_802_11_security)); | ||
1710 | |||
1711 | lbs_set_mac_control(priv); | ||
1712 | |||
1713 | /* If RSN is already enabled, don't try to enable it again, since | ||
1714 | * ENABLE_RSN resets internal state machines and will clobber the | ||
1715 | * 4-way WPA handshake. | ||
1716 | */ | ||
1717 | |||
1718 | /* Get RSN enabled/disabled */ | ||
1719 | ret = lbs_cmd_802_11_enable_rsn(priv, CMD_ACT_GET, &rsn); | ||
1720 | if (ret) { | ||
1721 | lbs_deb_assoc("Failed to get RSN status: %d\n", ret); | ||
1722 | goto out; | ||
1723 | } | ||
1724 | |||
1725 | /* Don't re-enable RSN if it's already enabled */ | ||
1726 | do_wpa = assoc_req->secinfo.WPAenabled || assoc_req->secinfo.WPA2enabled; | ||
1727 | if (do_wpa == rsn) | ||
1728 | goto out; | ||
1729 | |||
1730 | /* Set RSN enabled/disabled */ | ||
1731 | ret = lbs_cmd_802_11_enable_rsn(priv, CMD_ACT_SET, &do_wpa); | ||
1732 | |||
1733 | out: | ||
1734 | lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret); | ||
1735 | return ret; | ||
1736 | } | ||
1737 | |||
1738 | |||
1739 | static int assoc_helper_wpa_keys(struct lbs_private *priv, | ||
1740 | struct assoc_request * assoc_req) | ||
1741 | { | ||
1742 | int ret = 0; | ||
1743 | unsigned int flags = assoc_req->flags; | ||
1744 | |||
1745 | lbs_deb_enter(LBS_DEB_ASSOC); | ||
1746 | |||
1747 | /* Work around older firmware bug where WPA unicast and multicast | ||
1748 | * keys must be set independently. Seen in SDIO parts with firmware | ||
1749 | * version 5.0.11p0. | ||
1750 | */ | ||
1751 | |||
1752 | if (test_bit(ASSOC_FLAG_WPA_UCAST_KEY, &assoc_req->flags)) { | ||
1753 | clear_bit(ASSOC_FLAG_WPA_MCAST_KEY, &assoc_req->flags); | ||
1754 | ret = lbs_cmd_802_11_key_material(priv, CMD_ACT_SET, assoc_req); | ||
1755 | assoc_req->flags = flags; | ||
1756 | } | ||
1757 | |||
1758 | if (ret) | ||
1759 | goto out; | ||
1760 | |||
1761 | memcpy(&priv->wpa_unicast_key, &assoc_req->wpa_unicast_key, | ||
1762 | sizeof(struct enc_key)); | ||
1763 | |||
1764 | if (test_bit(ASSOC_FLAG_WPA_MCAST_KEY, &assoc_req->flags)) { | ||
1765 | clear_bit(ASSOC_FLAG_WPA_UCAST_KEY, &assoc_req->flags); | ||
1766 | |||
1767 | ret = lbs_cmd_802_11_key_material(priv, CMD_ACT_SET, assoc_req); | ||
1768 | assoc_req->flags = flags; | ||
1769 | |||
1770 | memcpy(&priv->wpa_mcast_key, &assoc_req->wpa_mcast_key, | ||
1771 | sizeof(struct enc_key)); | ||
1772 | } | ||
1773 | |||
1774 | out: | ||
1775 | lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret); | ||
1776 | return ret; | ||
1777 | } | ||
1778 | |||
1779 | |||
1780 | static int assoc_helper_wpa_ie(struct lbs_private *priv, | ||
1781 | struct assoc_request * assoc_req) | ||
1782 | { | ||
1783 | int ret = 0; | ||
1784 | |||
1785 | lbs_deb_enter(LBS_DEB_ASSOC); | ||
1786 | |||
1787 | if (assoc_req->secinfo.WPAenabled || assoc_req->secinfo.WPA2enabled) { | ||
1788 | memcpy(&priv->wpa_ie, &assoc_req->wpa_ie, assoc_req->wpa_ie_len); | ||
1789 | priv->wpa_ie_len = assoc_req->wpa_ie_len; | ||
1790 | } else { | ||
1791 | memset(&priv->wpa_ie, 0, MAX_WPA_IE_LEN); | ||
1792 | priv->wpa_ie_len = 0; | ||
1793 | } | ||
1794 | |||
1795 | lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret); | ||
1796 | return ret; | ||
1797 | } | ||
1798 | |||
1799 | |||
1800 | static int should_deauth_infrastructure(struct lbs_private *priv, | ||
1801 | struct assoc_request * assoc_req) | ||
1802 | { | ||
1803 | int ret = 0; | ||
1804 | |||
1805 | if (priv->connect_status != LBS_CONNECTED) | ||
1806 | return 0; | ||
1807 | |||
1808 | lbs_deb_enter(LBS_DEB_ASSOC); | ||
1809 | if (test_bit(ASSOC_FLAG_SSID, &assoc_req->flags)) { | ||
1810 | lbs_deb_assoc("Deauthenticating due to new SSID\n"); | ||
1811 | ret = 1; | ||
1812 | goto out; | ||
1813 | } | ||
1814 | |||
1815 | if (test_bit(ASSOC_FLAG_SECINFO, &assoc_req->flags)) { | ||
1816 | if (priv->secinfo.auth_mode != assoc_req->secinfo.auth_mode) { | ||
1817 | lbs_deb_assoc("Deauthenticating due to new security\n"); | ||
1818 | ret = 1; | ||
1819 | goto out; | ||
1820 | } | ||
1821 | } | ||
1822 | |||
1823 | if (test_bit(ASSOC_FLAG_BSSID, &assoc_req->flags)) { | ||
1824 | lbs_deb_assoc("Deauthenticating due to new BSSID\n"); | ||
1825 | ret = 1; | ||
1826 | goto out; | ||
1827 | } | ||
1828 | |||
1829 | if (test_bit(ASSOC_FLAG_CHANNEL, &assoc_req->flags)) { | ||
1830 | lbs_deb_assoc("Deauthenticating due to channel switch\n"); | ||
1831 | ret = 1; | ||
1832 | goto out; | ||
1833 | } | ||
1834 | |||
1835 | /* FIXME: deal with 'auto' mode somehow */ | ||
1836 | if (test_bit(ASSOC_FLAG_MODE, &assoc_req->flags)) { | ||
1837 | if (assoc_req->mode != IW_MODE_INFRA) { | ||
1838 | lbs_deb_assoc("Deauthenticating due to leaving " | ||
1839 | "infra mode\n"); | ||
1840 | ret = 1; | ||
1841 | goto out; | ||
1842 | } | ||
1843 | } | ||
1844 | |||
1845 | out: | ||
1846 | lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret); | ||
1847 | return ret; | ||
1848 | } | ||
1849 | |||
1850 | |||
1851 | static int should_stop_adhoc(struct lbs_private *priv, | ||
1852 | struct assoc_request * assoc_req) | ||
1853 | { | ||
1854 | lbs_deb_enter(LBS_DEB_ASSOC); | ||
1855 | |||
1856 | if (priv->connect_status != LBS_CONNECTED) | ||
1857 | return 0; | ||
1858 | |||
1859 | if (lbs_ssid_cmp(priv->curbssparams.ssid, | ||
1860 | priv->curbssparams.ssid_len, | ||
1861 | assoc_req->ssid, assoc_req->ssid_len) != 0) | ||
1862 | return 1; | ||
1863 | |||
1864 | /* FIXME: deal with 'auto' mode somehow */ | ||
1865 | if (test_bit(ASSOC_FLAG_MODE, &assoc_req->flags)) { | ||
1866 | if (assoc_req->mode != IW_MODE_ADHOC) | ||
1867 | return 1; | ||
1868 | } | ||
1869 | |||
1870 | if (test_bit(ASSOC_FLAG_CHANNEL, &assoc_req->flags)) { | ||
1871 | if (assoc_req->channel != priv->channel) | ||
1872 | return 1; | ||
1873 | } | ||
1874 | |||
1875 | lbs_deb_leave(LBS_DEB_ASSOC); | ||
1876 | return 0; | ||
1877 | } | ||
1878 | |||
1879 | |||
1880 | /** | ||
1881 | * @brief This function finds the best SSID in the Scan List | ||
1882 | * | ||
1883 | * Search the scan table for the best SSID that also matches the current | ||
1884 | * adapter network preference (infrastructure or adhoc) | ||
1885 | * | ||
1886 | * @param priv A pointer to struct lbs_private | ||
1887 | * | ||
1888 | * @return index in BSSID list | ||
1889 | */ | ||
1890 | static struct bss_descriptor *lbs_find_best_ssid_in_list( | ||
1891 | struct lbs_private *priv, uint8_t mode) | ||
1892 | { | ||
1893 | uint8_t bestrssi = 0; | ||
1894 | struct bss_descriptor *iter_bss; | ||
1895 | struct bss_descriptor *best_bss = NULL; | ||
1896 | |||
1897 | lbs_deb_enter(LBS_DEB_SCAN); | ||
1898 | |||
1899 | mutex_lock(&priv->lock); | ||
1900 | |||
1901 | list_for_each_entry(iter_bss, &priv->network_list, list) { | ||
1902 | switch (mode) { | ||
1903 | case IW_MODE_INFRA: | ||
1904 | case IW_MODE_ADHOC: | ||
1905 | if (!is_network_compatible(priv, iter_bss, mode)) | ||
1906 | break; | ||
1907 | if (SCAN_RSSI(iter_bss->rssi) <= bestrssi) | ||
1908 | break; | ||
1909 | bestrssi = SCAN_RSSI(iter_bss->rssi); | ||
1910 | best_bss = iter_bss; | ||
1911 | break; | ||
1912 | case IW_MODE_AUTO: | ||
1913 | default: | ||
1914 | if (SCAN_RSSI(iter_bss->rssi) <= bestrssi) | ||
1915 | break; | ||
1916 | bestrssi = SCAN_RSSI(iter_bss->rssi); | ||
1917 | best_bss = iter_bss; | ||
1918 | break; | ||
1919 | } | ||
1920 | } | ||
1921 | |||
1922 | mutex_unlock(&priv->lock); | ||
1923 | lbs_deb_leave_args(LBS_DEB_SCAN, "best_bss %p", best_bss); | ||
1924 | return best_bss; | ||
1925 | } | ||
1926 | |||
1927 | /** | ||
1928 | * @brief Find the best AP | ||
1929 | * | ||
1930 | * Used from association worker. | ||
1931 | * | ||
1932 | * @param priv A pointer to struct lbs_private structure | ||
1933 | * @param pSSID A pointer to AP's ssid | ||
1934 | * | ||
1935 | * @return 0--success, otherwise--fail | ||
1936 | */ | ||
1937 | static int lbs_find_best_network_ssid(struct lbs_private *priv, | ||
1938 | uint8_t *out_ssid, uint8_t *out_ssid_len, uint8_t preferred_mode, | ||
1939 | uint8_t *out_mode) | ||
1940 | { | ||
1941 | int ret = -1; | ||
1942 | struct bss_descriptor *found; | ||
1943 | |||
1944 | lbs_deb_enter(LBS_DEB_SCAN); | ||
1945 | |||
1946 | priv->scan_ssid_len = 0; | ||
1947 | lbs_scan_networks(priv, 1); | ||
1948 | if (priv->surpriseremoved) | ||
1949 | goto out; | ||
1950 | |||
1951 | found = lbs_find_best_ssid_in_list(priv, preferred_mode); | ||
1952 | if (found && (found->ssid_len > 0)) { | ||
1953 | memcpy(out_ssid, &found->ssid, IEEE80211_MAX_SSID_LEN); | ||
1954 | *out_ssid_len = found->ssid_len; | ||
1955 | *out_mode = found->mode; | ||
1956 | ret = 0; | ||
1957 | } | ||
1958 | |||
1959 | out: | ||
1960 | lbs_deb_leave_args(LBS_DEB_SCAN, "ret %d", ret); | ||
1961 | return ret; | ||
1962 | } | ||
1963 | |||
1964 | |||
1965 | void lbs_association_worker(struct work_struct *work) | ||
1966 | { | ||
1967 | struct lbs_private *priv = container_of(work, struct lbs_private, | ||
1968 | assoc_work.work); | ||
1969 | struct assoc_request * assoc_req = NULL; | ||
1970 | int ret = 0; | ||
1971 | int find_any_ssid = 0; | ||
1972 | DECLARE_SSID_BUF(ssid); | ||
1973 | |||
1974 | lbs_deb_enter(LBS_DEB_ASSOC); | ||
1975 | |||
1976 | mutex_lock(&priv->lock); | ||
1977 | assoc_req = priv->pending_assoc_req; | ||
1978 | priv->pending_assoc_req = NULL; | ||
1979 | priv->in_progress_assoc_req = assoc_req; | ||
1980 | mutex_unlock(&priv->lock); | ||
1981 | |||
1982 | if (!assoc_req) | ||
1983 | goto done; | ||
1984 | |||
1985 | lbs_deb_assoc( | ||
1986 | "Association Request:\n" | ||
1987 | " flags: 0x%08lx\n" | ||
1988 | " SSID: '%s'\n" | ||
1989 | " chann: %d\n" | ||
1990 | " band: %d\n" | ||
1991 | " mode: %d\n" | ||
1992 | " BSSID: %pM\n" | ||
1993 | " secinfo: %s%s%s\n" | ||
1994 | " auth_mode: %d\n", | ||
1995 | assoc_req->flags, | ||
1996 | print_ssid(ssid, assoc_req->ssid, assoc_req->ssid_len), | ||
1997 | assoc_req->channel, assoc_req->band, assoc_req->mode, | ||
1998 | assoc_req->bssid, | ||
1999 | assoc_req->secinfo.WPAenabled ? " WPA" : "", | ||
2000 | assoc_req->secinfo.WPA2enabled ? " WPA2" : "", | ||
2001 | assoc_req->secinfo.wep_enabled ? " WEP" : "", | ||
2002 | assoc_req->secinfo.auth_mode); | ||
2003 | |||
2004 | /* If 'any' SSID was specified, find an SSID to associate with */ | ||
2005 | if (test_bit(ASSOC_FLAG_SSID, &assoc_req->flags) && | ||
2006 | !assoc_req->ssid_len) | ||
2007 | find_any_ssid = 1; | ||
2008 | |||
2009 | /* But don't use 'any' SSID if there's a valid locked BSSID to use */ | ||
2010 | if (test_bit(ASSOC_FLAG_BSSID, &assoc_req->flags)) { | ||
2011 | if (compare_ether_addr(assoc_req->bssid, bssid_any) && | ||
2012 | compare_ether_addr(assoc_req->bssid, bssid_off)) | ||
2013 | find_any_ssid = 0; | ||
2014 | } | ||
2015 | |||
2016 | if (find_any_ssid) { | ||
2017 | u8 new_mode = assoc_req->mode; | ||
2018 | |||
2019 | ret = lbs_find_best_network_ssid(priv, assoc_req->ssid, | ||
2020 | &assoc_req->ssid_len, assoc_req->mode, &new_mode); | ||
2021 | if (ret) { | ||
2022 | lbs_deb_assoc("Could not find best network\n"); | ||
2023 | ret = -ENETUNREACH; | ||
2024 | goto out; | ||
2025 | } | ||
2026 | |||
2027 | /* Ensure we switch to the mode of the AP */ | ||
2028 | if (assoc_req->mode == IW_MODE_AUTO) { | ||
2029 | set_bit(ASSOC_FLAG_MODE, &assoc_req->flags); | ||
2030 | assoc_req->mode = new_mode; | ||
2031 | } | ||
2032 | } | ||
2033 | |||
2034 | /* | ||
2035 | * Check if the attributes being changing require deauthentication | ||
2036 | * from the currently associated infrastructure access point. | ||
2037 | */ | ||
2038 | if (priv->mode == IW_MODE_INFRA) { | ||
2039 | if (should_deauth_infrastructure(priv, assoc_req)) { | ||
2040 | ret = lbs_cmd_80211_deauthenticate(priv, | ||
2041 | priv->curbssparams.bssid, | ||
2042 | WLAN_REASON_DEAUTH_LEAVING); | ||
2043 | if (ret) { | ||
2044 | lbs_deb_assoc("Deauthentication due to new " | ||
2045 | "configuration request failed: %d\n", | ||
2046 | ret); | ||
2047 | } | ||
2048 | } | ||
2049 | } else if (priv->mode == IW_MODE_ADHOC) { | ||
2050 | if (should_stop_adhoc(priv, assoc_req)) { | ||
2051 | ret = lbs_adhoc_stop(priv); | ||
2052 | if (ret) { | ||
2053 | lbs_deb_assoc("Teardown of AdHoc network due to " | ||
2054 | "new configuration request failed: %d\n", | ||
2055 | ret); | ||
2056 | } | ||
2057 | |||
2058 | } | ||
2059 | } | ||
2060 | |||
2061 | /* Send the various configuration bits to the firmware */ | ||
2062 | if (test_bit(ASSOC_FLAG_MODE, &assoc_req->flags)) { | ||
2063 | ret = assoc_helper_mode(priv, assoc_req); | ||
2064 | if (ret) | ||
2065 | goto out; | ||
2066 | } | ||
2067 | |||
2068 | if (test_bit(ASSOC_FLAG_CHANNEL, &assoc_req->flags)) { | ||
2069 | ret = assoc_helper_channel(priv, assoc_req); | ||
2070 | if (ret) | ||
2071 | goto out; | ||
2072 | } | ||
2073 | |||
2074 | if (test_bit(ASSOC_FLAG_SECINFO, &assoc_req->flags)) { | ||
2075 | ret = assoc_helper_secinfo(priv, assoc_req); | ||
2076 | if (ret) | ||
2077 | goto out; | ||
2078 | } | ||
2079 | |||
2080 | if (test_bit(ASSOC_FLAG_WPA_IE, &assoc_req->flags)) { | ||
2081 | ret = assoc_helper_wpa_ie(priv, assoc_req); | ||
2082 | if (ret) | ||
2083 | goto out; | ||
2084 | } | ||
2085 | |||
2086 | /* | ||
2087 | * v10 FW wants WPA keys to be set/cleared before WEP key operations, | ||
2088 | * otherwise it will fail to correctly associate to WEP networks. | ||
2089 | * Other firmware versions don't appear to care. | ||
2090 | */ | ||
2091 | if (test_bit(ASSOC_FLAG_WPA_MCAST_KEY, &assoc_req->flags) || | ||
2092 | test_bit(ASSOC_FLAG_WPA_UCAST_KEY, &assoc_req->flags)) { | ||
2093 | ret = assoc_helper_wpa_keys(priv, assoc_req); | ||
2094 | if (ret) | ||
2095 | goto out; | ||
2096 | } | ||
2097 | |||
2098 | if (test_bit(ASSOC_FLAG_WEP_KEYS, &assoc_req->flags) || | ||
2099 | test_bit(ASSOC_FLAG_WEP_TX_KEYIDX, &assoc_req->flags)) { | ||
2100 | ret = assoc_helper_wep_keys(priv, assoc_req); | ||
2101 | if (ret) | ||
2102 | goto out; | ||
2103 | } | ||
2104 | |||
2105 | |||
2106 | /* SSID/BSSID should be the _last_ config option set, because they | ||
2107 | * trigger the association attempt. | ||
2108 | */ | ||
2109 | if (test_bit(ASSOC_FLAG_BSSID, &assoc_req->flags) || | ||
2110 | test_bit(ASSOC_FLAG_SSID, &assoc_req->flags)) { | ||
2111 | int success = 1; | ||
2112 | |||
2113 | ret = assoc_helper_associate(priv, assoc_req); | ||
2114 | if (ret) { | ||
2115 | lbs_deb_assoc("ASSOC: association unsuccessful: %d\n", | ||
2116 | ret); | ||
2117 | success = 0; | ||
2118 | } | ||
2119 | |||
2120 | if (priv->connect_status != LBS_CONNECTED) { | ||
2121 | lbs_deb_assoc("ASSOC: association unsuccessful, " | ||
2122 | "not connected\n"); | ||
2123 | success = 0; | ||
2124 | } | ||
2125 | |||
2126 | if (success) { | ||
2127 | lbs_deb_assoc("associated to %pM\n", | ||
2128 | priv->curbssparams.bssid); | ||
2129 | lbs_prepare_and_send_command(priv, | ||
2130 | CMD_802_11_RSSI, | ||
2131 | 0, CMD_OPTION_WAITFORRSP, 0, NULL); | ||
2132 | } else { | ||
2133 | ret = -1; | ||
2134 | } | ||
2135 | } | ||
2136 | |||
2137 | out: | ||
2138 | if (ret) { | ||
2139 | lbs_deb_assoc("ASSOC: reconfiguration attempt unsuccessful: %d\n", | ||
2140 | ret); | ||
2141 | } | ||
2142 | |||
2143 | mutex_lock(&priv->lock); | ||
2144 | priv->in_progress_assoc_req = NULL; | ||
2145 | mutex_unlock(&priv->lock); | ||
2146 | kfree(assoc_req); | ||
2147 | |||
2148 | done: | ||
2149 | lbs_deb_leave(LBS_DEB_ASSOC); | ||
2150 | } | ||
2151 | |||
2152 | |||
2153 | /* | ||
2154 | * Caller MUST hold any necessary locks | ||
2155 | */ | ||
2156 | struct assoc_request *lbs_get_association_request(struct lbs_private *priv) | ||
2157 | { | ||
2158 | struct assoc_request * assoc_req; | ||
2159 | |||
2160 | lbs_deb_enter(LBS_DEB_ASSOC); | ||
2161 | if (!priv->pending_assoc_req) { | ||
2162 | priv->pending_assoc_req = kzalloc(sizeof(struct assoc_request), | ||
2163 | GFP_KERNEL); | ||
2164 | if (!priv->pending_assoc_req) { | ||
2165 | lbs_pr_info("Not enough memory to allocate association" | ||
2166 | " request!\n"); | ||
2167 | return NULL; | ||
2168 | } | ||
2169 | } | ||
2170 | |||
2171 | /* Copy current configuration attributes to the association request, | ||
2172 | * but don't overwrite any that are already set. | ||
2173 | */ | ||
2174 | assoc_req = priv->pending_assoc_req; | ||
2175 | if (!test_bit(ASSOC_FLAG_SSID, &assoc_req->flags)) { | ||
2176 | memcpy(&assoc_req->ssid, &priv->curbssparams.ssid, | ||
2177 | IEEE80211_MAX_SSID_LEN); | ||
2178 | assoc_req->ssid_len = priv->curbssparams.ssid_len; | ||
2179 | } | ||
2180 | |||
2181 | if (!test_bit(ASSOC_FLAG_CHANNEL, &assoc_req->flags)) | ||
2182 | assoc_req->channel = priv->channel; | ||
2183 | |||
2184 | if (!test_bit(ASSOC_FLAG_BAND, &assoc_req->flags)) | ||
2185 | assoc_req->band = priv->curbssparams.band; | ||
2186 | |||
2187 | if (!test_bit(ASSOC_FLAG_MODE, &assoc_req->flags)) | ||
2188 | assoc_req->mode = priv->mode; | ||
2189 | |||
2190 | if (!test_bit(ASSOC_FLAG_BSSID, &assoc_req->flags)) { | ||
2191 | memcpy(&assoc_req->bssid, priv->curbssparams.bssid, | ||
2192 | ETH_ALEN); | ||
2193 | } | ||
2194 | |||
2195 | if (!test_bit(ASSOC_FLAG_WEP_KEYS, &assoc_req->flags)) { | ||
2196 | int i; | ||
2197 | for (i = 0; i < 4; i++) { | ||
2198 | memcpy(&assoc_req->wep_keys[i], &priv->wep_keys[i], | ||
2199 | sizeof(struct enc_key)); | ||
2200 | } | ||
2201 | } | ||
2202 | |||
2203 | if (!test_bit(ASSOC_FLAG_WEP_TX_KEYIDX, &assoc_req->flags)) | ||
2204 | assoc_req->wep_tx_keyidx = priv->wep_tx_keyidx; | ||
2205 | |||
2206 | if (!test_bit(ASSOC_FLAG_WPA_MCAST_KEY, &assoc_req->flags)) { | ||
2207 | memcpy(&assoc_req->wpa_mcast_key, &priv->wpa_mcast_key, | ||
2208 | sizeof(struct enc_key)); | ||
2209 | } | ||
2210 | |||
2211 | if (!test_bit(ASSOC_FLAG_WPA_UCAST_KEY, &assoc_req->flags)) { | ||
2212 | memcpy(&assoc_req->wpa_unicast_key, &priv->wpa_unicast_key, | ||
2213 | sizeof(struct enc_key)); | ||
2214 | } | ||
2215 | |||
2216 | if (!test_bit(ASSOC_FLAG_SECINFO, &assoc_req->flags)) { | ||
2217 | memcpy(&assoc_req->secinfo, &priv->secinfo, | ||
2218 | sizeof(struct lbs_802_11_security)); | ||
2219 | } | ||
2220 | |||
2221 | if (!test_bit(ASSOC_FLAG_WPA_IE, &assoc_req->flags)) { | ||
2222 | memcpy(&assoc_req->wpa_ie, &priv->wpa_ie, | ||
2223 | MAX_WPA_IE_LEN); | ||
2224 | assoc_req->wpa_ie_len = priv->wpa_ie_len; | ||
2225 | } | ||
2226 | |||
2227 | lbs_deb_leave(LBS_DEB_ASSOC); | ||
2228 | return assoc_req; | ||
2229 | } | ||
2230 | |||
2231 | |||
2232 | /** | ||
2233 | * @brief Deauthenticate from a specific BSS | ||
2234 | * | ||
2235 | * @param priv A pointer to struct lbs_private structure | ||
2236 | * @param bssid The specific BSS to deauthenticate from | ||
2237 | * @param reason The 802.11 sec. 7.3.1.7 Reason Code for deauthenticating | ||
2238 | * | ||
2239 | * @return 0 on success, error on failure | ||
2240 | */ | ||
2241 | int lbs_cmd_80211_deauthenticate(struct lbs_private *priv, u8 bssid[ETH_ALEN], | ||
2242 | u16 reason) | ||
2243 | { | ||
2244 | struct cmd_ds_802_11_deauthenticate cmd; | ||
2245 | int ret; | ||
2246 | |||
2247 | lbs_deb_enter(LBS_DEB_JOIN); | ||
2248 | |||
2249 | memset(&cmd, 0, sizeof(cmd)); | ||
2250 | cmd.hdr.size = cpu_to_le16(sizeof(cmd)); | ||
2251 | memcpy(cmd.macaddr, &bssid[0], ETH_ALEN); | ||
2252 | cmd.reasoncode = cpu_to_le16(reason); | ||
2253 | |||
2254 | ret = lbs_cmd_with_response(priv, CMD_802_11_DEAUTHENTICATE, &cmd); | ||
2255 | |||
2256 | /* Clean up everything even if there was an error; can't assume that | ||
2257 | * we're still authenticated to the AP after trying to deauth. | ||
2258 | */ | ||
2259 | lbs_mac_event_disconnected(priv); | ||
2260 | |||
2261 | lbs_deb_leave(LBS_DEB_JOIN); | ||
2262 | return ret; | ||
2263 | } | ||
2264 | |||