Commit | Line | Data |
---|---|---|
876c9d3a MT |
1 | /* Copyright (C) 2006, Red Hat, Inc. */ |
2 | ||
3cf20931 | 3 | #include <linux/etherdevice.h> |
876c9d3a MT |
4 | |
5 | #include "assoc.h" | |
876c9d3a | 6 | #include "decl.h" |
876c9d3a | 7 | #include "host.h" |
245bf20f | 8 | #include "scan.h" |
2dd4b262 | 9 | #include "cmd.h" |
876c9d3a MT |
10 | |
11 | ||
5a6e0434 IH |
12 | static const u8 bssid_any[ETH_ALEN] __attribute__ ((aligned (2))) = |
13 | { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; | |
14 | static const u8 bssid_off[ETH_ALEN] __attribute__ ((aligned (2))) = | |
15 | { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; | |
876c9d3a | 16 | |
697900ac HS |
17 | /* The firmware needs certain bits masked out of the beacon-derviced capability |
18 | * field when associating/joining to BSSs. | |
19 | */ | |
20 | #define CAPINFO_MASK (~(0xda00)) | |
21 | ||
22 | ||
23 | ||
24 | /** | |
25 | * @brief Associate to a specific BSS discovered in a scan | |
26 | * | |
27 | * @param priv A pointer to struct lbs_private structure | |
28 | * @param pbssdesc Pointer to the BSS descriptor to associate with. | |
29 | * | |
30 | * @return 0-success, otherwise fail | |
31 | */ | |
32 | static int lbs_associate(struct lbs_private *priv, | |
33 | struct assoc_request *assoc_req) | |
34 | { | |
35 | int ret; | |
36 | ||
37 | lbs_deb_enter(LBS_DEB_ASSOC); | |
38 | ||
39 | ret = lbs_prepare_and_send_command(priv, CMD_802_11_AUTHENTICATE, | |
40 | 0, CMD_OPTION_WAITFORRSP, | |
41 | 0, assoc_req->bss.bssid); | |
42 | ||
43 | if (ret) | |
44 | goto done; | |
45 | ||
46 | /* set preamble to firmware */ | |
47 | if ((priv->capability & WLAN_CAPABILITY_SHORT_PREAMBLE) && | |
48 | (assoc_req->bss.capability & WLAN_CAPABILITY_SHORT_PREAMBLE)) | |
49 | priv->preamble = CMD_TYPE_SHORT_PREAMBLE; | |
50 | else | |
51 | priv->preamble = CMD_TYPE_LONG_PREAMBLE; | |
52 | ||
53 | lbs_set_radio_control(priv); | |
54 | ||
55 | ret = lbs_prepare_and_send_command(priv, CMD_802_11_ASSOCIATE, | |
56 | 0, CMD_OPTION_WAITFORRSP, 0, assoc_req); | |
57 | ||
58 | done: | |
59 | lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret); | |
60 | return ret; | |
61 | } | |
62 | ||
63 | /** | |
64 | * @brief Join an adhoc network found in a previous scan | |
65 | * | |
66 | * @param priv A pointer to struct lbs_private structure | |
67 | * @param pbssdesc Pointer to a BSS descriptor found in a previous scan | |
68 | * to attempt to join | |
69 | * | |
70 | * @return 0--success, -1--fail | |
71 | */ | |
72 | static int lbs_join_adhoc_network(struct lbs_private *priv, | |
73 | struct assoc_request *assoc_req) | |
74 | { | |
75 | struct bss_descriptor *bss = &assoc_req->bss; | |
76 | int ret = 0; | |
77 | ||
78 | lbs_deb_join("current SSID '%s', ssid length %u\n", | |
79 | escape_essid(priv->curbssparams.ssid, | |
80 | priv->curbssparams.ssid_len), | |
81 | priv->curbssparams.ssid_len); | |
82 | lbs_deb_join("requested ssid '%s', ssid length %u\n", | |
83 | escape_essid(bss->ssid, bss->ssid_len), | |
84 | bss->ssid_len); | |
85 | ||
86 | /* check if the requested SSID is already joined */ | |
87 | if (priv->curbssparams.ssid_len && | |
88 | !lbs_ssid_cmp(priv->curbssparams.ssid, | |
89 | priv->curbssparams.ssid_len, | |
90 | bss->ssid, bss->ssid_len) && | |
91 | (priv->mode == IW_MODE_ADHOC) && | |
92 | (priv->connect_status == LBS_CONNECTED)) { | |
93 | union iwreq_data wrqu; | |
94 | ||
95 | lbs_deb_join("ADHOC_J_CMD: New ad-hoc SSID is the same as " | |
96 | "current, not attempting to re-join"); | |
97 | ||
98 | /* Send the re-association event though, because the association | |
99 | * request really was successful, even if just a null-op. | |
100 | */ | |
101 | memset(&wrqu, 0, sizeof(wrqu)); | |
102 | memcpy(wrqu.ap_addr.sa_data, priv->curbssparams.bssid, | |
103 | ETH_ALEN); | |
104 | wrqu.ap_addr.sa_family = ARPHRD_ETHER; | |
105 | wireless_send_event(priv->dev, SIOCGIWAP, &wrqu, NULL); | |
106 | goto out; | |
107 | } | |
108 | ||
109 | /* Use shortpreamble only when both creator and card supports | |
110 | short preamble */ | |
111 | if (!(bss->capability & WLAN_CAPABILITY_SHORT_PREAMBLE) || | |
112 | !(priv->capability & WLAN_CAPABILITY_SHORT_PREAMBLE)) { | |
113 | lbs_deb_join("AdhocJoin: Long preamble\n"); | |
114 | priv->preamble = CMD_TYPE_LONG_PREAMBLE; | |
115 | } else { | |
116 | lbs_deb_join("AdhocJoin: Short preamble\n"); | |
117 | priv->preamble = CMD_TYPE_SHORT_PREAMBLE; | |
118 | } | |
119 | ||
120 | lbs_set_radio_control(priv); | |
121 | ||
122 | lbs_deb_join("AdhocJoin: channel = %d\n", assoc_req->channel); | |
123 | lbs_deb_join("AdhocJoin: band = %c\n", assoc_req->band); | |
124 | ||
125 | priv->adhoccreate = 0; | |
126 | ||
127 | ret = lbs_prepare_and_send_command(priv, CMD_802_11_AD_HOC_JOIN, | |
128 | 0, CMD_OPTION_WAITFORRSP, | |
129 | OID_802_11_SSID, assoc_req); | |
130 | ||
131 | out: | |
132 | return ret; | |
133 | } | |
134 | ||
135 | /** | |
136 | * @brief Start an Adhoc Network | |
137 | * | |
138 | * @param priv A pointer to struct lbs_private structure | |
139 | * @param adhocssid The ssid of the Adhoc Network | |
140 | * @return 0--success, -1--fail | |
141 | */ | |
142 | static int lbs_start_adhoc_network(struct lbs_private *priv, | |
143 | struct assoc_request *assoc_req) | |
144 | { | |
145 | int ret = 0; | |
146 | ||
147 | priv->adhoccreate = 1; | |
148 | ||
149 | if (priv->capability & WLAN_CAPABILITY_SHORT_PREAMBLE) { | |
150 | lbs_deb_join("AdhocStart: Short preamble\n"); | |
151 | priv->preamble = CMD_TYPE_SHORT_PREAMBLE; | |
152 | } else { | |
153 | lbs_deb_join("AdhocStart: Long preamble\n"); | |
154 | priv->preamble = CMD_TYPE_LONG_PREAMBLE; | |
155 | } | |
156 | ||
157 | lbs_set_radio_control(priv); | |
158 | ||
159 | lbs_deb_join("AdhocStart: channel = %d\n", assoc_req->channel); | |
160 | lbs_deb_join("AdhocStart: band = %d\n", assoc_req->band); | |
161 | ||
162 | ret = lbs_prepare_and_send_command(priv, CMD_802_11_AD_HOC_START, | |
163 | 0, CMD_OPTION_WAITFORRSP, 0, assoc_req); | |
164 | ||
165 | return ret; | |
166 | } | |
167 | ||
168 | int lbs_stop_adhoc_network(struct lbs_private *priv) | |
169 | { | |
170 | return lbs_prepare_and_send_command(priv, CMD_802_11_AD_HOC_STOP, | |
171 | 0, CMD_OPTION_WAITFORRSP, 0, NULL); | |
172 | } | |
e76850d6 | 173 | |
245bf20f HS |
174 | static inline int match_bss_no_security(struct lbs_802_11_security *secinfo, |
175 | struct bss_descriptor *match_bss) | |
176 | { | |
177 | if (!secinfo->wep_enabled && !secinfo->WPAenabled | |
178 | && !secinfo->WPA2enabled | |
179 | && match_bss->wpa_ie[0] != MFIE_TYPE_GENERIC | |
180 | && match_bss->rsn_ie[0] != MFIE_TYPE_RSN | |
181 | && !(match_bss->capability & WLAN_CAPABILITY_PRIVACY)) | |
182 | return 1; | |
183 | else | |
184 | return 0; | |
185 | } | |
186 | ||
187 | static inline int match_bss_static_wep(struct lbs_802_11_security *secinfo, | |
188 | struct bss_descriptor *match_bss) | |
189 | { | |
190 | if (secinfo->wep_enabled && !secinfo->WPAenabled | |
191 | && !secinfo->WPA2enabled | |
192 | && (match_bss->capability & WLAN_CAPABILITY_PRIVACY)) | |
193 | return 1; | |
194 | else | |
195 | return 0; | |
196 | } | |
197 | ||
198 | static inline int match_bss_wpa(struct lbs_802_11_security *secinfo, | |
199 | struct bss_descriptor *match_bss) | |
200 | { | |
201 | if (!secinfo->wep_enabled && secinfo->WPAenabled | |
202 | && (match_bss->wpa_ie[0] == MFIE_TYPE_GENERIC) | |
203 | /* privacy bit may NOT be set in some APs like LinkSys WRT54G | |
204 | && (match_bss->capability & WLAN_CAPABILITY_PRIVACY) */ | |
205 | ) | |
206 | return 1; | |
207 | else | |
208 | return 0; | |
209 | } | |
210 | ||
211 | static inline int match_bss_wpa2(struct lbs_802_11_security *secinfo, | |
212 | struct bss_descriptor *match_bss) | |
213 | { | |
214 | if (!secinfo->wep_enabled && secinfo->WPA2enabled && | |
215 | (match_bss->rsn_ie[0] == MFIE_TYPE_RSN) | |
216 | /* privacy bit may NOT be set in some APs like LinkSys WRT54G | |
217 | (match_bss->capability & WLAN_CAPABILITY_PRIVACY) */ | |
218 | ) | |
219 | return 1; | |
220 | else | |
221 | return 0; | |
222 | } | |
223 | ||
224 | static inline int match_bss_dynamic_wep(struct lbs_802_11_security *secinfo, | |
225 | struct bss_descriptor *match_bss) | |
226 | { | |
227 | if (!secinfo->wep_enabled && !secinfo->WPAenabled | |
228 | && !secinfo->WPA2enabled | |
229 | && (match_bss->wpa_ie[0] != MFIE_TYPE_GENERIC) | |
230 | && (match_bss->rsn_ie[0] != MFIE_TYPE_RSN) | |
231 | && (match_bss->capability & WLAN_CAPABILITY_PRIVACY)) | |
232 | return 1; | |
233 | else | |
234 | return 0; | |
235 | } | |
236 | ||
237 | /** | |
238 | * @brief Check if a scanned network compatible with the driver settings | |
239 | * | |
240 | * WEP WPA WPA2 ad-hoc encrypt Network | |
241 | * enabled enabled enabled AES mode privacy WPA WPA2 Compatible | |
242 | * 0 0 0 0 NONE 0 0 0 yes No security | |
243 | * 1 0 0 0 NONE 1 0 0 yes Static WEP | |
244 | * 0 1 0 0 x 1x 1 x yes WPA | |
245 | * 0 0 1 0 x 1x x 1 yes WPA2 | |
246 | * 0 0 0 1 NONE 1 0 0 yes Ad-hoc AES | |
247 | * 0 0 0 0 !=NONE 1 0 0 yes Dynamic WEP | |
248 | * | |
249 | * | |
250 | * @param priv A pointer to struct lbs_private | |
251 | * @param index Index in scantable to check against current driver settings | |
252 | * @param mode Network mode: Infrastructure or IBSS | |
253 | * | |
254 | * @return Index in scantable, or error code if negative | |
255 | */ | |
256 | static int is_network_compatible(struct lbs_private *priv, | |
257 | struct bss_descriptor *bss, uint8_t mode) | |
258 | { | |
259 | int matched = 0; | |
260 | ||
261 | lbs_deb_enter(LBS_DEB_SCAN); | |
262 | ||
263 | if (bss->mode != mode) | |
264 | goto done; | |
265 | ||
266 | matched = match_bss_no_security(&priv->secinfo, bss); | |
267 | if (matched) | |
268 | goto done; | |
269 | matched = match_bss_static_wep(&priv->secinfo, bss); | |
270 | if (matched) | |
271 | goto done; | |
272 | matched = match_bss_wpa(&priv->secinfo, bss); | |
273 | if (matched) { | |
274 | lbs_deb_scan("is_network_compatible() WPA: wpa_ie 0x%x " | |
275 | "wpa2_ie 0x%x WEP %s WPA %s WPA2 %s " | |
276 | "privacy 0x%x\n", bss->wpa_ie[0], bss->rsn_ie[0], | |
277 | priv->secinfo.wep_enabled ? "e" : "d", | |
278 | priv->secinfo.WPAenabled ? "e" : "d", | |
279 | priv->secinfo.WPA2enabled ? "e" : "d", | |
280 | (bss->capability & WLAN_CAPABILITY_PRIVACY)); | |
281 | goto done; | |
282 | } | |
283 | matched = match_bss_wpa2(&priv->secinfo, bss); | |
284 | if (matched) { | |
285 | lbs_deb_scan("is_network_compatible() WPA2: wpa_ie 0x%x " | |
286 | "wpa2_ie 0x%x WEP %s WPA %s WPA2 %s " | |
287 | "privacy 0x%x\n", bss->wpa_ie[0], bss->rsn_ie[0], | |
288 | priv->secinfo.wep_enabled ? "e" : "d", | |
289 | priv->secinfo.WPAenabled ? "e" : "d", | |
290 | priv->secinfo.WPA2enabled ? "e" : "d", | |
291 | (bss->capability & WLAN_CAPABILITY_PRIVACY)); | |
292 | goto done; | |
293 | } | |
294 | matched = match_bss_dynamic_wep(&priv->secinfo, bss); | |
295 | if (matched) { | |
296 | lbs_deb_scan("is_network_compatible() dynamic WEP: " | |
297 | "wpa_ie 0x%x wpa2_ie 0x%x privacy 0x%x\n", | |
298 | bss->wpa_ie[0], bss->rsn_ie[0], | |
299 | (bss->capability & WLAN_CAPABILITY_PRIVACY)); | |
300 | goto done; | |
301 | } | |
302 | ||
303 | /* bss security settings don't match those configured on card */ | |
304 | lbs_deb_scan("is_network_compatible() FAILED: wpa_ie 0x%x " | |
305 | "wpa2_ie 0x%x WEP %s WPA %s WPA2 %s privacy 0x%x\n", | |
306 | bss->wpa_ie[0], bss->rsn_ie[0], | |
307 | priv->secinfo.wep_enabled ? "e" : "d", | |
308 | priv->secinfo.WPAenabled ? "e" : "d", | |
309 | priv->secinfo.WPA2enabled ? "e" : "d", | |
310 | (bss->capability & WLAN_CAPABILITY_PRIVACY)); | |
311 | ||
312 | done: | |
313 | lbs_deb_leave_args(LBS_DEB_SCAN, "matched: %d", matched); | |
314 | return matched; | |
315 | } | |
316 | ||
317 | /** | |
318 | * @brief This function finds a specific compatible BSSID in the scan list | |
319 | * | |
320 | * Used in association code | |
321 | * | |
322 | * @param priv A pointer to struct lbs_private | |
323 | * @param bssid BSSID to find in the scan list | |
324 | * @param mode Network mode: Infrastructure or IBSS | |
325 | * | |
326 | * @return index in BSSID list, or error return code (< 0) | |
327 | */ | |
328 | static struct bss_descriptor *lbs_find_bssid_in_list(struct lbs_private *priv, | |
329 | uint8_t *bssid, uint8_t mode) | |
330 | { | |
331 | struct bss_descriptor *iter_bss; | |
332 | struct bss_descriptor *found_bss = NULL; | |
333 | ||
334 | lbs_deb_enter(LBS_DEB_SCAN); | |
335 | ||
336 | if (!bssid) | |
337 | goto out; | |
338 | ||
339 | lbs_deb_hex(LBS_DEB_SCAN, "looking for", bssid, ETH_ALEN); | |
340 | ||
341 | /* Look through the scan table for a compatible match. The loop will | |
342 | * continue past a matched bssid that is not compatible in case there | |
343 | * is an AP with multiple SSIDs assigned to the same BSSID | |
344 | */ | |
345 | mutex_lock(&priv->lock); | |
346 | list_for_each_entry(iter_bss, &priv->network_list, list) { | |
347 | if (compare_ether_addr(iter_bss->bssid, bssid)) | |
348 | continue; /* bssid doesn't match */ | |
349 | switch (mode) { | |
350 | case IW_MODE_INFRA: | |
351 | case IW_MODE_ADHOC: | |
352 | if (!is_network_compatible(priv, iter_bss, mode)) | |
353 | break; | |
354 | found_bss = iter_bss; | |
355 | break; | |
356 | default: | |
357 | found_bss = iter_bss; | |
358 | break; | |
359 | } | |
360 | } | |
361 | mutex_unlock(&priv->lock); | |
362 | ||
363 | out: | |
364 | lbs_deb_leave_args(LBS_DEB_SCAN, "found_bss %p", found_bss); | |
365 | return found_bss; | |
366 | } | |
367 | ||
368 | /** | |
369 | * @brief This function finds ssid in ssid list. | |
370 | * | |
371 | * Used in association code | |
372 | * | |
373 | * @param priv A pointer to struct lbs_private | |
374 | * @param ssid SSID to find in the list | |
375 | * @param bssid BSSID to qualify the SSID selection (if provided) | |
376 | * @param mode Network mode: Infrastructure or IBSS | |
377 | * | |
378 | * @return index in BSSID list | |
379 | */ | |
380 | static struct bss_descriptor *lbs_find_ssid_in_list(struct lbs_private *priv, | |
381 | uint8_t *ssid, uint8_t ssid_len, | |
382 | uint8_t *bssid, uint8_t mode, | |
383 | int channel) | |
384 | { | |
385 | u32 bestrssi = 0; | |
386 | struct bss_descriptor *iter_bss = NULL; | |
387 | struct bss_descriptor *found_bss = NULL; | |
388 | struct bss_descriptor *tmp_oldest = NULL; | |
389 | ||
390 | lbs_deb_enter(LBS_DEB_SCAN); | |
391 | ||
392 | mutex_lock(&priv->lock); | |
393 | ||
394 | list_for_each_entry(iter_bss, &priv->network_list, list) { | |
395 | if (!tmp_oldest || | |
396 | (iter_bss->last_scanned < tmp_oldest->last_scanned)) | |
397 | tmp_oldest = iter_bss; | |
398 | ||
399 | if (lbs_ssid_cmp(iter_bss->ssid, iter_bss->ssid_len, | |
400 | ssid, ssid_len) != 0) | |
401 | continue; /* ssid doesn't match */ | |
402 | if (bssid && compare_ether_addr(iter_bss->bssid, bssid) != 0) | |
403 | continue; /* bssid doesn't match */ | |
404 | if ((channel > 0) && (iter_bss->channel != channel)) | |
405 | continue; /* channel doesn't match */ | |
406 | ||
407 | switch (mode) { | |
408 | case IW_MODE_INFRA: | |
409 | case IW_MODE_ADHOC: | |
410 | if (!is_network_compatible(priv, iter_bss, mode)) | |
411 | break; | |
412 | ||
413 | if (bssid) { | |
414 | /* Found requested BSSID */ | |
415 | found_bss = iter_bss; | |
416 | goto out; | |
417 | } | |
418 | ||
419 | if (SCAN_RSSI(iter_bss->rssi) > bestrssi) { | |
420 | bestrssi = SCAN_RSSI(iter_bss->rssi); | |
421 | found_bss = iter_bss; | |
422 | } | |
423 | break; | |
424 | case IW_MODE_AUTO: | |
425 | default: | |
426 | if (SCAN_RSSI(iter_bss->rssi) > bestrssi) { | |
427 | bestrssi = SCAN_RSSI(iter_bss->rssi); | |
428 | found_bss = iter_bss; | |
429 | } | |
430 | break; | |
431 | } | |
432 | } | |
433 | ||
434 | out: | |
435 | mutex_unlock(&priv->lock); | |
436 | lbs_deb_leave_args(LBS_DEB_SCAN, "found_bss %p", found_bss); | |
437 | return found_bss; | |
438 | } | |
439 | ||
69f9032d | 440 | static int assoc_helper_essid(struct lbs_private *priv, |
876c9d3a MT |
441 | struct assoc_request * assoc_req) |
442 | { | |
876c9d3a | 443 | int ret = 0; |
fcdb53db | 444 | struct bss_descriptor * bss; |
aeea0ab4 | 445 | int channel = -1; |
876c9d3a | 446 | |
9012b28a | 447 | lbs_deb_enter(LBS_DEB_ASSOC); |
876c9d3a | 448 | |
ef9a264b DW |
449 | /* FIXME: take channel into account when picking SSIDs if a channel |
450 | * is set. | |
451 | */ | |
452 | ||
aeea0ab4 DW |
453 | if (test_bit(ASSOC_FLAG_CHANNEL, &assoc_req->flags)) |
454 | channel = assoc_req->channel; | |
455 | ||
0765af44 | 456 | lbs_deb_assoc("SSID '%s' requested\n", |
d8efea25 | 457 | escape_essid(assoc_req->ssid, assoc_req->ssid_len)); |
0dc5a290 | 458 | if (assoc_req->mode == IW_MODE_INFRA) { |
10078321 | 459 | lbs_send_specific_ssid_scan(priv, assoc_req->ssid, |
52933d81 | 460 | assoc_req->ssid_len); |
876c9d3a | 461 | |
aa21c004 | 462 | bss = lbs_find_ssid_in_list(priv, assoc_req->ssid, |
d8efea25 | 463 | assoc_req->ssid_len, NULL, IW_MODE_INFRA, channel); |
fcdb53db | 464 | if (bss != NULL) { |
e76850d6 | 465 | memcpy(&assoc_req->bss, bss, sizeof(struct bss_descriptor)); |
10078321 | 466 | ret = lbs_associate(priv, assoc_req); |
876c9d3a | 467 | } else { |
d8efea25 | 468 | lbs_deb_assoc("SSID not found; cannot associate\n"); |
876c9d3a | 469 | } |
0dc5a290 | 470 | } else if (assoc_req->mode == IW_MODE_ADHOC) { |
876c9d3a MT |
471 | /* Scan for the network, do not save previous results. Stale |
472 | * scan data will cause us to join a non-existant adhoc network | |
473 | */ | |
10078321 | 474 | lbs_send_specific_ssid_scan(priv, assoc_req->ssid, |
52933d81 | 475 | assoc_req->ssid_len); |
876c9d3a MT |
476 | |
477 | /* Search for the requested SSID in the scan table */ | |
aa21c004 | 478 | bss = lbs_find_ssid_in_list(priv, assoc_req->ssid, |
d8efea25 | 479 | assoc_req->ssid_len, NULL, IW_MODE_ADHOC, channel); |
fcdb53db | 480 | if (bss != NULL) { |
d8efea25 | 481 | lbs_deb_assoc("SSID found, will join\n"); |
e76850d6 | 482 | memcpy(&assoc_req->bss, bss, sizeof(struct bss_descriptor)); |
10078321 | 483 | lbs_join_adhoc_network(priv, assoc_req); |
876c9d3a MT |
484 | } else { |
485 | /* else send START command */ | |
d8efea25 | 486 | lbs_deb_assoc("SSID not found, creating adhoc network\n"); |
e76850d6 | 487 | memcpy(&assoc_req->bss.ssid, &assoc_req->ssid, |
d8efea25 DW |
488 | IW_ESSID_MAX_SIZE); |
489 | assoc_req->bss.ssid_len = assoc_req->ssid_len; | |
10078321 | 490 | lbs_start_adhoc_network(priv, assoc_req); |
876c9d3a | 491 | } |
876c9d3a MT |
492 | } |
493 | ||
9012b28a | 494 | lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret); |
876c9d3a MT |
495 | return ret; |
496 | } | |
497 | ||
498 | ||
69f9032d | 499 | static int assoc_helper_bssid(struct lbs_private *priv, |
876c9d3a MT |
500 | struct assoc_request * assoc_req) |
501 | { | |
fcdb53db DW |
502 | int ret = 0; |
503 | struct bss_descriptor * bss; | |
0795af57 | 504 | DECLARE_MAC_BUF(mac); |
876c9d3a | 505 | |
0795af57 JP |
506 | lbs_deb_enter_args(LBS_DEB_ASSOC, "BSSID %s", |
507 | print_mac(mac, assoc_req->bssid)); | |
876c9d3a MT |
508 | |
509 | /* Search for index position in list for requested MAC */ | |
aa21c004 | 510 | bss = lbs_find_bssid_in_list(priv, assoc_req->bssid, |
876c9d3a | 511 | assoc_req->mode); |
fcdb53db | 512 | if (bss == NULL) { |
0795af57 JP |
513 | lbs_deb_assoc("ASSOC: WAP: BSSID %s not found, " |
514 | "cannot associate.\n", print_mac(mac, assoc_req->bssid)); | |
876c9d3a MT |
515 | goto out; |
516 | } | |
517 | ||
e76850d6 | 518 | memcpy(&assoc_req->bss, bss, sizeof(struct bss_descriptor)); |
0dc5a290 | 519 | if (assoc_req->mode == IW_MODE_INFRA) { |
10078321 HS |
520 | ret = lbs_associate(priv, assoc_req); |
521 | lbs_deb_assoc("ASSOC: lbs_associate(bssid) returned %d\n", ret); | |
0dc5a290 | 522 | } else if (assoc_req->mode == IW_MODE_ADHOC) { |
10078321 | 523 | lbs_join_adhoc_network(priv, assoc_req); |
876c9d3a | 524 | } |
876c9d3a MT |
525 | |
526 | out: | |
9012b28a | 527 | lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret); |
876c9d3a MT |
528 | return ret; |
529 | } | |
530 | ||
531 | ||
69f9032d | 532 | static int assoc_helper_associate(struct lbs_private *priv, |
876c9d3a MT |
533 | struct assoc_request * assoc_req) |
534 | { | |
535 | int ret = 0, done = 0; | |
536 | ||
0765af44 HS |
537 | lbs_deb_enter(LBS_DEB_ASSOC); |
538 | ||
876c9d3a MT |
539 | /* If we're given and 'any' BSSID, try associating based on SSID */ |
540 | ||
541 | if (test_bit(ASSOC_FLAG_BSSID, &assoc_req->flags)) { | |
3cf20931 DW |
542 | if (compare_ether_addr(bssid_any, assoc_req->bssid) |
543 | && compare_ether_addr(bssid_off, assoc_req->bssid)) { | |
876c9d3a MT |
544 | ret = assoc_helper_bssid(priv, assoc_req); |
545 | done = 1; | |
876c9d3a MT |
546 | } |
547 | } | |
548 | ||
549 | if (!done && test_bit(ASSOC_FLAG_SSID, &assoc_req->flags)) { | |
550 | ret = assoc_helper_essid(priv, assoc_req); | |
876c9d3a MT |
551 | } |
552 | ||
0765af44 | 553 | lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret); |
876c9d3a MT |
554 | return ret; |
555 | } | |
556 | ||
557 | ||
69f9032d | 558 | static int assoc_helper_mode(struct lbs_private *priv, |
876c9d3a MT |
559 | struct assoc_request * assoc_req) |
560 | { | |
876c9d3a MT |
561 | int ret = 0; |
562 | ||
9012b28a | 563 | lbs_deb_enter(LBS_DEB_ASSOC); |
876c9d3a | 564 | |
aa21c004 | 565 | if (assoc_req->mode == priv->mode) |
9012b28a | 566 | goto done; |
876c9d3a | 567 | |
0dc5a290 | 568 | if (assoc_req->mode == IW_MODE_INFRA) { |
aa21c004 | 569 | if (priv->psstate != PS_STATE_FULL_POWER) |
10078321 | 570 | lbs_ps_wakeup(priv, CMD_OPTION_WAITFORRSP); |
aa21c004 | 571 | priv->psmode = LBS802_11POWERMODECAM; |
876c9d3a MT |
572 | } |
573 | ||
aa21c004 | 574 | priv->mode = assoc_req->mode; |
10078321 | 575 | ret = lbs_prepare_and_send_command(priv, |
0aef64d7 DW |
576 | CMD_802_11_SNMP_MIB, |
577 | 0, CMD_OPTION_WAITFORRSP, | |
876c9d3a | 578 | OID_802_11_INFRASTRUCTURE_MODE, |
981f187b | 579 | /* Shoot me now */ (void *) (size_t) assoc_req->mode); |
876c9d3a | 580 | |
9012b28a HS |
581 | done: |
582 | lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret); | |
876c9d3a MT |
583 | return ret; |
584 | } | |
585 | ||
69f9032d | 586 | static int assoc_helper_channel(struct lbs_private *priv, |
ef9a264b DW |
587 | struct assoc_request * assoc_req) |
588 | { | |
ef9a264b DW |
589 | int ret = 0; |
590 | ||
591 | lbs_deb_enter(LBS_DEB_ASSOC); | |
592 | ||
9f462577 | 593 | ret = lbs_update_channel(priv); |
d1a469fd | 594 | if (ret) { |
23d36eec | 595 | lbs_deb_assoc("ASSOC: channel: error getting channel.\n"); |
d1a469fd | 596 | goto done; |
ef9a264b DW |
597 | } |
598 | ||
aa21c004 | 599 | if (assoc_req->channel == priv->curbssparams.channel) |
ef9a264b DW |
600 | goto done; |
601 | ||
8642f1f0 | 602 | if (priv->mesh_dev) { |
86062134 DW |
603 | /* Change mesh channel first; 21.p21 firmware won't let |
604 | you change channel otherwise (even though it'll return | |
605 | an error to this */ | |
edaea5ce JC |
606 | lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_STOP, |
607 | assoc_req->channel); | |
8642f1f0 DW |
608 | } |
609 | ||
ef9a264b | 610 | lbs_deb_assoc("ASSOC: channel: %d -> %d\n", |
86062134 | 611 | priv->curbssparams.channel, assoc_req->channel); |
ef9a264b | 612 | |
2dd4b262 DW |
613 | ret = lbs_set_channel(priv, assoc_req->channel); |
614 | if (ret < 0) | |
23d36eec | 615 | lbs_deb_assoc("ASSOC: channel: error setting channel.\n"); |
ef9a264b | 616 | |
2dd4b262 DW |
617 | /* FIXME: shouldn't need to grab the channel _again_ after setting |
618 | * it since the firmware is supposed to return the new channel, but | |
619 | * whatever... */ | |
9f462577 | 620 | ret = lbs_update_channel(priv); |
d1a469fd | 621 | if (ret) { |
23d36eec | 622 | lbs_deb_assoc("ASSOC: channel: error getting channel.\n"); |
d1a469fd DW |
623 | goto done; |
624 | } | |
ef9a264b | 625 | |
aa21c004 | 626 | if (assoc_req->channel != priv->curbssparams.channel) { |
88ae2915 | 627 | lbs_deb_assoc("ASSOC: channel: failed to update channel to %d\n", |
ef9a264b | 628 | assoc_req->channel); |
8642f1f0 | 629 | goto restore_mesh; |
ef9a264b DW |
630 | } |
631 | ||
632 | if ( assoc_req->secinfo.wep_enabled | |
633 | && (assoc_req->wep_keys[0].len | |
634 | || assoc_req->wep_keys[1].len | |
635 | || assoc_req->wep_keys[2].len | |
636 | || assoc_req->wep_keys[3].len)) { | |
637 | /* Make sure WEP keys are re-sent to firmware */ | |
638 | set_bit(ASSOC_FLAG_WEP_KEYS, &assoc_req->flags); | |
639 | } | |
640 | ||
641 | /* Must restart/rejoin adhoc networks after channel change */ | |
23d36eec | 642 | set_bit(ASSOC_FLAG_SSID, &assoc_req->flags); |
ef9a264b | 643 | |
8642f1f0 DW |
644 | restore_mesh: |
645 | if (priv->mesh_dev) | |
edaea5ce JC |
646 | lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_START, |
647 | priv->curbssparams.channel); | |
8642f1f0 DW |
648 | |
649 | done: | |
ef9a264b DW |
650 | lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret); |
651 | return ret; | |
652 | } | |
653 | ||
654 | ||
69f9032d | 655 | static int assoc_helper_wep_keys(struct lbs_private *priv, |
f70dd451 | 656 | struct assoc_request *assoc_req) |
876c9d3a | 657 | { |
876c9d3a MT |
658 | int i; |
659 | int ret = 0; | |
660 | ||
9012b28a | 661 | lbs_deb_enter(LBS_DEB_ASSOC); |
876c9d3a MT |
662 | |
663 | /* Set or remove WEP keys */ | |
f70dd451 DW |
664 | if (assoc_req->wep_keys[0].len || assoc_req->wep_keys[1].len || |
665 | assoc_req->wep_keys[2].len || assoc_req->wep_keys[3].len) | |
666 | ret = lbs_cmd_802_11_set_wep(priv, CMD_ACT_ADD, assoc_req); | |
667 | else | |
668 | ret = lbs_cmd_802_11_set_wep(priv, CMD_ACT_REMOVE, assoc_req); | |
876c9d3a MT |
669 | |
670 | if (ret) | |
671 | goto out; | |
672 | ||
673 | /* enable/disable the MAC's WEP packet filter */ | |
889c05bd | 674 | if (assoc_req->secinfo.wep_enabled) |
d9e9778c | 675 | priv->mac_control |= CMD_ACT_MAC_WEP_ENABLE; |
876c9d3a | 676 | else |
d9e9778c | 677 | priv->mac_control &= ~CMD_ACT_MAC_WEP_ENABLE; |
f70dd451 | 678 | |
c97329e2 | 679 | lbs_set_mac_control(priv); |
876c9d3a | 680 | |
aa21c004 | 681 | mutex_lock(&priv->lock); |
876c9d3a | 682 | |
aa21c004 | 683 | /* Copy WEP keys into priv wep key fields */ |
876c9d3a | 684 | for (i = 0; i < 4; i++) { |
aa21c004 | 685 | memcpy(&priv->wep_keys[i], &assoc_req->wep_keys[i], |
f70dd451 | 686 | sizeof(struct enc_key)); |
876c9d3a | 687 | } |
aa21c004 | 688 | priv->wep_tx_keyidx = assoc_req->wep_tx_keyidx; |
876c9d3a | 689 | |
aa21c004 | 690 | mutex_unlock(&priv->lock); |
876c9d3a MT |
691 | |
692 | out: | |
9012b28a | 693 | lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret); |
876c9d3a MT |
694 | return ret; |
695 | } | |
696 | ||
69f9032d | 697 | static int assoc_helper_secinfo(struct lbs_private *priv, |
876c9d3a MT |
698 | struct assoc_request * assoc_req) |
699 | { | |
876c9d3a | 700 | int ret = 0; |
4f59abf1 DW |
701 | uint16_t do_wpa; |
702 | uint16_t rsn = 0; | |
876c9d3a | 703 | |
9012b28a | 704 | lbs_deb_enter(LBS_DEB_ASSOC); |
876c9d3a | 705 | |
aa21c004 | 706 | memcpy(&priv->secinfo, &assoc_req->secinfo, |
10078321 | 707 | sizeof(struct lbs_802_11_security)); |
876c9d3a | 708 | |
c97329e2 | 709 | lbs_set_mac_control(priv); |
876c9d3a | 710 | |
18c96c34 DW |
711 | /* If RSN is already enabled, don't try to enable it again, since |
712 | * ENABLE_RSN resets internal state machines and will clobber the | |
713 | * 4-way WPA handshake. | |
714 | */ | |
715 | ||
716 | /* Get RSN enabled/disabled */ | |
4f59abf1 | 717 | ret = lbs_cmd_802_11_enable_rsn(priv, CMD_ACT_GET, &rsn); |
18c96c34 | 718 | if (ret) { |
23d36eec | 719 | lbs_deb_assoc("Failed to get RSN status: %d\n", ret); |
18c96c34 DW |
720 | goto out; |
721 | } | |
722 | ||
723 | /* Don't re-enable RSN if it's already enabled */ | |
4f59abf1 | 724 | do_wpa = assoc_req->secinfo.WPAenabled || assoc_req->secinfo.WPA2enabled; |
18c96c34 DW |
725 | if (do_wpa == rsn) |
726 | goto out; | |
727 | ||
728 | /* Set RSN enabled/disabled */ | |
4f59abf1 | 729 | ret = lbs_cmd_802_11_enable_rsn(priv, CMD_ACT_SET, &do_wpa); |
90a42210 DW |
730 | |
731 | out: | |
9012b28a | 732 | lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret); |
876c9d3a MT |
733 | return ret; |
734 | } | |
735 | ||
736 | ||
69f9032d | 737 | static int assoc_helper_wpa_keys(struct lbs_private *priv, |
876c9d3a MT |
738 | struct assoc_request * assoc_req) |
739 | { | |
740 | int ret = 0; | |
2bcde51d | 741 | unsigned int flags = assoc_req->flags; |
876c9d3a | 742 | |
9012b28a | 743 | lbs_deb_enter(LBS_DEB_ASSOC); |
876c9d3a | 744 | |
2bcde51d DW |
745 | /* Work around older firmware bug where WPA unicast and multicast |
746 | * keys must be set independently. Seen in SDIO parts with firmware | |
747 | * version 5.0.11p0. | |
748 | */ | |
876c9d3a | 749 | |
2bcde51d DW |
750 | if (test_bit(ASSOC_FLAG_WPA_UCAST_KEY, &assoc_req->flags)) { |
751 | clear_bit(ASSOC_FLAG_WPA_MCAST_KEY, &assoc_req->flags); | |
9e1228d0 | 752 | ret = lbs_cmd_802_11_key_material(priv, CMD_ACT_SET, assoc_req); |
2bcde51d DW |
753 | assoc_req->flags = flags; |
754 | } | |
755 | ||
756 | if (ret) | |
757 | goto out; | |
758 | ||
759 | if (test_bit(ASSOC_FLAG_WPA_MCAST_KEY, &assoc_req->flags)) { | |
760 | clear_bit(ASSOC_FLAG_WPA_UCAST_KEY, &assoc_req->flags); | |
761 | ||
9e1228d0 | 762 | ret = lbs_cmd_802_11_key_material(priv, CMD_ACT_SET, assoc_req); |
2bcde51d DW |
763 | assoc_req->flags = flags; |
764 | } | |
765 | ||
766 | out: | |
9012b28a | 767 | lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret); |
876c9d3a MT |
768 | return ret; |
769 | } | |
770 | ||
771 | ||
69f9032d | 772 | static int assoc_helper_wpa_ie(struct lbs_private *priv, |
876c9d3a MT |
773 | struct assoc_request * assoc_req) |
774 | { | |
876c9d3a MT |
775 | int ret = 0; |
776 | ||
9012b28a | 777 | lbs_deb_enter(LBS_DEB_ASSOC); |
876c9d3a MT |
778 | |
779 | if (assoc_req->secinfo.WPAenabled || assoc_req->secinfo.WPA2enabled) { | |
aa21c004 DW |
780 | memcpy(&priv->wpa_ie, &assoc_req->wpa_ie, assoc_req->wpa_ie_len); |
781 | priv->wpa_ie_len = assoc_req->wpa_ie_len; | |
876c9d3a | 782 | } else { |
aa21c004 DW |
783 | memset(&priv->wpa_ie, 0, MAX_WPA_IE_LEN); |
784 | priv->wpa_ie_len = 0; | |
876c9d3a MT |
785 | } |
786 | ||
9012b28a | 787 | lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret); |
876c9d3a MT |
788 | return ret; |
789 | } | |
790 | ||
791 | ||
aa21c004 | 792 | static int should_deauth_infrastructure(struct lbs_private *priv, |
876c9d3a MT |
793 | struct assoc_request * assoc_req) |
794 | { | |
0765af44 HS |
795 | int ret = 0; |
796 | ||
aa21c004 | 797 | if (priv->connect_status != LBS_CONNECTED) |
876c9d3a MT |
798 | return 0; |
799 | ||
52507c20 | 800 | lbs_deb_enter(LBS_DEB_ASSOC); |
876c9d3a | 801 | if (test_bit(ASSOC_FLAG_SSID, &assoc_req->flags)) { |
0765af44 HS |
802 | lbs_deb_assoc("Deauthenticating due to new SSID\n"); |
803 | ret = 1; | |
804 | goto out; | |
876c9d3a MT |
805 | } |
806 | ||
807 | if (test_bit(ASSOC_FLAG_SECINFO, &assoc_req->flags)) { | |
aa21c004 | 808 | if (priv->secinfo.auth_mode != assoc_req->secinfo.auth_mode) { |
0765af44 HS |
809 | lbs_deb_assoc("Deauthenticating due to new security\n"); |
810 | ret = 1; | |
811 | goto out; | |
876c9d3a MT |
812 | } |
813 | } | |
814 | ||
815 | if (test_bit(ASSOC_FLAG_BSSID, &assoc_req->flags)) { | |
0765af44 HS |
816 | lbs_deb_assoc("Deauthenticating due to new BSSID\n"); |
817 | ret = 1; | |
818 | goto out; | |
876c9d3a MT |
819 | } |
820 | ||
fff47f10 | 821 | if (test_bit(ASSOC_FLAG_CHANNEL, &assoc_req->flags)) { |
0765af44 HS |
822 | lbs_deb_assoc("Deauthenticating due to channel switch\n"); |
823 | ret = 1; | |
824 | goto out; | |
fff47f10 LCCR |
825 | } |
826 | ||
876c9d3a MT |
827 | /* FIXME: deal with 'auto' mode somehow */ |
828 | if (test_bit(ASSOC_FLAG_MODE, &assoc_req->flags)) { | |
0765af44 HS |
829 | if (assoc_req->mode != IW_MODE_INFRA) { |
830 | lbs_deb_assoc("Deauthenticating due to leaving " | |
831 | "infra mode\n"); | |
832 | ret = 1; | |
833 | goto out; | |
834 | } | |
876c9d3a MT |
835 | } |
836 | ||
0765af44 HS |
837 | out: |
838 | lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret); | |
52507c20 | 839 | return ret; |
876c9d3a MT |
840 | } |
841 | ||
842 | ||
aa21c004 | 843 | static int should_stop_adhoc(struct lbs_private *priv, |
876c9d3a MT |
844 | struct assoc_request * assoc_req) |
845 | { | |
0765af44 HS |
846 | lbs_deb_enter(LBS_DEB_ASSOC); |
847 | ||
aa21c004 | 848 | if (priv->connect_status != LBS_CONNECTED) |
876c9d3a MT |
849 | return 0; |
850 | ||
aa21c004 DW |
851 | if (lbs_ssid_cmp(priv->curbssparams.ssid, |
852 | priv->curbssparams.ssid_len, | |
d8efea25 | 853 | assoc_req->ssid, assoc_req->ssid_len) != 0) |
876c9d3a MT |
854 | return 1; |
855 | ||
856 | /* FIXME: deal with 'auto' mode somehow */ | |
857 | if (test_bit(ASSOC_FLAG_MODE, &assoc_req->flags)) { | |
0dc5a290 | 858 | if (assoc_req->mode != IW_MODE_ADHOC) |
876c9d3a MT |
859 | return 1; |
860 | } | |
861 | ||
ef9a264b | 862 | if (test_bit(ASSOC_FLAG_CHANNEL, &assoc_req->flags)) { |
aa21c004 | 863 | if (assoc_req->channel != priv->curbssparams.channel) |
ef9a264b DW |
864 | return 1; |
865 | } | |
866 | ||
0765af44 | 867 | lbs_deb_leave(LBS_DEB_ASSOC); |
876c9d3a MT |
868 | return 0; |
869 | } | |
870 | ||
871 | ||
245bf20f HS |
872 | /** |
873 | * @brief This function finds the best SSID in the Scan List | |
874 | * | |
875 | * Search the scan table for the best SSID that also matches the current | |
876 | * adapter network preference (infrastructure or adhoc) | |
877 | * | |
878 | * @param priv A pointer to struct lbs_private | |
879 | * | |
880 | * @return index in BSSID list | |
881 | */ | |
882 | static struct bss_descriptor *lbs_find_best_ssid_in_list( | |
883 | struct lbs_private *priv, uint8_t mode) | |
884 | { | |
885 | uint8_t bestrssi = 0; | |
886 | struct bss_descriptor *iter_bss; | |
887 | struct bss_descriptor *best_bss = NULL; | |
888 | ||
889 | lbs_deb_enter(LBS_DEB_SCAN); | |
890 | ||
891 | mutex_lock(&priv->lock); | |
892 | ||
893 | list_for_each_entry(iter_bss, &priv->network_list, list) { | |
894 | switch (mode) { | |
895 | case IW_MODE_INFRA: | |
896 | case IW_MODE_ADHOC: | |
897 | if (!is_network_compatible(priv, iter_bss, mode)) | |
898 | break; | |
899 | if (SCAN_RSSI(iter_bss->rssi) <= bestrssi) | |
900 | break; | |
901 | bestrssi = SCAN_RSSI(iter_bss->rssi); | |
902 | best_bss = iter_bss; | |
903 | break; | |
904 | case IW_MODE_AUTO: | |
905 | default: | |
906 | if (SCAN_RSSI(iter_bss->rssi) <= bestrssi) | |
907 | break; | |
908 | bestrssi = SCAN_RSSI(iter_bss->rssi); | |
909 | best_bss = iter_bss; | |
910 | break; | |
911 | } | |
912 | } | |
913 | ||
914 | mutex_unlock(&priv->lock); | |
915 | lbs_deb_leave_args(LBS_DEB_SCAN, "best_bss %p", best_bss); | |
916 | return best_bss; | |
917 | } | |
918 | ||
919 | /** | |
920 | * @brief Find the best AP | |
921 | * | |
922 | * Used from association worker. | |
923 | * | |
924 | * @param priv A pointer to struct lbs_private structure | |
925 | * @param pSSID A pointer to AP's ssid | |
926 | * | |
927 | * @return 0--success, otherwise--fail | |
928 | */ | |
929 | static int lbs_find_best_network_ssid(struct lbs_private *priv, | |
930 | uint8_t *out_ssid, uint8_t *out_ssid_len, uint8_t preferred_mode, | |
931 | uint8_t *out_mode) | |
932 | { | |
933 | int ret = -1; | |
934 | struct bss_descriptor *found; | |
935 | ||
936 | lbs_deb_enter(LBS_DEB_SCAN); | |
937 | ||
938 | priv->scan_ssid_len = 0; | |
939 | lbs_scan_networks(priv, 1); | |
940 | if (priv->surpriseremoved) | |
941 | goto out; | |
942 | ||
943 | found = lbs_find_best_ssid_in_list(priv, preferred_mode); | |
944 | if (found && (found->ssid_len > 0)) { | |
945 | memcpy(out_ssid, &found->ssid, IW_ESSID_MAX_SIZE); | |
946 | *out_ssid_len = found->ssid_len; | |
947 | *out_mode = found->mode; | |
948 | ret = 0; | |
949 | } | |
950 | ||
951 | out: | |
952 | lbs_deb_leave_args(LBS_DEB_SCAN, "ret %d", ret); | |
953 | return ret; | |
954 | } | |
955 | ||
956 | ||
10078321 | 957 | void lbs_association_worker(struct work_struct *work) |
876c9d3a | 958 | { |
69f9032d HS |
959 | struct lbs_private *priv = container_of(work, struct lbs_private, |
960 | assoc_work.work); | |
876c9d3a MT |
961 | struct assoc_request * assoc_req = NULL; |
962 | int ret = 0; | |
963 | int find_any_ssid = 0; | |
0795af57 | 964 | DECLARE_MAC_BUF(mac); |
876c9d3a | 965 | |
9012b28a | 966 | lbs_deb_enter(LBS_DEB_ASSOC); |
876c9d3a | 967 | |
aa21c004 DW |
968 | mutex_lock(&priv->lock); |
969 | assoc_req = priv->pending_assoc_req; | |
970 | priv->pending_assoc_req = NULL; | |
971 | priv->in_progress_assoc_req = assoc_req; | |
972 | mutex_unlock(&priv->lock); | |
876c9d3a | 973 | |
9012b28a HS |
974 | if (!assoc_req) |
975 | goto done; | |
876c9d3a | 976 | |
0765af44 HS |
977 | lbs_deb_assoc( |
978 | "Association Request:\n" | |
979 | " flags: 0x%08lx\n" | |
980 | " SSID: '%s'\n" | |
981 | " chann: %d\n" | |
982 | " band: %d\n" | |
983 | " mode: %d\n" | |
984 | " BSSID: %s\n" | |
985 | " secinfo: %s%s%s\n" | |
986 | " auth_mode: %d\n", | |
987 | assoc_req->flags, | |
988 | escape_essid(assoc_req->ssid, assoc_req->ssid_len), | |
989 | assoc_req->channel, assoc_req->band, assoc_req->mode, | |
990 | print_mac(mac, assoc_req->bssid), | |
991 | assoc_req->secinfo.WPAenabled ? " WPA" : "", | |
992 | assoc_req->secinfo.WPA2enabled ? " WPA2" : "", | |
993 | assoc_req->secinfo.wep_enabled ? " WEP" : "", | |
994 | assoc_req->secinfo.auth_mode); | |
876c9d3a MT |
995 | |
996 | /* If 'any' SSID was specified, find an SSID to associate with */ | |
997 | if (test_bit(ASSOC_FLAG_SSID, &assoc_req->flags) | |
d8efea25 | 998 | && !assoc_req->ssid_len) |
876c9d3a MT |
999 | find_any_ssid = 1; |
1000 | ||
1001 | /* But don't use 'any' SSID if there's a valid locked BSSID to use */ | |
1002 | if (test_bit(ASSOC_FLAG_BSSID, &assoc_req->flags)) { | |
3cf20931 DW |
1003 | if (compare_ether_addr(assoc_req->bssid, bssid_any) |
1004 | && compare_ether_addr(assoc_req->bssid, bssid_off)) | |
876c9d3a MT |
1005 | find_any_ssid = 0; |
1006 | } | |
1007 | ||
1008 | if (find_any_ssid) { | |
877cb0d4 | 1009 | u8 new_mode = assoc_req->mode; |
876c9d3a | 1010 | |
10078321 | 1011 | ret = lbs_find_best_network_ssid(priv, assoc_req->ssid, |
d8efea25 | 1012 | &assoc_req->ssid_len, assoc_req->mode, &new_mode); |
876c9d3a | 1013 | if (ret) { |
9012b28a | 1014 | lbs_deb_assoc("Could not find best network\n"); |
876c9d3a MT |
1015 | ret = -ENETUNREACH; |
1016 | goto out; | |
1017 | } | |
1018 | ||
1019 | /* Ensure we switch to the mode of the AP */ | |
0dc5a290 | 1020 | if (assoc_req->mode == IW_MODE_AUTO) { |
876c9d3a MT |
1021 | set_bit(ASSOC_FLAG_MODE, &assoc_req->flags); |
1022 | assoc_req->mode = new_mode; | |
1023 | } | |
1024 | } | |
1025 | ||
1026 | /* | |
1027 | * Check if the attributes being changing require deauthentication | |
1028 | * from the currently associated infrastructure access point. | |
1029 | */ | |
aa21c004 DW |
1030 | if (priv->mode == IW_MODE_INFRA) { |
1031 | if (should_deauth_infrastructure(priv, assoc_req)) { | |
10078321 | 1032 | ret = lbs_send_deauthentication(priv); |
876c9d3a | 1033 | if (ret) { |
9012b28a | 1034 | lbs_deb_assoc("Deauthentication due to new " |
876c9d3a MT |
1035 | "configuration request failed: %d\n", |
1036 | ret); | |
1037 | } | |
1038 | } | |
aa21c004 DW |
1039 | } else if (priv->mode == IW_MODE_ADHOC) { |
1040 | if (should_stop_adhoc(priv, assoc_req)) { | |
10078321 | 1041 | ret = lbs_stop_adhoc_network(priv); |
876c9d3a | 1042 | if (ret) { |
9012b28a | 1043 | lbs_deb_assoc("Teardown of AdHoc network due to " |
876c9d3a MT |
1044 | "new configuration request failed: %d\n", |
1045 | ret); | |
1046 | } | |
1047 | ||
1048 | } | |
1049 | } | |
1050 | ||
1051 | /* Send the various configuration bits to the firmware */ | |
1052 | if (test_bit(ASSOC_FLAG_MODE, &assoc_req->flags)) { | |
1053 | ret = assoc_helper_mode(priv, assoc_req); | |
0765af44 | 1054 | if (ret) |
876c9d3a | 1055 | goto out; |
876c9d3a MT |
1056 | } |
1057 | ||
ef9a264b DW |
1058 | if (test_bit(ASSOC_FLAG_CHANNEL, &assoc_req->flags)) { |
1059 | ret = assoc_helper_channel(priv, assoc_req); | |
0765af44 | 1060 | if (ret) |
ef9a264b | 1061 | goto out; |
ef9a264b DW |
1062 | } |
1063 | ||
876c9d3a MT |
1064 | if ( test_bit(ASSOC_FLAG_WEP_KEYS, &assoc_req->flags) |
1065 | || test_bit(ASSOC_FLAG_WEP_TX_KEYIDX, &assoc_req->flags)) { | |
1066 | ret = assoc_helper_wep_keys(priv, assoc_req); | |
0765af44 | 1067 | if (ret) |
876c9d3a | 1068 | goto out; |
876c9d3a MT |
1069 | } |
1070 | ||
1071 | if (test_bit(ASSOC_FLAG_SECINFO, &assoc_req->flags)) { | |
1072 | ret = assoc_helper_secinfo(priv, assoc_req); | |
0765af44 | 1073 | if (ret) |
876c9d3a | 1074 | goto out; |
876c9d3a MT |
1075 | } |
1076 | ||
1077 | if (test_bit(ASSOC_FLAG_WPA_IE, &assoc_req->flags)) { | |
1078 | ret = assoc_helper_wpa_ie(priv, assoc_req); | |
0765af44 | 1079 | if (ret) |
876c9d3a | 1080 | goto out; |
876c9d3a MT |
1081 | } |
1082 | ||
1083 | if (test_bit(ASSOC_FLAG_WPA_MCAST_KEY, &assoc_req->flags) | |
1084 | || test_bit(ASSOC_FLAG_WPA_UCAST_KEY, &assoc_req->flags)) { | |
1085 | ret = assoc_helper_wpa_keys(priv, assoc_req); | |
0765af44 | 1086 | if (ret) |
876c9d3a | 1087 | goto out; |
876c9d3a MT |
1088 | } |
1089 | ||
1090 | /* SSID/BSSID should be the _last_ config option set, because they | |
1091 | * trigger the association attempt. | |
1092 | */ | |
1093 | if (test_bit(ASSOC_FLAG_BSSID, &assoc_req->flags) | |
1094 | || test_bit(ASSOC_FLAG_SSID, &assoc_req->flags)) { | |
1095 | int success = 1; | |
1096 | ||
1097 | ret = assoc_helper_associate(priv, assoc_req); | |
1098 | if (ret) { | |
91843463 | 1099 | lbs_deb_assoc("ASSOC: association unsuccessful: %d\n", |
876c9d3a MT |
1100 | ret); |
1101 | success = 0; | |
1102 | } | |
1103 | ||
aa21c004 | 1104 | if (priv->connect_status != LBS_CONNECTED) { |
91843463 HS |
1105 | lbs_deb_assoc("ASSOC: association unsuccessful, " |
1106 | "not connected\n"); | |
876c9d3a MT |
1107 | success = 0; |
1108 | } | |
1109 | ||
1110 | if (success) { | |
52507c20 | 1111 | lbs_deb_assoc("associated to %s\n", |
aa21c004 | 1112 | print_mac(mac, priv->curbssparams.bssid)); |
10078321 | 1113 | lbs_prepare_and_send_command(priv, |
0aef64d7 DW |
1114 | CMD_802_11_RSSI, |
1115 | 0, CMD_OPTION_WAITFORRSP, 0, NULL); | |
876c9d3a | 1116 | } else { |
876c9d3a MT |
1117 | ret = -1; |
1118 | } | |
1119 | } | |
1120 | ||
1121 | out: | |
1122 | if (ret) { | |
9012b28a | 1123 | lbs_deb_assoc("ASSOC: reconfiguration attempt unsuccessful: %d\n", |
876c9d3a MT |
1124 | ret); |
1125 | } | |
e76850d6 | 1126 | |
aa21c004 DW |
1127 | mutex_lock(&priv->lock); |
1128 | priv->in_progress_assoc_req = NULL; | |
1129 | mutex_unlock(&priv->lock); | |
876c9d3a | 1130 | kfree(assoc_req); |
9012b28a HS |
1131 | |
1132 | done: | |
1133 | lbs_deb_leave(LBS_DEB_ASSOC); | |
876c9d3a MT |
1134 | } |
1135 | ||
1136 | ||
1137 | /* | |
1138 | * Caller MUST hold any necessary locks | |
1139 | */ | |
aa21c004 | 1140 | struct assoc_request *lbs_get_association_request(struct lbs_private *priv) |
876c9d3a MT |
1141 | { |
1142 | struct assoc_request * assoc_req; | |
1143 | ||
0765af44 | 1144 | lbs_deb_enter(LBS_DEB_ASSOC); |
aa21c004 DW |
1145 | if (!priv->pending_assoc_req) { |
1146 | priv->pending_assoc_req = kzalloc(sizeof(struct assoc_request), | |
e76850d6 | 1147 | GFP_KERNEL); |
aa21c004 | 1148 | if (!priv->pending_assoc_req) { |
876c9d3a MT |
1149 | lbs_pr_info("Not enough memory to allocate association" |
1150 | " request!\n"); | |
1151 | return NULL; | |
1152 | } | |
1153 | } | |
1154 | ||
1155 | /* Copy current configuration attributes to the association request, | |
1156 | * but don't overwrite any that are already set. | |
1157 | */ | |
aa21c004 | 1158 | assoc_req = priv->pending_assoc_req; |
876c9d3a | 1159 | if (!test_bit(ASSOC_FLAG_SSID, &assoc_req->flags)) { |
aa21c004 | 1160 | memcpy(&assoc_req->ssid, &priv->curbssparams.ssid, |
d8efea25 | 1161 | IW_ESSID_MAX_SIZE); |
aa21c004 | 1162 | assoc_req->ssid_len = priv->curbssparams.ssid_len; |
876c9d3a MT |
1163 | } |
1164 | ||
1165 | if (!test_bit(ASSOC_FLAG_CHANNEL, &assoc_req->flags)) | |
aa21c004 | 1166 | assoc_req->channel = priv->curbssparams.channel; |
876c9d3a | 1167 | |
e76850d6 | 1168 | if (!test_bit(ASSOC_FLAG_BAND, &assoc_req->flags)) |
aa21c004 | 1169 | assoc_req->band = priv->curbssparams.band; |
e76850d6 | 1170 | |
876c9d3a | 1171 | if (!test_bit(ASSOC_FLAG_MODE, &assoc_req->flags)) |
aa21c004 | 1172 | assoc_req->mode = priv->mode; |
876c9d3a MT |
1173 | |
1174 | if (!test_bit(ASSOC_FLAG_BSSID, &assoc_req->flags)) { | |
aa21c004 | 1175 | memcpy(&assoc_req->bssid, priv->curbssparams.bssid, |
876c9d3a MT |
1176 | ETH_ALEN); |
1177 | } | |
1178 | ||
1179 | if (!test_bit(ASSOC_FLAG_WEP_KEYS, &assoc_req->flags)) { | |
1180 | int i; | |
1181 | for (i = 0; i < 4; i++) { | |
aa21c004 | 1182 | memcpy(&assoc_req->wep_keys[i], &priv->wep_keys[i], |
1443b653 | 1183 | sizeof(struct enc_key)); |
876c9d3a MT |
1184 | } |
1185 | } | |
1186 | ||
1187 | if (!test_bit(ASSOC_FLAG_WEP_TX_KEYIDX, &assoc_req->flags)) | |
aa21c004 | 1188 | assoc_req->wep_tx_keyidx = priv->wep_tx_keyidx; |
876c9d3a MT |
1189 | |
1190 | if (!test_bit(ASSOC_FLAG_WPA_MCAST_KEY, &assoc_req->flags)) { | |
aa21c004 | 1191 | memcpy(&assoc_req->wpa_mcast_key, &priv->wpa_mcast_key, |
1443b653 | 1192 | sizeof(struct enc_key)); |
876c9d3a MT |
1193 | } |
1194 | ||
1195 | if (!test_bit(ASSOC_FLAG_WPA_UCAST_KEY, &assoc_req->flags)) { | |
aa21c004 | 1196 | memcpy(&assoc_req->wpa_unicast_key, &priv->wpa_unicast_key, |
1443b653 | 1197 | sizeof(struct enc_key)); |
876c9d3a MT |
1198 | } |
1199 | ||
1200 | if (!test_bit(ASSOC_FLAG_SECINFO, &assoc_req->flags)) { | |
aa21c004 | 1201 | memcpy(&assoc_req->secinfo, &priv->secinfo, |
10078321 | 1202 | sizeof(struct lbs_802_11_security)); |
876c9d3a MT |
1203 | } |
1204 | ||
1205 | if (!test_bit(ASSOC_FLAG_WPA_IE, &assoc_req->flags)) { | |
aa21c004 | 1206 | memcpy(&assoc_req->wpa_ie, &priv->wpa_ie, |
876c9d3a | 1207 | MAX_WPA_IE_LEN); |
aa21c004 | 1208 | assoc_req->wpa_ie_len = priv->wpa_ie_len; |
876c9d3a MT |
1209 | } |
1210 | ||
0765af44 | 1211 | lbs_deb_leave(LBS_DEB_ASSOC); |
876c9d3a MT |
1212 | return assoc_req; |
1213 | } | |
697900ac HS |
1214 | |
1215 | ||
1216 | /** | |
1217 | * @brief This function finds common rates between rate1 and card rates. | |
1218 | * | |
1219 | * It will fill common rates in rate1 as output if found. | |
1220 | * | |
1221 | * NOTE: Setting the MSB of the basic rates need to be taken | |
1222 | * care, either before or after calling this function | |
1223 | * | |
1224 | * @param priv A pointer to struct lbs_private structure | |
1225 | * @param rate1 the buffer which keeps input and output | |
1226 | * @param rate1_size the size of rate1 buffer; new size of buffer on return | |
1227 | * | |
1228 | * @return 0 or -1 | |
1229 | */ | |
1230 | static int get_common_rates(struct lbs_private *priv, | |
1231 | u8 *rates, | |
1232 | u16 *rates_size) | |
1233 | { | |
1234 | u8 *card_rates = lbs_bg_rates; | |
1235 | size_t num_card_rates = sizeof(lbs_bg_rates); | |
1236 | int ret = 0, i, j; | |
1237 | u8 tmp[30]; | |
1238 | size_t tmp_size = 0; | |
1239 | ||
1240 | /* For each rate in card_rates that exists in rate1, copy to tmp */ | |
1241 | for (i = 0; card_rates[i] && (i < num_card_rates); i++) { | |
1242 | for (j = 0; rates[j] && (j < *rates_size); j++) { | |
1243 | if (rates[j] == card_rates[i]) | |
1244 | tmp[tmp_size++] = card_rates[i]; | |
1245 | } | |
1246 | } | |
1247 | ||
1248 | lbs_deb_hex(LBS_DEB_JOIN, "AP rates ", rates, *rates_size); | |
1249 | lbs_deb_hex(LBS_DEB_JOIN, "card rates ", card_rates, num_card_rates); | |
1250 | lbs_deb_hex(LBS_DEB_JOIN, "common rates", tmp, tmp_size); | |
1251 | lbs_deb_join("TX data rate 0x%02x\n", priv->cur_rate); | |
1252 | ||
1253 | if (!priv->auto_rate) { | |
1254 | for (i = 0; i < tmp_size; i++) { | |
1255 | if (tmp[i] == priv->cur_rate) | |
1256 | goto done; | |
1257 | } | |
1258 | lbs_pr_alert("Previously set fixed data rate %#x isn't " | |
1259 | "compatible with the network.\n", priv->cur_rate); | |
1260 | ret = -1; | |
1261 | goto done; | |
1262 | } | |
1263 | ret = 0; | |
1264 | ||
1265 | done: | |
1266 | memset(rates, 0, *rates_size); | |
1267 | *rates_size = min_t(int, tmp_size, *rates_size); | |
1268 | memcpy(rates, tmp, *rates_size); | |
1269 | return ret; | |
1270 | } | |
1271 | ||
1272 | ||
1273 | /** | |
1274 | * @brief Sets the MSB on basic rates as the firmware requires | |
1275 | * | |
1276 | * Scan through an array and set the MSB for basic data rates. | |
1277 | * | |
1278 | * @param rates buffer of data rates | |
1279 | * @param len size of buffer | |
1280 | */ | |
1281 | static void lbs_set_basic_rate_flags(u8 *rates, size_t len) | |
1282 | { | |
1283 | int i; | |
1284 | ||
1285 | for (i = 0; i < len; i++) { | |
1286 | if (rates[i] == 0x02 || rates[i] == 0x04 || | |
1287 | rates[i] == 0x0b || rates[i] == 0x16) | |
1288 | rates[i] |= 0x80; | |
1289 | } | |
1290 | } | |
1291 | ||
1292 | /** | |
1293 | * @brief Send Deauthentication Request | |
1294 | * | |
1295 | * @param priv A pointer to struct lbs_private structure | |
1296 | * @return 0--success, -1--fail | |
1297 | */ | |
1298 | int lbs_send_deauthentication(struct lbs_private *priv) | |
1299 | { | |
1300 | return lbs_prepare_and_send_command(priv, CMD_802_11_DEAUTHENTICATE, | |
1301 | 0, CMD_OPTION_WAITFORRSP, 0, NULL); | |
1302 | } | |
1303 | ||
1304 | /** | |
1305 | * @brief This function prepares command of authenticate. | |
1306 | * | |
1307 | * @param priv A pointer to struct lbs_private structure | |
1308 | * @param cmd A pointer to cmd_ds_command structure | |
1309 | * @param pdata_buf Void cast of pointer to a BSSID to authenticate with | |
1310 | * | |
1311 | * @return 0 or -1 | |
1312 | */ | |
1313 | int lbs_cmd_80211_authenticate(struct lbs_private *priv, | |
1314 | struct cmd_ds_command *cmd, | |
1315 | void *pdata_buf) | |
1316 | { | |
1317 | struct cmd_ds_802_11_authenticate *pauthenticate = &cmd->params.auth; | |
1318 | int ret = -1; | |
1319 | u8 *bssid = pdata_buf; | |
1320 | DECLARE_MAC_BUF(mac); | |
1321 | ||
1322 | lbs_deb_enter(LBS_DEB_JOIN); | |
1323 | ||
1324 | cmd->command = cpu_to_le16(CMD_802_11_AUTHENTICATE); | |
1325 | cmd->size = cpu_to_le16(sizeof(struct cmd_ds_802_11_authenticate) | |
1326 | + S_DS_GEN); | |
1327 | ||
1328 | /* translate auth mode to 802.11 defined wire value */ | |
1329 | switch (priv->secinfo.auth_mode) { | |
1330 | case IW_AUTH_ALG_OPEN_SYSTEM: | |
1331 | pauthenticate->authtype = 0x00; | |
1332 | break; | |
1333 | case IW_AUTH_ALG_SHARED_KEY: | |
1334 | pauthenticate->authtype = 0x01; | |
1335 | break; | |
1336 | case IW_AUTH_ALG_LEAP: | |
1337 | pauthenticate->authtype = 0x80; | |
1338 | break; | |
1339 | default: | |
1340 | lbs_deb_join("AUTH_CMD: invalid auth alg 0x%X\n", | |
1341 | priv->secinfo.auth_mode); | |
1342 | goto out; | |
1343 | } | |
1344 | ||
1345 | memcpy(pauthenticate->macaddr, bssid, ETH_ALEN); | |
1346 | ||
1347 | lbs_deb_join("AUTH_CMD: BSSID %s, auth 0x%x\n", | |
1348 | print_mac(mac, bssid), pauthenticate->authtype); | |
1349 | ret = 0; | |
1350 | ||
1351 | out: | |
1352 | lbs_deb_leave_args(LBS_DEB_JOIN, "ret %d", ret); | |
1353 | return ret; | |
1354 | } | |
1355 | ||
1356 | int lbs_cmd_80211_deauthenticate(struct lbs_private *priv, | |
1357 | struct cmd_ds_command *cmd) | |
1358 | { | |
1359 | struct cmd_ds_802_11_deauthenticate *dauth = &cmd->params.deauth; | |
1360 | ||
1361 | lbs_deb_enter(LBS_DEB_JOIN); | |
1362 | ||
1363 | cmd->command = cpu_to_le16(CMD_802_11_DEAUTHENTICATE); | |
1364 | cmd->size = cpu_to_le16(sizeof(struct cmd_ds_802_11_deauthenticate) + | |
1365 | S_DS_GEN); | |
1366 | ||
1367 | /* set AP MAC address */ | |
1368 | memmove(dauth->macaddr, priv->curbssparams.bssid, ETH_ALEN); | |
1369 | ||
1370 | /* Reason code 3 = Station is leaving */ | |
1371 | #define REASON_CODE_STA_LEAVING 3 | |
1372 | dauth->reasoncode = cpu_to_le16(REASON_CODE_STA_LEAVING); | |
1373 | ||
1374 | lbs_deb_leave(LBS_DEB_JOIN); | |
1375 | return 0; | |
1376 | } | |
1377 | ||
1378 | int lbs_cmd_80211_associate(struct lbs_private *priv, | |
1379 | struct cmd_ds_command *cmd, void *pdata_buf) | |
1380 | { | |
1381 | struct cmd_ds_802_11_associate *passo = &cmd->params.associate; | |
1382 | int ret = 0; | |
1383 | struct assoc_request *assoc_req = pdata_buf; | |
1384 | struct bss_descriptor *bss = &assoc_req->bss; | |
1385 | u8 *pos; | |
1386 | u16 tmpcap, tmplen; | |
1387 | struct mrvlietypes_ssidparamset *ssid; | |
1388 | struct mrvlietypes_phyparamset *phy; | |
1389 | struct mrvlietypes_ssparamset *ss; | |
1390 | struct mrvlietypes_ratesparamset *rates; | |
1391 | struct mrvlietypes_rsnparamset *rsn; | |
1392 | ||
1393 | lbs_deb_enter(LBS_DEB_ASSOC); | |
1394 | ||
1395 | pos = (u8 *) passo; | |
1396 | ||
1397 | if (!priv) { | |
1398 | ret = -1; | |
1399 | goto done; | |
1400 | } | |
1401 | ||
1402 | cmd->command = cpu_to_le16(CMD_802_11_ASSOCIATE); | |
1403 | ||
1404 | memcpy(passo->peerstaaddr, bss->bssid, sizeof(passo->peerstaaddr)); | |
1405 | pos += sizeof(passo->peerstaaddr); | |
1406 | ||
1407 | /* set the listen interval */ | |
1408 | passo->listeninterval = cpu_to_le16(MRVDRV_DEFAULT_LISTEN_INTERVAL); | |
1409 | ||
1410 | pos += sizeof(passo->capability); | |
1411 | pos += sizeof(passo->listeninterval); | |
1412 | pos += sizeof(passo->bcnperiod); | |
1413 | pos += sizeof(passo->dtimperiod); | |
1414 | ||
1415 | ssid = (struct mrvlietypes_ssidparamset *) pos; | |
1416 | ssid->header.type = cpu_to_le16(TLV_TYPE_SSID); | |
1417 | tmplen = bss->ssid_len; | |
1418 | ssid->header.len = cpu_to_le16(tmplen); | |
1419 | memcpy(ssid->ssid, bss->ssid, tmplen); | |
1420 | pos += sizeof(ssid->header) + tmplen; | |
1421 | ||
1422 | phy = (struct mrvlietypes_phyparamset *) pos; | |
1423 | phy->header.type = cpu_to_le16(TLV_TYPE_PHY_DS); | |
1424 | tmplen = sizeof(phy->fh_ds.dsparamset); | |
1425 | phy->header.len = cpu_to_le16(tmplen); | |
1426 | memcpy(&phy->fh_ds.dsparamset, | |
1427 | &bss->phyparamset.dsparamset.currentchan, | |
1428 | tmplen); | |
1429 | pos += sizeof(phy->header) + tmplen; | |
1430 | ||
1431 | ss = (struct mrvlietypes_ssparamset *) pos; | |
1432 | ss->header.type = cpu_to_le16(TLV_TYPE_CF); | |
1433 | tmplen = sizeof(ss->cf_ibss.cfparamset); | |
1434 | ss->header.len = cpu_to_le16(tmplen); | |
1435 | pos += sizeof(ss->header) + tmplen; | |
1436 | ||
1437 | rates = (struct mrvlietypes_ratesparamset *) pos; | |
1438 | rates->header.type = cpu_to_le16(TLV_TYPE_RATES); | |
1439 | memcpy(&rates->rates, &bss->rates, MAX_RATES); | |
1440 | tmplen = MAX_RATES; | |
1441 | if (get_common_rates(priv, rates->rates, &tmplen)) { | |
1442 | ret = -1; | |
1443 | goto done; | |
1444 | } | |
1445 | pos += sizeof(rates->header) + tmplen; | |
1446 | rates->header.len = cpu_to_le16(tmplen); | |
1447 | lbs_deb_assoc("ASSOC_CMD: num rates %u\n", tmplen); | |
1448 | ||
1449 | /* Copy the infra. association rates into Current BSS state structure */ | |
1450 | memset(&priv->curbssparams.rates, 0, sizeof(priv->curbssparams.rates)); | |
1451 | memcpy(&priv->curbssparams.rates, &rates->rates, tmplen); | |
1452 | ||
1453 | /* Set MSB on basic rates as the firmware requires, but _after_ | |
1454 | * copying to current bss rates. | |
1455 | */ | |
1456 | lbs_set_basic_rate_flags(rates->rates, tmplen); | |
1457 | ||
1458 | if (assoc_req->secinfo.WPAenabled || assoc_req->secinfo.WPA2enabled) { | |
1459 | rsn = (struct mrvlietypes_rsnparamset *) pos; | |
1460 | /* WPA_IE or WPA2_IE */ | |
1461 | rsn->header.type = cpu_to_le16((u16) assoc_req->wpa_ie[0]); | |
1462 | tmplen = (u16) assoc_req->wpa_ie[1]; | |
1463 | rsn->header.len = cpu_to_le16(tmplen); | |
1464 | memcpy(rsn->rsnie, &assoc_req->wpa_ie[2], tmplen); | |
1465 | lbs_deb_hex(LBS_DEB_JOIN, "ASSOC_CMD: RSN IE", (u8 *) rsn, | |
1466 | sizeof(rsn->header) + tmplen); | |
1467 | pos += sizeof(rsn->header) + tmplen; | |
1468 | } | |
1469 | ||
1470 | /* update curbssparams */ | |
1471 | priv->curbssparams.channel = bss->phyparamset.dsparamset.currentchan; | |
1472 | ||
1473 | if (lbs_parse_dnld_countryinfo_11d(priv, bss)) { | |
1474 | ret = -1; | |
1475 | goto done; | |
1476 | } | |
1477 | ||
1478 | cmd->size = cpu_to_le16((u16) (pos - (u8 *) passo) + S_DS_GEN); | |
1479 | ||
1480 | /* set the capability info */ | |
1481 | tmpcap = (bss->capability & CAPINFO_MASK); | |
1482 | if (bss->mode == IW_MODE_INFRA) | |
1483 | tmpcap |= WLAN_CAPABILITY_ESS; | |
1484 | passo->capability = cpu_to_le16(tmpcap); | |
1485 | lbs_deb_assoc("ASSOC_CMD: capability 0x%04x\n", tmpcap); | |
1486 | ||
1487 | done: | |
1488 | lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret); | |
1489 | return ret; | |
1490 | } | |
1491 | ||
1492 | int lbs_cmd_80211_ad_hoc_start(struct lbs_private *priv, | |
1493 | struct cmd_ds_command *cmd, void *pdata_buf) | |
1494 | { | |
1495 | struct cmd_ds_802_11_ad_hoc_start *adhs = &cmd->params.ads; | |
1496 | int ret = 0; | |
1497 | int cmdappendsize = 0; | |
1498 | struct assoc_request *assoc_req = pdata_buf; | |
1499 | u16 tmpcap = 0; | |
1500 | size_t ratesize = 0; | |
1501 | ||
1502 | lbs_deb_enter(LBS_DEB_JOIN); | |
1503 | ||
1504 | if (!priv) { | |
1505 | ret = -1; | |
1506 | goto done; | |
1507 | } | |
1508 | ||
1509 | cmd->command = cpu_to_le16(CMD_802_11_AD_HOC_START); | |
1510 | ||
1511 | /* | |
1512 | * Fill in the parameters for 2 data structures: | |
1513 | * 1. cmd_ds_802_11_ad_hoc_start command | |
1514 | * 2. priv->scantable[i] | |
1515 | * | |
1516 | * Driver will fill up SSID, bsstype,IBSS param, Physical Param, | |
1517 | * probe delay, and cap info. | |
1518 | * | |
1519 | * Firmware will fill up beacon period, DTIM, Basic rates | |
1520 | * and operational rates. | |
1521 | */ | |
1522 | ||
1523 | memset(adhs->ssid, 0, IW_ESSID_MAX_SIZE); | |
1524 | memcpy(adhs->ssid, assoc_req->ssid, assoc_req->ssid_len); | |
1525 | ||
1526 | lbs_deb_join("ADHOC_S_CMD: SSID '%s', ssid length %u\n", | |
1527 | escape_essid(assoc_req->ssid, assoc_req->ssid_len), | |
1528 | assoc_req->ssid_len); | |
1529 | ||
1530 | /* set the BSS type */ | |
1531 | adhs->bsstype = CMD_BSS_TYPE_IBSS; | |
1532 | priv->mode = IW_MODE_ADHOC; | |
1533 | if (priv->beacon_period == 0) | |
1534 | priv->beacon_period = MRVDRV_BEACON_INTERVAL; | |
1535 | adhs->beaconperiod = cpu_to_le16(priv->beacon_period); | |
1536 | ||
1537 | /* set Physical param set */ | |
1538 | #define DS_PARA_IE_ID 3 | |
1539 | #define DS_PARA_IE_LEN 1 | |
1540 | ||
1541 | adhs->phyparamset.dsparamset.elementid = DS_PARA_IE_ID; | |
1542 | adhs->phyparamset.dsparamset.len = DS_PARA_IE_LEN; | |
1543 | ||
1544 | WARN_ON(!assoc_req->channel); | |
1545 | ||
1546 | lbs_deb_join("ADHOC_S_CMD: Creating ADHOC on channel %d\n", | |
1547 | assoc_req->channel); | |
1548 | ||
1549 | adhs->phyparamset.dsparamset.currentchan = assoc_req->channel; | |
1550 | ||
1551 | /* set IBSS param set */ | |
1552 | #define IBSS_PARA_IE_ID 6 | |
1553 | #define IBSS_PARA_IE_LEN 2 | |
1554 | ||
1555 | adhs->ssparamset.ibssparamset.elementid = IBSS_PARA_IE_ID; | |
1556 | adhs->ssparamset.ibssparamset.len = IBSS_PARA_IE_LEN; | |
1557 | adhs->ssparamset.ibssparamset.atimwindow = 0; | |
1558 | ||
1559 | /* set capability info */ | |
1560 | tmpcap = WLAN_CAPABILITY_IBSS; | |
1561 | if (assoc_req->secinfo.wep_enabled) { | |
1562 | lbs_deb_join("ADHOC_S_CMD: WEP enabled, " | |
1563 | "setting privacy on\n"); | |
1564 | tmpcap |= WLAN_CAPABILITY_PRIVACY; | |
1565 | } else { | |
1566 | lbs_deb_join("ADHOC_S_CMD: WEP disabled, " | |
1567 | "setting privacy off\n"); | |
1568 | } | |
1569 | adhs->capability = cpu_to_le16(tmpcap); | |
1570 | ||
1571 | /* probedelay */ | |
1572 | adhs->probedelay = cpu_to_le16(CMD_SCAN_PROBE_DELAY_TIME); | |
1573 | ||
1574 | memset(adhs->rates, 0, sizeof(adhs->rates)); | |
1575 | ratesize = min(sizeof(adhs->rates), sizeof(lbs_bg_rates)); | |
1576 | memcpy(adhs->rates, lbs_bg_rates, ratesize); | |
1577 | ||
1578 | /* Copy the ad-hoc creating rates into Current BSS state structure */ | |
1579 | memset(&priv->curbssparams.rates, 0, sizeof(priv->curbssparams.rates)); | |
1580 | memcpy(&priv->curbssparams.rates, &adhs->rates, ratesize); | |
1581 | ||
1582 | /* Set MSB on basic rates as the firmware requires, but _after_ | |
1583 | * copying to current bss rates. | |
1584 | */ | |
1585 | lbs_set_basic_rate_flags(adhs->rates, ratesize); | |
1586 | ||
1587 | lbs_deb_join("ADHOC_S_CMD: rates=%02x %02x %02x %02x \n", | |
1588 | adhs->rates[0], adhs->rates[1], adhs->rates[2], adhs->rates[3]); | |
1589 | ||
1590 | lbs_deb_join("ADHOC_S_CMD: AD HOC Start command is ready\n"); | |
1591 | ||
1592 | if (lbs_create_dnld_countryinfo_11d(priv)) { | |
1593 | lbs_deb_join("ADHOC_S_CMD: dnld_countryinfo_11d failed\n"); | |
1594 | ret = -1; | |
1595 | goto done; | |
1596 | } | |
1597 | ||
1598 | cmd->size = cpu_to_le16(sizeof(struct cmd_ds_802_11_ad_hoc_start) + | |
1599 | S_DS_GEN + cmdappendsize); | |
1600 | ||
1601 | ret = 0; | |
1602 | done: | |
1603 | lbs_deb_leave_args(LBS_DEB_JOIN, "ret %d", ret); | |
1604 | return ret; | |
1605 | } | |
1606 | ||
1607 | int lbs_cmd_80211_ad_hoc_stop(struct cmd_ds_command *cmd) | |
1608 | { | |
1609 | cmd->command = cpu_to_le16(CMD_802_11_AD_HOC_STOP); | |
1610 | cmd->size = cpu_to_le16(S_DS_GEN); | |
1611 | ||
1612 | return 0; | |
1613 | } | |
1614 | ||
1615 | int lbs_cmd_80211_ad_hoc_join(struct lbs_private *priv, | |
1616 | struct cmd_ds_command *cmd, void *pdata_buf) | |
1617 | { | |
1618 | struct cmd_ds_802_11_ad_hoc_join *join_cmd = &cmd->params.adj; | |
1619 | struct assoc_request *assoc_req = pdata_buf; | |
1620 | struct bss_descriptor *bss = &assoc_req->bss; | |
1621 | int cmdappendsize = 0; | |
1622 | int ret = 0; | |
1623 | u16 ratesize = 0; | |
1624 | DECLARE_MAC_BUF(mac); | |
1625 | ||
1626 | lbs_deb_enter(LBS_DEB_JOIN); | |
1627 | ||
1628 | cmd->command = cpu_to_le16(CMD_802_11_AD_HOC_JOIN); | |
1629 | ||
1630 | join_cmd->bss.type = CMD_BSS_TYPE_IBSS; | |
1631 | join_cmd->bss.beaconperiod = cpu_to_le16(bss->beaconperiod); | |
1632 | ||
1633 | memcpy(&join_cmd->bss.bssid, &bss->bssid, ETH_ALEN); | |
1634 | memcpy(&join_cmd->bss.ssid, &bss->ssid, bss->ssid_len); | |
1635 | ||
1636 | memcpy(&join_cmd->bss.phyparamset, &bss->phyparamset, | |
1637 | sizeof(union ieeetypes_phyparamset)); | |
1638 | ||
1639 | memcpy(&join_cmd->bss.ssparamset, &bss->ssparamset, | |
1640 | sizeof(union IEEEtypes_ssparamset)); | |
1641 | ||
1642 | join_cmd->bss.capability = cpu_to_le16(bss->capability & CAPINFO_MASK); | |
1643 | lbs_deb_join("ADHOC_J_CMD: tmpcap=%4X CAPINFO_MASK=%4X\n", | |
1644 | bss->capability, CAPINFO_MASK); | |
1645 | ||
1646 | /* information on BSSID descriptor passed to FW */ | |
1647 | lbs_deb_join( | |
1648 | "ADHOC_J_CMD: BSSID = %s, SSID = '%s'\n", | |
1649 | print_mac(mac, join_cmd->bss.bssid), | |
1650 | join_cmd->bss.ssid); | |
1651 | ||
1652 | /* failtimeout */ | |
1653 | join_cmd->failtimeout = cpu_to_le16(MRVDRV_ASSOCIATION_TIME_OUT); | |
1654 | ||
1655 | /* probedelay */ | |
1656 | join_cmd->probedelay = cpu_to_le16(CMD_SCAN_PROBE_DELAY_TIME); | |
1657 | ||
1658 | priv->curbssparams.channel = bss->channel; | |
1659 | ||
1660 | /* Copy Data rates from the rates recorded in scan response */ | |
1661 | memset(join_cmd->bss.rates, 0, sizeof(join_cmd->bss.rates)); | |
1662 | ratesize = min_t(u16, sizeof(join_cmd->bss.rates), MAX_RATES); | |
1663 | memcpy(join_cmd->bss.rates, bss->rates, ratesize); | |
1664 | if (get_common_rates(priv, join_cmd->bss.rates, &ratesize)) { | |
1665 | lbs_deb_join("ADHOC_J_CMD: get_common_rates returns error.\n"); | |
1666 | ret = -1; | |
1667 | goto done; | |
1668 | } | |
1669 | ||
1670 | /* Copy the ad-hoc creating rates into Current BSS state structure */ | |
1671 | memset(&priv->curbssparams.rates, 0, sizeof(priv->curbssparams.rates)); | |
1672 | memcpy(&priv->curbssparams.rates, join_cmd->bss.rates, ratesize); | |
1673 | ||
1674 | /* Set MSB on basic rates as the firmware requires, but _after_ | |
1675 | * copying to current bss rates. | |
1676 | */ | |
1677 | lbs_set_basic_rate_flags(join_cmd->bss.rates, ratesize); | |
1678 | ||
1679 | join_cmd->bss.ssparamset.ibssparamset.atimwindow = | |
1680 | cpu_to_le16(bss->atimwindow); | |
1681 | ||
1682 | if (assoc_req->secinfo.wep_enabled) { | |
1683 | u16 tmp = le16_to_cpu(join_cmd->bss.capability); | |
1684 | tmp |= WLAN_CAPABILITY_PRIVACY; | |
1685 | join_cmd->bss.capability = cpu_to_le16(tmp); | |
1686 | } | |
1687 | ||
1688 | if (priv->psmode == LBS802_11POWERMODEMAX_PSP) { | |
1689 | /* wake up first */ | |
1690 | __le32 Localpsmode; | |
1691 | ||
1692 | Localpsmode = cpu_to_le32(LBS802_11POWERMODECAM); | |
1693 | ret = lbs_prepare_and_send_command(priv, | |
1694 | CMD_802_11_PS_MODE, | |
1695 | CMD_ACT_SET, | |
1696 | 0, 0, &Localpsmode); | |
1697 | ||
1698 | if (ret) { | |
1699 | ret = -1; | |
1700 | goto done; | |
1701 | } | |
1702 | } | |
1703 | ||
1704 | if (lbs_parse_dnld_countryinfo_11d(priv, bss)) { | |
1705 | ret = -1; | |
1706 | goto done; | |
1707 | } | |
1708 | ||
1709 | cmd->size = cpu_to_le16(sizeof(struct cmd_ds_802_11_ad_hoc_join) + | |
1710 | S_DS_GEN + cmdappendsize); | |
1711 | ||
1712 | done: | |
1713 | lbs_deb_leave_args(LBS_DEB_JOIN, "ret %d", ret); | |
1714 | return ret; | |
1715 | } | |
1716 | ||
1717 | int lbs_ret_80211_associate(struct lbs_private *priv, | |
1718 | struct cmd_ds_command *resp) | |
1719 | { | |
1720 | int ret = 0; | |
1721 | union iwreq_data wrqu; | |
1722 | struct ieeetypes_assocrsp *passocrsp; | |
1723 | struct bss_descriptor *bss; | |
1724 | u16 status_code; | |
1725 | ||
1726 | lbs_deb_enter(LBS_DEB_ASSOC); | |
1727 | ||
1728 | if (!priv->in_progress_assoc_req) { | |
1729 | lbs_deb_assoc("ASSOC_RESP: no in-progress assoc request\n"); | |
1730 | ret = -1; | |
1731 | goto done; | |
1732 | } | |
1733 | bss = &priv->in_progress_assoc_req->bss; | |
1734 | ||
1735 | passocrsp = (struct ieeetypes_assocrsp *) &resp->params; | |
1736 | ||
1737 | /* | |
1738 | * Older FW versions map the IEEE 802.11 Status Code in the association | |
1739 | * response to the following values returned in passocrsp->statuscode: | |
1740 | * | |
1741 | * IEEE Status Code Marvell Status Code | |
1742 | * 0 -> 0x0000 ASSOC_RESULT_SUCCESS | |
1743 | * 13 -> 0x0004 ASSOC_RESULT_AUTH_REFUSED | |
1744 | * 14 -> 0x0004 ASSOC_RESULT_AUTH_REFUSED | |
1745 | * 15 -> 0x0004 ASSOC_RESULT_AUTH_REFUSED | |
1746 | * 16 -> 0x0004 ASSOC_RESULT_AUTH_REFUSED | |
1747 | * others -> 0x0003 ASSOC_RESULT_REFUSED | |
1748 | * | |
1749 | * Other response codes: | |
1750 | * 0x0001 -> ASSOC_RESULT_INVALID_PARAMETERS (unused) | |
1751 | * 0x0002 -> ASSOC_RESULT_TIMEOUT (internal timer expired waiting for | |
1752 | * association response from the AP) | |
1753 | */ | |
1754 | ||
1755 | status_code = le16_to_cpu(passocrsp->statuscode); | |
1756 | switch (status_code) { | |
1757 | case 0x00: | |
1758 | break; | |
1759 | case 0x01: | |
1760 | lbs_deb_assoc("ASSOC_RESP: invalid parameters\n"); | |
1761 | break; | |
1762 | case 0x02: | |
1763 | lbs_deb_assoc("ASSOC_RESP: internal timer " | |
1764 | "expired while waiting for the AP\n"); | |
1765 | break; | |
1766 | case 0x03: | |
1767 | lbs_deb_assoc("ASSOC_RESP: association " | |
1768 | "refused by AP\n"); | |
1769 | break; | |
1770 | case 0x04: | |
1771 | lbs_deb_assoc("ASSOC_RESP: authentication " | |
1772 | "refused by AP\n"); | |
1773 | break; | |
1774 | default: | |
1775 | lbs_deb_assoc("ASSOC_RESP: failure reason 0x%02x " | |
1776 | " unknown\n", status_code); | |
1777 | break; | |
1778 | } | |
1779 | ||
1780 | if (status_code) { | |
1781 | lbs_mac_event_disconnected(priv); | |
1782 | ret = -1; | |
1783 | goto done; | |
1784 | } | |
1785 | ||
1786 | lbs_deb_hex(LBS_DEB_ASSOC, "ASSOC_RESP", (void *)&resp->params, | |
1787 | le16_to_cpu(resp->size) - S_DS_GEN); | |
1788 | ||
1789 | /* Send a Media Connected event, according to the Spec */ | |
1790 | priv->connect_status = LBS_CONNECTED; | |
1791 | ||
1792 | /* Update current SSID and BSSID */ | |
1793 | memcpy(&priv->curbssparams.ssid, &bss->ssid, IW_ESSID_MAX_SIZE); | |
1794 | priv->curbssparams.ssid_len = bss->ssid_len; | |
1795 | memcpy(priv->curbssparams.bssid, bss->bssid, ETH_ALEN); | |
1796 | ||
1797 | priv->SNR[TYPE_RXPD][TYPE_AVG] = 0; | |
1798 | priv->NF[TYPE_RXPD][TYPE_AVG] = 0; | |
1799 | ||
1800 | memset(priv->rawSNR, 0x00, sizeof(priv->rawSNR)); | |
1801 | memset(priv->rawNF, 0x00, sizeof(priv->rawNF)); | |
1802 | priv->nextSNRNF = 0; | |
1803 | priv->numSNRNF = 0; | |
1804 | ||
1805 | netif_carrier_on(priv->dev); | |
1806 | if (!priv->tx_pending_len) | |
1807 | netif_wake_queue(priv->dev); | |
1808 | ||
1809 | memcpy(wrqu.ap_addr.sa_data, priv->curbssparams.bssid, ETH_ALEN); | |
1810 | wrqu.ap_addr.sa_family = ARPHRD_ETHER; | |
1811 | wireless_send_event(priv->dev, SIOCGIWAP, &wrqu, NULL); | |
1812 | ||
1813 | done: | |
1814 | lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret); | |
1815 | return ret; | |
1816 | } | |
1817 | ||
1818 | int lbs_ret_80211_disassociate(struct lbs_private *priv) | |
1819 | { | |
1820 | lbs_deb_enter(LBS_DEB_JOIN); | |
1821 | ||
1822 | lbs_mac_event_disconnected(priv); | |
1823 | ||
1824 | lbs_deb_leave(LBS_DEB_JOIN); | |
1825 | return 0; | |
1826 | } | |
1827 | ||
1828 | int lbs_ret_80211_ad_hoc_start(struct lbs_private *priv, | |
1829 | struct cmd_ds_command *resp) | |
1830 | { | |
1831 | int ret = 0; | |
1832 | u16 command = le16_to_cpu(resp->command); | |
1833 | u16 result = le16_to_cpu(resp->result); | |
1834 | struct cmd_ds_802_11_ad_hoc_result *padhocresult; | |
1835 | union iwreq_data wrqu; | |
1836 | struct bss_descriptor *bss; | |
1837 | DECLARE_MAC_BUF(mac); | |
1838 | ||
1839 | lbs_deb_enter(LBS_DEB_JOIN); | |
1840 | ||
1841 | padhocresult = &resp->params.result; | |
1842 | ||
1843 | lbs_deb_join("ADHOC_RESP: size = %d\n", le16_to_cpu(resp->size)); | |
1844 | lbs_deb_join("ADHOC_RESP: command = %x\n", command); | |
1845 | lbs_deb_join("ADHOC_RESP: result = %x\n", result); | |
1846 | ||
1847 | if (!priv->in_progress_assoc_req) { | |
1848 | lbs_deb_join("ADHOC_RESP: no in-progress association " | |
1849 | "request\n"); | |
1850 | ret = -1; | |
1851 | goto done; | |
1852 | } | |
1853 | bss = &priv->in_progress_assoc_req->bss; | |
1854 | ||
1855 | /* | |
1856 | * Join result code 0 --> SUCCESS | |
1857 | */ | |
1858 | if (result) { | |
1859 | lbs_deb_join("ADHOC_RESP: failed\n"); | |
1860 | if (priv->connect_status == LBS_CONNECTED) | |
1861 | lbs_mac_event_disconnected(priv); | |
1862 | ret = -1; | |
1863 | goto done; | |
1864 | } | |
1865 | ||
1866 | /* | |
1867 | * Now the join cmd should be successful | |
1868 | * If BSSID has changed use SSID to compare instead of BSSID | |
1869 | */ | |
1870 | lbs_deb_join("ADHOC_RESP: associated to '%s'\n", | |
1871 | escape_essid(bss->ssid, bss->ssid_len)); | |
1872 | ||
1873 | /* Send a Media Connected event, according to the Spec */ | |
1874 | priv->connect_status = LBS_CONNECTED; | |
1875 | ||
1876 | if (command == CMD_RET(CMD_802_11_AD_HOC_START)) { | |
1877 | /* Update the created network descriptor with the new BSSID */ | |
1878 | memcpy(bss->bssid, padhocresult->bssid, ETH_ALEN); | |
1879 | } | |
1880 | ||
1881 | /* Set the BSSID from the joined/started descriptor */ | |
1882 | memcpy(&priv->curbssparams.bssid, bss->bssid, ETH_ALEN); | |
1883 | ||
1884 | /* Set the new SSID to current SSID */ | |
1885 | memcpy(&priv->curbssparams.ssid, &bss->ssid, IW_ESSID_MAX_SIZE); | |
1886 | priv->curbssparams.ssid_len = bss->ssid_len; | |
1887 | ||
1888 | netif_carrier_on(priv->dev); | |
1889 | if (!priv->tx_pending_len) | |
1890 | netif_wake_queue(priv->dev); | |
1891 | ||
1892 | memset(&wrqu, 0, sizeof(wrqu)); | |
1893 | memcpy(wrqu.ap_addr.sa_data, priv->curbssparams.bssid, ETH_ALEN); | |
1894 | wrqu.ap_addr.sa_family = ARPHRD_ETHER; | |
1895 | wireless_send_event(priv->dev, SIOCGIWAP, &wrqu, NULL); | |
1896 | ||
1897 | lbs_deb_join("ADHOC_RESP: - Joined/Started Ad Hoc\n"); | |
1898 | lbs_deb_join("ADHOC_RESP: channel = %d\n", priv->curbssparams.channel); | |
1899 | lbs_deb_join("ADHOC_RESP: BSSID = %s\n", | |
1900 | print_mac(mac, padhocresult->bssid)); | |
1901 | ||
1902 | done: | |
1903 | lbs_deb_leave_args(LBS_DEB_JOIN, "ret %d", ret); | |
1904 | return ret; | |
1905 | } | |
1906 | ||
1907 | int lbs_ret_80211_ad_hoc_stop(struct lbs_private *priv) | |
1908 | { | |
1909 | lbs_deb_enter(LBS_DEB_JOIN); | |
1910 | ||
1911 | lbs_mac_event_disconnected(priv); | |
1912 | ||
1913 | lbs_deb_leave(LBS_DEB_JOIN); | |
1914 | return 0; | |
1915 | } |