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 */ | |
606 | lbs_mesh_config(priv, 0, assoc_req->channel); | |
8642f1f0 DW |
607 | } |
608 | ||
ef9a264b | 609 | lbs_deb_assoc("ASSOC: channel: %d -> %d\n", |
86062134 | 610 | priv->curbssparams.channel, assoc_req->channel); |
ef9a264b | 611 | |
2dd4b262 DW |
612 | ret = lbs_set_channel(priv, assoc_req->channel); |
613 | if (ret < 0) | |
23d36eec | 614 | lbs_deb_assoc("ASSOC: channel: error setting channel.\n"); |
ef9a264b | 615 | |
2dd4b262 DW |
616 | /* FIXME: shouldn't need to grab the channel _again_ after setting |
617 | * it since the firmware is supposed to return the new channel, but | |
618 | * whatever... */ | |
9f462577 | 619 | ret = lbs_update_channel(priv); |
d1a469fd | 620 | if (ret) { |
23d36eec | 621 | lbs_deb_assoc("ASSOC: channel: error getting channel.\n"); |
d1a469fd DW |
622 | goto done; |
623 | } | |
ef9a264b | 624 | |
aa21c004 | 625 | if (assoc_req->channel != priv->curbssparams.channel) { |
88ae2915 | 626 | lbs_deb_assoc("ASSOC: channel: failed to update channel to %d\n", |
ef9a264b | 627 | assoc_req->channel); |
8642f1f0 | 628 | goto restore_mesh; |
ef9a264b DW |
629 | } |
630 | ||
631 | if ( assoc_req->secinfo.wep_enabled | |
632 | && (assoc_req->wep_keys[0].len | |
633 | || assoc_req->wep_keys[1].len | |
634 | || assoc_req->wep_keys[2].len | |
635 | || assoc_req->wep_keys[3].len)) { | |
636 | /* Make sure WEP keys are re-sent to firmware */ | |
637 | set_bit(ASSOC_FLAG_WEP_KEYS, &assoc_req->flags); | |
638 | } | |
639 | ||
640 | /* Must restart/rejoin adhoc networks after channel change */ | |
23d36eec | 641 | set_bit(ASSOC_FLAG_SSID, &assoc_req->flags); |
ef9a264b | 642 | |
8642f1f0 DW |
643 | restore_mesh: |
644 | if (priv->mesh_dev) | |
86062134 | 645 | lbs_mesh_config(priv, 1, priv->curbssparams.channel); |
8642f1f0 DW |
646 | |
647 | done: | |
ef9a264b DW |
648 | lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret); |
649 | return ret; | |
650 | } | |
651 | ||
652 | ||
69f9032d | 653 | static int assoc_helper_wep_keys(struct lbs_private *priv, |
f70dd451 | 654 | struct assoc_request *assoc_req) |
876c9d3a | 655 | { |
876c9d3a MT |
656 | int i; |
657 | int ret = 0; | |
658 | ||
9012b28a | 659 | lbs_deb_enter(LBS_DEB_ASSOC); |
876c9d3a MT |
660 | |
661 | /* Set or remove WEP keys */ | |
f70dd451 DW |
662 | if (assoc_req->wep_keys[0].len || assoc_req->wep_keys[1].len || |
663 | assoc_req->wep_keys[2].len || assoc_req->wep_keys[3].len) | |
664 | ret = lbs_cmd_802_11_set_wep(priv, CMD_ACT_ADD, assoc_req); | |
665 | else | |
666 | ret = lbs_cmd_802_11_set_wep(priv, CMD_ACT_REMOVE, assoc_req); | |
876c9d3a MT |
667 | |
668 | if (ret) | |
669 | goto out; | |
670 | ||
671 | /* enable/disable the MAC's WEP packet filter */ | |
889c05bd | 672 | if (assoc_req->secinfo.wep_enabled) |
d9e9778c | 673 | priv->mac_control |= CMD_ACT_MAC_WEP_ENABLE; |
876c9d3a | 674 | else |
d9e9778c | 675 | priv->mac_control &= ~CMD_ACT_MAC_WEP_ENABLE; |
f70dd451 | 676 | |
c97329e2 | 677 | lbs_set_mac_control(priv); |
876c9d3a | 678 | |
aa21c004 | 679 | mutex_lock(&priv->lock); |
876c9d3a | 680 | |
aa21c004 | 681 | /* Copy WEP keys into priv wep key fields */ |
876c9d3a | 682 | for (i = 0; i < 4; i++) { |
aa21c004 | 683 | memcpy(&priv->wep_keys[i], &assoc_req->wep_keys[i], |
f70dd451 | 684 | sizeof(struct enc_key)); |
876c9d3a | 685 | } |
aa21c004 | 686 | priv->wep_tx_keyidx = assoc_req->wep_tx_keyidx; |
876c9d3a | 687 | |
aa21c004 | 688 | mutex_unlock(&priv->lock); |
876c9d3a MT |
689 | |
690 | out: | |
9012b28a | 691 | lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret); |
876c9d3a MT |
692 | return ret; |
693 | } | |
694 | ||
69f9032d | 695 | static int assoc_helper_secinfo(struct lbs_private *priv, |
876c9d3a MT |
696 | struct assoc_request * assoc_req) |
697 | { | |
876c9d3a | 698 | int ret = 0; |
4f59abf1 DW |
699 | uint16_t do_wpa; |
700 | uint16_t rsn = 0; | |
876c9d3a | 701 | |
9012b28a | 702 | lbs_deb_enter(LBS_DEB_ASSOC); |
876c9d3a | 703 | |
aa21c004 | 704 | memcpy(&priv->secinfo, &assoc_req->secinfo, |
10078321 | 705 | sizeof(struct lbs_802_11_security)); |
876c9d3a | 706 | |
c97329e2 | 707 | lbs_set_mac_control(priv); |
876c9d3a | 708 | |
18c96c34 DW |
709 | /* If RSN is already enabled, don't try to enable it again, since |
710 | * ENABLE_RSN resets internal state machines and will clobber the | |
711 | * 4-way WPA handshake. | |
712 | */ | |
713 | ||
714 | /* Get RSN enabled/disabled */ | |
4f59abf1 | 715 | ret = lbs_cmd_802_11_enable_rsn(priv, CMD_ACT_GET, &rsn); |
18c96c34 | 716 | if (ret) { |
23d36eec | 717 | lbs_deb_assoc("Failed to get RSN status: %d\n", ret); |
18c96c34 DW |
718 | goto out; |
719 | } | |
720 | ||
721 | /* Don't re-enable RSN if it's already enabled */ | |
4f59abf1 | 722 | do_wpa = assoc_req->secinfo.WPAenabled || assoc_req->secinfo.WPA2enabled; |
18c96c34 DW |
723 | if (do_wpa == rsn) |
724 | goto out; | |
725 | ||
726 | /* Set RSN enabled/disabled */ | |
4f59abf1 | 727 | ret = lbs_cmd_802_11_enable_rsn(priv, CMD_ACT_SET, &do_wpa); |
90a42210 DW |
728 | |
729 | out: | |
9012b28a | 730 | lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret); |
876c9d3a MT |
731 | return ret; |
732 | } | |
733 | ||
734 | ||
69f9032d | 735 | static int assoc_helper_wpa_keys(struct lbs_private *priv, |
876c9d3a MT |
736 | struct assoc_request * assoc_req) |
737 | { | |
738 | int ret = 0; | |
2bcde51d | 739 | unsigned int flags = assoc_req->flags; |
876c9d3a | 740 | |
9012b28a | 741 | lbs_deb_enter(LBS_DEB_ASSOC); |
876c9d3a | 742 | |
2bcde51d DW |
743 | /* Work around older firmware bug where WPA unicast and multicast |
744 | * keys must be set independently. Seen in SDIO parts with firmware | |
745 | * version 5.0.11p0. | |
746 | */ | |
876c9d3a | 747 | |
2bcde51d DW |
748 | if (test_bit(ASSOC_FLAG_WPA_UCAST_KEY, &assoc_req->flags)) { |
749 | clear_bit(ASSOC_FLAG_WPA_MCAST_KEY, &assoc_req->flags); | |
9e1228d0 | 750 | ret = lbs_cmd_802_11_key_material(priv, CMD_ACT_SET, assoc_req); |
2bcde51d DW |
751 | assoc_req->flags = flags; |
752 | } | |
753 | ||
754 | if (ret) | |
755 | goto out; | |
756 | ||
757 | if (test_bit(ASSOC_FLAG_WPA_MCAST_KEY, &assoc_req->flags)) { | |
758 | clear_bit(ASSOC_FLAG_WPA_UCAST_KEY, &assoc_req->flags); | |
759 | ||
9e1228d0 | 760 | ret = lbs_cmd_802_11_key_material(priv, CMD_ACT_SET, assoc_req); |
2bcde51d DW |
761 | assoc_req->flags = flags; |
762 | } | |
763 | ||
764 | out: | |
9012b28a | 765 | lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret); |
876c9d3a MT |
766 | return ret; |
767 | } | |
768 | ||
769 | ||
69f9032d | 770 | static int assoc_helper_wpa_ie(struct lbs_private *priv, |
876c9d3a MT |
771 | struct assoc_request * assoc_req) |
772 | { | |
876c9d3a MT |
773 | int ret = 0; |
774 | ||
9012b28a | 775 | lbs_deb_enter(LBS_DEB_ASSOC); |
876c9d3a MT |
776 | |
777 | if (assoc_req->secinfo.WPAenabled || assoc_req->secinfo.WPA2enabled) { | |
aa21c004 DW |
778 | memcpy(&priv->wpa_ie, &assoc_req->wpa_ie, assoc_req->wpa_ie_len); |
779 | priv->wpa_ie_len = assoc_req->wpa_ie_len; | |
876c9d3a | 780 | } else { |
aa21c004 DW |
781 | memset(&priv->wpa_ie, 0, MAX_WPA_IE_LEN); |
782 | priv->wpa_ie_len = 0; | |
876c9d3a MT |
783 | } |
784 | ||
9012b28a | 785 | lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret); |
876c9d3a MT |
786 | return ret; |
787 | } | |
788 | ||
789 | ||
aa21c004 | 790 | static int should_deauth_infrastructure(struct lbs_private *priv, |
876c9d3a MT |
791 | struct assoc_request * assoc_req) |
792 | { | |
0765af44 HS |
793 | int ret = 0; |
794 | ||
aa21c004 | 795 | if (priv->connect_status != LBS_CONNECTED) |
876c9d3a MT |
796 | return 0; |
797 | ||
52507c20 | 798 | lbs_deb_enter(LBS_DEB_ASSOC); |
876c9d3a | 799 | if (test_bit(ASSOC_FLAG_SSID, &assoc_req->flags)) { |
0765af44 HS |
800 | lbs_deb_assoc("Deauthenticating due to new SSID\n"); |
801 | ret = 1; | |
802 | goto out; | |
876c9d3a MT |
803 | } |
804 | ||
805 | if (test_bit(ASSOC_FLAG_SECINFO, &assoc_req->flags)) { | |
aa21c004 | 806 | if (priv->secinfo.auth_mode != assoc_req->secinfo.auth_mode) { |
0765af44 HS |
807 | lbs_deb_assoc("Deauthenticating due to new security\n"); |
808 | ret = 1; | |
809 | goto out; | |
876c9d3a MT |
810 | } |
811 | } | |
812 | ||
813 | if (test_bit(ASSOC_FLAG_BSSID, &assoc_req->flags)) { | |
0765af44 HS |
814 | lbs_deb_assoc("Deauthenticating due to new BSSID\n"); |
815 | ret = 1; | |
816 | goto out; | |
876c9d3a MT |
817 | } |
818 | ||
fff47f10 | 819 | if (test_bit(ASSOC_FLAG_CHANNEL, &assoc_req->flags)) { |
0765af44 HS |
820 | lbs_deb_assoc("Deauthenticating due to channel switch\n"); |
821 | ret = 1; | |
822 | goto out; | |
fff47f10 LCCR |
823 | } |
824 | ||
876c9d3a MT |
825 | /* FIXME: deal with 'auto' mode somehow */ |
826 | if (test_bit(ASSOC_FLAG_MODE, &assoc_req->flags)) { | |
0765af44 HS |
827 | if (assoc_req->mode != IW_MODE_INFRA) { |
828 | lbs_deb_assoc("Deauthenticating due to leaving " | |
829 | "infra mode\n"); | |
830 | ret = 1; | |
831 | goto out; | |
832 | } | |
876c9d3a MT |
833 | } |
834 | ||
0765af44 HS |
835 | out: |
836 | lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret); | |
52507c20 | 837 | return ret; |
876c9d3a MT |
838 | } |
839 | ||
840 | ||
aa21c004 | 841 | static int should_stop_adhoc(struct lbs_private *priv, |
876c9d3a MT |
842 | struct assoc_request * assoc_req) |
843 | { | |
0765af44 HS |
844 | lbs_deb_enter(LBS_DEB_ASSOC); |
845 | ||
aa21c004 | 846 | if (priv->connect_status != LBS_CONNECTED) |
876c9d3a MT |
847 | return 0; |
848 | ||
aa21c004 DW |
849 | if (lbs_ssid_cmp(priv->curbssparams.ssid, |
850 | priv->curbssparams.ssid_len, | |
d8efea25 | 851 | assoc_req->ssid, assoc_req->ssid_len) != 0) |
876c9d3a MT |
852 | return 1; |
853 | ||
854 | /* FIXME: deal with 'auto' mode somehow */ | |
855 | if (test_bit(ASSOC_FLAG_MODE, &assoc_req->flags)) { | |
0dc5a290 | 856 | if (assoc_req->mode != IW_MODE_ADHOC) |
876c9d3a MT |
857 | return 1; |
858 | } | |
859 | ||
ef9a264b | 860 | if (test_bit(ASSOC_FLAG_CHANNEL, &assoc_req->flags)) { |
aa21c004 | 861 | if (assoc_req->channel != priv->curbssparams.channel) |
ef9a264b DW |
862 | return 1; |
863 | } | |
864 | ||
0765af44 | 865 | lbs_deb_leave(LBS_DEB_ASSOC); |
876c9d3a MT |
866 | return 0; |
867 | } | |
868 | ||
869 | ||
245bf20f HS |
870 | /** |
871 | * @brief This function finds the best SSID in the Scan List | |
872 | * | |
873 | * Search the scan table for the best SSID that also matches the current | |
874 | * adapter network preference (infrastructure or adhoc) | |
875 | * | |
876 | * @param priv A pointer to struct lbs_private | |
877 | * | |
878 | * @return index in BSSID list | |
879 | */ | |
880 | static struct bss_descriptor *lbs_find_best_ssid_in_list( | |
881 | struct lbs_private *priv, uint8_t mode) | |
882 | { | |
883 | uint8_t bestrssi = 0; | |
884 | struct bss_descriptor *iter_bss; | |
885 | struct bss_descriptor *best_bss = NULL; | |
886 | ||
887 | lbs_deb_enter(LBS_DEB_SCAN); | |
888 | ||
889 | mutex_lock(&priv->lock); | |
890 | ||
891 | list_for_each_entry(iter_bss, &priv->network_list, list) { | |
892 | switch (mode) { | |
893 | case IW_MODE_INFRA: | |
894 | case IW_MODE_ADHOC: | |
895 | if (!is_network_compatible(priv, iter_bss, mode)) | |
896 | break; | |
897 | if (SCAN_RSSI(iter_bss->rssi) <= bestrssi) | |
898 | break; | |
899 | bestrssi = SCAN_RSSI(iter_bss->rssi); | |
900 | best_bss = iter_bss; | |
901 | break; | |
902 | case IW_MODE_AUTO: | |
903 | default: | |
904 | if (SCAN_RSSI(iter_bss->rssi) <= bestrssi) | |
905 | break; | |
906 | bestrssi = SCAN_RSSI(iter_bss->rssi); | |
907 | best_bss = iter_bss; | |
908 | break; | |
909 | } | |
910 | } | |
911 | ||
912 | mutex_unlock(&priv->lock); | |
913 | lbs_deb_leave_args(LBS_DEB_SCAN, "best_bss %p", best_bss); | |
914 | return best_bss; | |
915 | } | |
916 | ||
917 | /** | |
918 | * @brief Find the best AP | |
919 | * | |
920 | * Used from association worker. | |
921 | * | |
922 | * @param priv A pointer to struct lbs_private structure | |
923 | * @param pSSID A pointer to AP's ssid | |
924 | * | |
925 | * @return 0--success, otherwise--fail | |
926 | */ | |
927 | static int lbs_find_best_network_ssid(struct lbs_private *priv, | |
928 | uint8_t *out_ssid, uint8_t *out_ssid_len, uint8_t preferred_mode, | |
929 | uint8_t *out_mode) | |
930 | { | |
931 | int ret = -1; | |
932 | struct bss_descriptor *found; | |
933 | ||
934 | lbs_deb_enter(LBS_DEB_SCAN); | |
935 | ||
936 | priv->scan_ssid_len = 0; | |
937 | lbs_scan_networks(priv, 1); | |
938 | if (priv->surpriseremoved) | |
939 | goto out; | |
940 | ||
941 | found = lbs_find_best_ssid_in_list(priv, preferred_mode); | |
942 | if (found && (found->ssid_len > 0)) { | |
943 | memcpy(out_ssid, &found->ssid, IW_ESSID_MAX_SIZE); | |
944 | *out_ssid_len = found->ssid_len; | |
945 | *out_mode = found->mode; | |
946 | ret = 0; | |
947 | } | |
948 | ||
949 | out: | |
950 | lbs_deb_leave_args(LBS_DEB_SCAN, "ret %d", ret); | |
951 | return ret; | |
952 | } | |
953 | ||
954 | ||
10078321 | 955 | void lbs_association_worker(struct work_struct *work) |
876c9d3a | 956 | { |
69f9032d HS |
957 | struct lbs_private *priv = container_of(work, struct lbs_private, |
958 | assoc_work.work); | |
876c9d3a MT |
959 | struct assoc_request * assoc_req = NULL; |
960 | int ret = 0; | |
961 | int find_any_ssid = 0; | |
0795af57 | 962 | DECLARE_MAC_BUF(mac); |
876c9d3a | 963 | |
9012b28a | 964 | lbs_deb_enter(LBS_DEB_ASSOC); |
876c9d3a | 965 | |
aa21c004 DW |
966 | mutex_lock(&priv->lock); |
967 | assoc_req = priv->pending_assoc_req; | |
968 | priv->pending_assoc_req = NULL; | |
969 | priv->in_progress_assoc_req = assoc_req; | |
970 | mutex_unlock(&priv->lock); | |
876c9d3a | 971 | |
9012b28a HS |
972 | if (!assoc_req) |
973 | goto done; | |
876c9d3a | 974 | |
0765af44 HS |
975 | lbs_deb_assoc( |
976 | "Association Request:\n" | |
977 | " flags: 0x%08lx\n" | |
978 | " SSID: '%s'\n" | |
979 | " chann: %d\n" | |
980 | " band: %d\n" | |
981 | " mode: %d\n" | |
982 | " BSSID: %s\n" | |
983 | " secinfo: %s%s%s\n" | |
984 | " auth_mode: %d\n", | |
985 | assoc_req->flags, | |
986 | escape_essid(assoc_req->ssid, assoc_req->ssid_len), | |
987 | assoc_req->channel, assoc_req->band, assoc_req->mode, | |
988 | print_mac(mac, assoc_req->bssid), | |
989 | assoc_req->secinfo.WPAenabled ? " WPA" : "", | |
990 | assoc_req->secinfo.WPA2enabled ? " WPA2" : "", | |
991 | assoc_req->secinfo.wep_enabled ? " WEP" : "", | |
992 | assoc_req->secinfo.auth_mode); | |
876c9d3a MT |
993 | |
994 | /* If 'any' SSID was specified, find an SSID to associate with */ | |
995 | if (test_bit(ASSOC_FLAG_SSID, &assoc_req->flags) | |
d8efea25 | 996 | && !assoc_req->ssid_len) |
876c9d3a MT |
997 | find_any_ssid = 1; |
998 | ||
999 | /* But don't use 'any' SSID if there's a valid locked BSSID to use */ | |
1000 | if (test_bit(ASSOC_FLAG_BSSID, &assoc_req->flags)) { | |
3cf20931 DW |
1001 | if (compare_ether_addr(assoc_req->bssid, bssid_any) |
1002 | && compare_ether_addr(assoc_req->bssid, bssid_off)) | |
876c9d3a MT |
1003 | find_any_ssid = 0; |
1004 | } | |
1005 | ||
1006 | if (find_any_ssid) { | |
0dc5a290 | 1007 | u8 new_mode; |
876c9d3a | 1008 | |
10078321 | 1009 | ret = lbs_find_best_network_ssid(priv, assoc_req->ssid, |
d8efea25 | 1010 | &assoc_req->ssid_len, assoc_req->mode, &new_mode); |
876c9d3a | 1011 | if (ret) { |
9012b28a | 1012 | lbs_deb_assoc("Could not find best network\n"); |
876c9d3a MT |
1013 | ret = -ENETUNREACH; |
1014 | goto out; | |
1015 | } | |
1016 | ||
1017 | /* Ensure we switch to the mode of the AP */ | |
0dc5a290 | 1018 | if (assoc_req->mode == IW_MODE_AUTO) { |
876c9d3a MT |
1019 | set_bit(ASSOC_FLAG_MODE, &assoc_req->flags); |
1020 | assoc_req->mode = new_mode; | |
1021 | } | |
1022 | } | |
1023 | ||
1024 | /* | |
1025 | * Check if the attributes being changing require deauthentication | |
1026 | * from the currently associated infrastructure access point. | |
1027 | */ | |
aa21c004 DW |
1028 | if (priv->mode == IW_MODE_INFRA) { |
1029 | if (should_deauth_infrastructure(priv, assoc_req)) { | |
10078321 | 1030 | ret = lbs_send_deauthentication(priv); |
876c9d3a | 1031 | if (ret) { |
9012b28a | 1032 | lbs_deb_assoc("Deauthentication due to new " |
876c9d3a MT |
1033 | "configuration request failed: %d\n", |
1034 | ret); | |
1035 | } | |
1036 | } | |
aa21c004 DW |
1037 | } else if (priv->mode == IW_MODE_ADHOC) { |
1038 | if (should_stop_adhoc(priv, assoc_req)) { | |
10078321 | 1039 | ret = lbs_stop_adhoc_network(priv); |
876c9d3a | 1040 | if (ret) { |
9012b28a | 1041 | lbs_deb_assoc("Teardown of AdHoc network due to " |
876c9d3a MT |
1042 | "new configuration request failed: %d\n", |
1043 | ret); | |
1044 | } | |
1045 | ||
1046 | } | |
1047 | } | |
1048 | ||
1049 | /* Send the various configuration bits to the firmware */ | |
1050 | if (test_bit(ASSOC_FLAG_MODE, &assoc_req->flags)) { | |
1051 | ret = assoc_helper_mode(priv, assoc_req); | |
0765af44 | 1052 | if (ret) |
876c9d3a | 1053 | goto out; |
876c9d3a MT |
1054 | } |
1055 | ||
ef9a264b DW |
1056 | if (test_bit(ASSOC_FLAG_CHANNEL, &assoc_req->flags)) { |
1057 | ret = assoc_helper_channel(priv, assoc_req); | |
0765af44 | 1058 | if (ret) |
ef9a264b | 1059 | goto out; |
ef9a264b DW |
1060 | } |
1061 | ||
876c9d3a MT |
1062 | if ( test_bit(ASSOC_FLAG_WEP_KEYS, &assoc_req->flags) |
1063 | || test_bit(ASSOC_FLAG_WEP_TX_KEYIDX, &assoc_req->flags)) { | |
1064 | ret = assoc_helper_wep_keys(priv, assoc_req); | |
0765af44 | 1065 | if (ret) |
876c9d3a | 1066 | goto out; |
876c9d3a MT |
1067 | } |
1068 | ||
1069 | if (test_bit(ASSOC_FLAG_SECINFO, &assoc_req->flags)) { | |
1070 | ret = assoc_helper_secinfo(priv, assoc_req); | |
0765af44 | 1071 | if (ret) |
876c9d3a | 1072 | goto out; |
876c9d3a MT |
1073 | } |
1074 | ||
1075 | if (test_bit(ASSOC_FLAG_WPA_IE, &assoc_req->flags)) { | |
1076 | ret = assoc_helper_wpa_ie(priv, assoc_req); | |
0765af44 | 1077 | if (ret) |
876c9d3a | 1078 | goto out; |
876c9d3a MT |
1079 | } |
1080 | ||
1081 | if (test_bit(ASSOC_FLAG_WPA_MCAST_KEY, &assoc_req->flags) | |
1082 | || test_bit(ASSOC_FLAG_WPA_UCAST_KEY, &assoc_req->flags)) { | |
1083 | ret = assoc_helper_wpa_keys(priv, assoc_req); | |
0765af44 | 1084 | if (ret) |
876c9d3a | 1085 | goto out; |
876c9d3a MT |
1086 | } |
1087 | ||
1088 | /* SSID/BSSID should be the _last_ config option set, because they | |
1089 | * trigger the association attempt. | |
1090 | */ | |
1091 | if (test_bit(ASSOC_FLAG_BSSID, &assoc_req->flags) | |
1092 | || test_bit(ASSOC_FLAG_SSID, &assoc_req->flags)) { | |
1093 | int success = 1; | |
1094 | ||
1095 | ret = assoc_helper_associate(priv, assoc_req); | |
1096 | if (ret) { | |
91843463 | 1097 | lbs_deb_assoc("ASSOC: association unsuccessful: %d\n", |
876c9d3a MT |
1098 | ret); |
1099 | success = 0; | |
1100 | } | |
1101 | ||
aa21c004 | 1102 | if (priv->connect_status != LBS_CONNECTED) { |
91843463 HS |
1103 | lbs_deb_assoc("ASSOC: association unsuccessful, " |
1104 | "not connected\n"); | |
876c9d3a MT |
1105 | success = 0; |
1106 | } | |
1107 | ||
1108 | if (success) { | |
52507c20 | 1109 | lbs_deb_assoc("associated to %s\n", |
aa21c004 | 1110 | print_mac(mac, priv->curbssparams.bssid)); |
10078321 | 1111 | lbs_prepare_and_send_command(priv, |
0aef64d7 DW |
1112 | CMD_802_11_RSSI, |
1113 | 0, CMD_OPTION_WAITFORRSP, 0, NULL); | |
876c9d3a | 1114 | } else { |
876c9d3a MT |
1115 | ret = -1; |
1116 | } | |
1117 | } | |
1118 | ||
1119 | out: | |
1120 | if (ret) { | |
9012b28a | 1121 | lbs_deb_assoc("ASSOC: reconfiguration attempt unsuccessful: %d\n", |
876c9d3a MT |
1122 | ret); |
1123 | } | |
e76850d6 | 1124 | |
aa21c004 DW |
1125 | mutex_lock(&priv->lock); |
1126 | priv->in_progress_assoc_req = NULL; | |
1127 | mutex_unlock(&priv->lock); | |
876c9d3a | 1128 | kfree(assoc_req); |
9012b28a HS |
1129 | |
1130 | done: | |
1131 | lbs_deb_leave(LBS_DEB_ASSOC); | |
876c9d3a MT |
1132 | } |
1133 | ||
1134 | ||
1135 | /* | |
1136 | * Caller MUST hold any necessary locks | |
1137 | */ | |
aa21c004 | 1138 | struct assoc_request *lbs_get_association_request(struct lbs_private *priv) |
876c9d3a MT |
1139 | { |
1140 | struct assoc_request * assoc_req; | |
1141 | ||
0765af44 | 1142 | lbs_deb_enter(LBS_DEB_ASSOC); |
aa21c004 DW |
1143 | if (!priv->pending_assoc_req) { |
1144 | priv->pending_assoc_req = kzalloc(sizeof(struct assoc_request), | |
e76850d6 | 1145 | GFP_KERNEL); |
aa21c004 | 1146 | if (!priv->pending_assoc_req) { |
876c9d3a MT |
1147 | lbs_pr_info("Not enough memory to allocate association" |
1148 | " request!\n"); | |
1149 | return NULL; | |
1150 | } | |
1151 | } | |
1152 | ||
1153 | /* Copy current configuration attributes to the association request, | |
1154 | * but don't overwrite any that are already set. | |
1155 | */ | |
aa21c004 | 1156 | assoc_req = priv->pending_assoc_req; |
876c9d3a | 1157 | if (!test_bit(ASSOC_FLAG_SSID, &assoc_req->flags)) { |
aa21c004 | 1158 | memcpy(&assoc_req->ssid, &priv->curbssparams.ssid, |
d8efea25 | 1159 | IW_ESSID_MAX_SIZE); |
aa21c004 | 1160 | assoc_req->ssid_len = priv->curbssparams.ssid_len; |
876c9d3a MT |
1161 | } |
1162 | ||
1163 | if (!test_bit(ASSOC_FLAG_CHANNEL, &assoc_req->flags)) | |
aa21c004 | 1164 | assoc_req->channel = priv->curbssparams.channel; |
876c9d3a | 1165 | |
e76850d6 | 1166 | if (!test_bit(ASSOC_FLAG_BAND, &assoc_req->flags)) |
aa21c004 | 1167 | assoc_req->band = priv->curbssparams.band; |
e76850d6 | 1168 | |
876c9d3a | 1169 | if (!test_bit(ASSOC_FLAG_MODE, &assoc_req->flags)) |
aa21c004 | 1170 | assoc_req->mode = priv->mode; |
876c9d3a MT |
1171 | |
1172 | if (!test_bit(ASSOC_FLAG_BSSID, &assoc_req->flags)) { | |
aa21c004 | 1173 | memcpy(&assoc_req->bssid, priv->curbssparams.bssid, |
876c9d3a MT |
1174 | ETH_ALEN); |
1175 | } | |
1176 | ||
1177 | if (!test_bit(ASSOC_FLAG_WEP_KEYS, &assoc_req->flags)) { | |
1178 | int i; | |
1179 | for (i = 0; i < 4; i++) { | |
aa21c004 | 1180 | memcpy(&assoc_req->wep_keys[i], &priv->wep_keys[i], |
1443b653 | 1181 | sizeof(struct enc_key)); |
876c9d3a MT |
1182 | } |
1183 | } | |
1184 | ||
1185 | if (!test_bit(ASSOC_FLAG_WEP_TX_KEYIDX, &assoc_req->flags)) | |
aa21c004 | 1186 | assoc_req->wep_tx_keyidx = priv->wep_tx_keyidx; |
876c9d3a MT |
1187 | |
1188 | if (!test_bit(ASSOC_FLAG_WPA_MCAST_KEY, &assoc_req->flags)) { | |
aa21c004 | 1189 | memcpy(&assoc_req->wpa_mcast_key, &priv->wpa_mcast_key, |
1443b653 | 1190 | sizeof(struct enc_key)); |
876c9d3a MT |
1191 | } |
1192 | ||
1193 | if (!test_bit(ASSOC_FLAG_WPA_UCAST_KEY, &assoc_req->flags)) { | |
aa21c004 | 1194 | memcpy(&assoc_req->wpa_unicast_key, &priv->wpa_unicast_key, |
1443b653 | 1195 | sizeof(struct enc_key)); |
876c9d3a MT |
1196 | } |
1197 | ||
1198 | if (!test_bit(ASSOC_FLAG_SECINFO, &assoc_req->flags)) { | |
aa21c004 | 1199 | memcpy(&assoc_req->secinfo, &priv->secinfo, |
10078321 | 1200 | sizeof(struct lbs_802_11_security)); |
876c9d3a MT |
1201 | } |
1202 | ||
1203 | if (!test_bit(ASSOC_FLAG_WPA_IE, &assoc_req->flags)) { | |
aa21c004 | 1204 | memcpy(&assoc_req->wpa_ie, &priv->wpa_ie, |
876c9d3a | 1205 | MAX_WPA_IE_LEN); |
aa21c004 | 1206 | assoc_req->wpa_ie_len = priv->wpa_ie_len; |
876c9d3a MT |
1207 | } |
1208 | ||
0765af44 | 1209 | lbs_deb_leave(LBS_DEB_ASSOC); |
876c9d3a MT |
1210 | return assoc_req; |
1211 | } | |
697900ac HS |
1212 | |
1213 | ||
1214 | /** | |
1215 | * @brief This function finds common rates between rate1 and card rates. | |
1216 | * | |
1217 | * It will fill common rates in rate1 as output if found. | |
1218 | * | |
1219 | * NOTE: Setting the MSB of the basic rates need to be taken | |
1220 | * care, either before or after calling this function | |
1221 | * | |
1222 | * @param priv A pointer to struct lbs_private structure | |
1223 | * @param rate1 the buffer which keeps input and output | |
1224 | * @param rate1_size the size of rate1 buffer; new size of buffer on return | |
1225 | * | |
1226 | * @return 0 or -1 | |
1227 | */ | |
1228 | static int get_common_rates(struct lbs_private *priv, | |
1229 | u8 *rates, | |
1230 | u16 *rates_size) | |
1231 | { | |
1232 | u8 *card_rates = lbs_bg_rates; | |
1233 | size_t num_card_rates = sizeof(lbs_bg_rates); | |
1234 | int ret = 0, i, j; | |
1235 | u8 tmp[30]; | |
1236 | size_t tmp_size = 0; | |
1237 | ||
1238 | /* For each rate in card_rates that exists in rate1, copy to tmp */ | |
1239 | for (i = 0; card_rates[i] && (i < num_card_rates); i++) { | |
1240 | for (j = 0; rates[j] && (j < *rates_size); j++) { | |
1241 | if (rates[j] == card_rates[i]) | |
1242 | tmp[tmp_size++] = card_rates[i]; | |
1243 | } | |
1244 | } | |
1245 | ||
1246 | lbs_deb_hex(LBS_DEB_JOIN, "AP rates ", rates, *rates_size); | |
1247 | lbs_deb_hex(LBS_DEB_JOIN, "card rates ", card_rates, num_card_rates); | |
1248 | lbs_deb_hex(LBS_DEB_JOIN, "common rates", tmp, tmp_size); | |
1249 | lbs_deb_join("TX data rate 0x%02x\n", priv->cur_rate); | |
1250 | ||
1251 | if (!priv->auto_rate) { | |
1252 | for (i = 0; i < tmp_size; i++) { | |
1253 | if (tmp[i] == priv->cur_rate) | |
1254 | goto done; | |
1255 | } | |
1256 | lbs_pr_alert("Previously set fixed data rate %#x isn't " | |
1257 | "compatible with the network.\n", priv->cur_rate); | |
1258 | ret = -1; | |
1259 | goto done; | |
1260 | } | |
1261 | ret = 0; | |
1262 | ||
1263 | done: | |
1264 | memset(rates, 0, *rates_size); | |
1265 | *rates_size = min_t(int, tmp_size, *rates_size); | |
1266 | memcpy(rates, tmp, *rates_size); | |
1267 | return ret; | |
1268 | } | |
1269 | ||
1270 | ||
1271 | /** | |
1272 | * @brief Sets the MSB on basic rates as the firmware requires | |
1273 | * | |
1274 | * Scan through an array and set the MSB for basic data rates. | |
1275 | * | |
1276 | * @param rates buffer of data rates | |
1277 | * @param len size of buffer | |
1278 | */ | |
1279 | static void lbs_set_basic_rate_flags(u8 *rates, size_t len) | |
1280 | { | |
1281 | int i; | |
1282 | ||
1283 | for (i = 0; i < len; i++) { | |
1284 | if (rates[i] == 0x02 || rates[i] == 0x04 || | |
1285 | rates[i] == 0x0b || rates[i] == 0x16) | |
1286 | rates[i] |= 0x80; | |
1287 | } | |
1288 | } | |
1289 | ||
1290 | /** | |
1291 | * @brief Send Deauthentication Request | |
1292 | * | |
1293 | * @param priv A pointer to struct lbs_private structure | |
1294 | * @return 0--success, -1--fail | |
1295 | */ | |
1296 | int lbs_send_deauthentication(struct lbs_private *priv) | |
1297 | { | |
1298 | return lbs_prepare_and_send_command(priv, CMD_802_11_DEAUTHENTICATE, | |
1299 | 0, CMD_OPTION_WAITFORRSP, 0, NULL); | |
1300 | } | |
1301 | ||
1302 | /** | |
1303 | * @brief This function prepares command of authenticate. | |
1304 | * | |
1305 | * @param priv A pointer to struct lbs_private structure | |
1306 | * @param cmd A pointer to cmd_ds_command structure | |
1307 | * @param pdata_buf Void cast of pointer to a BSSID to authenticate with | |
1308 | * | |
1309 | * @return 0 or -1 | |
1310 | */ | |
1311 | int lbs_cmd_80211_authenticate(struct lbs_private *priv, | |
1312 | struct cmd_ds_command *cmd, | |
1313 | void *pdata_buf) | |
1314 | { | |
1315 | struct cmd_ds_802_11_authenticate *pauthenticate = &cmd->params.auth; | |
1316 | int ret = -1; | |
1317 | u8 *bssid = pdata_buf; | |
1318 | DECLARE_MAC_BUF(mac); | |
1319 | ||
1320 | lbs_deb_enter(LBS_DEB_JOIN); | |
1321 | ||
1322 | cmd->command = cpu_to_le16(CMD_802_11_AUTHENTICATE); | |
1323 | cmd->size = cpu_to_le16(sizeof(struct cmd_ds_802_11_authenticate) | |
1324 | + S_DS_GEN); | |
1325 | ||
1326 | /* translate auth mode to 802.11 defined wire value */ | |
1327 | switch (priv->secinfo.auth_mode) { | |
1328 | case IW_AUTH_ALG_OPEN_SYSTEM: | |
1329 | pauthenticate->authtype = 0x00; | |
1330 | break; | |
1331 | case IW_AUTH_ALG_SHARED_KEY: | |
1332 | pauthenticate->authtype = 0x01; | |
1333 | break; | |
1334 | case IW_AUTH_ALG_LEAP: | |
1335 | pauthenticate->authtype = 0x80; | |
1336 | break; | |
1337 | default: | |
1338 | lbs_deb_join("AUTH_CMD: invalid auth alg 0x%X\n", | |
1339 | priv->secinfo.auth_mode); | |
1340 | goto out; | |
1341 | } | |
1342 | ||
1343 | memcpy(pauthenticate->macaddr, bssid, ETH_ALEN); | |
1344 | ||
1345 | lbs_deb_join("AUTH_CMD: BSSID %s, auth 0x%x\n", | |
1346 | print_mac(mac, bssid), pauthenticate->authtype); | |
1347 | ret = 0; | |
1348 | ||
1349 | out: | |
1350 | lbs_deb_leave_args(LBS_DEB_JOIN, "ret %d", ret); | |
1351 | return ret; | |
1352 | } | |
1353 | ||
1354 | int lbs_cmd_80211_deauthenticate(struct lbs_private *priv, | |
1355 | struct cmd_ds_command *cmd) | |
1356 | { | |
1357 | struct cmd_ds_802_11_deauthenticate *dauth = &cmd->params.deauth; | |
1358 | ||
1359 | lbs_deb_enter(LBS_DEB_JOIN); | |
1360 | ||
1361 | cmd->command = cpu_to_le16(CMD_802_11_DEAUTHENTICATE); | |
1362 | cmd->size = cpu_to_le16(sizeof(struct cmd_ds_802_11_deauthenticate) + | |
1363 | S_DS_GEN); | |
1364 | ||
1365 | /* set AP MAC address */ | |
1366 | memmove(dauth->macaddr, priv->curbssparams.bssid, ETH_ALEN); | |
1367 | ||
1368 | /* Reason code 3 = Station is leaving */ | |
1369 | #define REASON_CODE_STA_LEAVING 3 | |
1370 | dauth->reasoncode = cpu_to_le16(REASON_CODE_STA_LEAVING); | |
1371 | ||
1372 | lbs_deb_leave(LBS_DEB_JOIN); | |
1373 | return 0; | |
1374 | } | |
1375 | ||
1376 | int lbs_cmd_80211_associate(struct lbs_private *priv, | |
1377 | struct cmd_ds_command *cmd, void *pdata_buf) | |
1378 | { | |
1379 | struct cmd_ds_802_11_associate *passo = &cmd->params.associate; | |
1380 | int ret = 0; | |
1381 | struct assoc_request *assoc_req = pdata_buf; | |
1382 | struct bss_descriptor *bss = &assoc_req->bss; | |
1383 | u8 *pos; | |
1384 | u16 tmpcap, tmplen; | |
1385 | struct mrvlietypes_ssidparamset *ssid; | |
1386 | struct mrvlietypes_phyparamset *phy; | |
1387 | struct mrvlietypes_ssparamset *ss; | |
1388 | struct mrvlietypes_ratesparamset *rates; | |
1389 | struct mrvlietypes_rsnparamset *rsn; | |
1390 | ||
1391 | lbs_deb_enter(LBS_DEB_ASSOC); | |
1392 | ||
1393 | pos = (u8 *) passo; | |
1394 | ||
1395 | if (!priv) { | |
1396 | ret = -1; | |
1397 | goto done; | |
1398 | } | |
1399 | ||
1400 | cmd->command = cpu_to_le16(CMD_802_11_ASSOCIATE); | |
1401 | ||
1402 | memcpy(passo->peerstaaddr, bss->bssid, sizeof(passo->peerstaaddr)); | |
1403 | pos += sizeof(passo->peerstaaddr); | |
1404 | ||
1405 | /* set the listen interval */ | |
1406 | passo->listeninterval = cpu_to_le16(MRVDRV_DEFAULT_LISTEN_INTERVAL); | |
1407 | ||
1408 | pos += sizeof(passo->capability); | |
1409 | pos += sizeof(passo->listeninterval); | |
1410 | pos += sizeof(passo->bcnperiod); | |
1411 | pos += sizeof(passo->dtimperiod); | |
1412 | ||
1413 | ssid = (struct mrvlietypes_ssidparamset *) pos; | |
1414 | ssid->header.type = cpu_to_le16(TLV_TYPE_SSID); | |
1415 | tmplen = bss->ssid_len; | |
1416 | ssid->header.len = cpu_to_le16(tmplen); | |
1417 | memcpy(ssid->ssid, bss->ssid, tmplen); | |
1418 | pos += sizeof(ssid->header) + tmplen; | |
1419 | ||
1420 | phy = (struct mrvlietypes_phyparamset *) pos; | |
1421 | phy->header.type = cpu_to_le16(TLV_TYPE_PHY_DS); | |
1422 | tmplen = sizeof(phy->fh_ds.dsparamset); | |
1423 | phy->header.len = cpu_to_le16(tmplen); | |
1424 | memcpy(&phy->fh_ds.dsparamset, | |
1425 | &bss->phyparamset.dsparamset.currentchan, | |
1426 | tmplen); | |
1427 | pos += sizeof(phy->header) + tmplen; | |
1428 | ||
1429 | ss = (struct mrvlietypes_ssparamset *) pos; | |
1430 | ss->header.type = cpu_to_le16(TLV_TYPE_CF); | |
1431 | tmplen = sizeof(ss->cf_ibss.cfparamset); | |
1432 | ss->header.len = cpu_to_le16(tmplen); | |
1433 | pos += sizeof(ss->header) + tmplen; | |
1434 | ||
1435 | rates = (struct mrvlietypes_ratesparamset *) pos; | |
1436 | rates->header.type = cpu_to_le16(TLV_TYPE_RATES); | |
1437 | memcpy(&rates->rates, &bss->rates, MAX_RATES); | |
1438 | tmplen = MAX_RATES; | |
1439 | if (get_common_rates(priv, rates->rates, &tmplen)) { | |
1440 | ret = -1; | |
1441 | goto done; | |
1442 | } | |
1443 | pos += sizeof(rates->header) + tmplen; | |
1444 | rates->header.len = cpu_to_le16(tmplen); | |
1445 | lbs_deb_assoc("ASSOC_CMD: num rates %u\n", tmplen); | |
1446 | ||
1447 | /* Copy the infra. association rates into Current BSS state structure */ | |
1448 | memset(&priv->curbssparams.rates, 0, sizeof(priv->curbssparams.rates)); | |
1449 | memcpy(&priv->curbssparams.rates, &rates->rates, tmplen); | |
1450 | ||
1451 | /* Set MSB on basic rates as the firmware requires, but _after_ | |
1452 | * copying to current bss rates. | |
1453 | */ | |
1454 | lbs_set_basic_rate_flags(rates->rates, tmplen); | |
1455 | ||
1456 | if (assoc_req->secinfo.WPAenabled || assoc_req->secinfo.WPA2enabled) { | |
1457 | rsn = (struct mrvlietypes_rsnparamset *) pos; | |
1458 | /* WPA_IE or WPA2_IE */ | |
1459 | rsn->header.type = cpu_to_le16((u16) assoc_req->wpa_ie[0]); | |
1460 | tmplen = (u16) assoc_req->wpa_ie[1]; | |
1461 | rsn->header.len = cpu_to_le16(tmplen); | |
1462 | memcpy(rsn->rsnie, &assoc_req->wpa_ie[2], tmplen); | |
1463 | lbs_deb_hex(LBS_DEB_JOIN, "ASSOC_CMD: RSN IE", (u8 *) rsn, | |
1464 | sizeof(rsn->header) + tmplen); | |
1465 | pos += sizeof(rsn->header) + tmplen; | |
1466 | } | |
1467 | ||
1468 | /* update curbssparams */ | |
1469 | priv->curbssparams.channel = bss->phyparamset.dsparamset.currentchan; | |
1470 | ||
1471 | if (lbs_parse_dnld_countryinfo_11d(priv, bss)) { | |
1472 | ret = -1; | |
1473 | goto done; | |
1474 | } | |
1475 | ||
1476 | cmd->size = cpu_to_le16((u16) (pos - (u8 *) passo) + S_DS_GEN); | |
1477 | ||
1478 | /* set the capability info */ | |
1479 | tmpcap = (bss->capability & CAPINFO_MASK); | |
1480 | if (bss->mode == IW_MODE_INFRA) | |
1481 | tmpcap |= WLAN_CAPABILITY_ESS; | |
1482 | passo->capability = cpu_to_le16(tmpcap); | |
1483 | lbs_deb_assoc("ASSOC_CMD: capability 0x%04x\n", tmpcap); | |
1484 | ||
1485 | done: | |
1486 | lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret); | |
1487 | return ret; | |
1488 | } | |
1489 | ||
1490 | int lbs_cmd_80211_ad_hoc_start(struct lbs_private *priv, | |
1491 | struct cmd_ds_command *cmd, void *pdata_buf) | |
1492 | { | |
1493 | struct cmd_ds_802_11_ad_hoc_start *adhs = &cmd->params.ads; | |
1494 | int ret = 0; | |
1495 | int cmdappendsize = 0; | |
1496 | struct assoc_request *assoc_req = pdata_buf; | |
1497 | u16 tmpcap = 0; | |
1498 | size_t ratesize = 0; | |
1499 | ||
1500 | lbs_deb_enter(LBS_DEB_JOIN); | |
1501 | ||
1502 | if (!priv) { | |
1503 | ret = -1; | |
1504 | goto done; | |
1505 | } | |
1506 | ||
1507 | cmd->command = cpu_to_le16(CMD_802_11_AD_HOC_START); | |
1508 | ||
1509 | /* | |
1510 | * Fill in the parameters for 2 data structures: | |
1511 | * 1. cmd_ds_802_11_ad_hoc_start command | |
1512 | * 2. priv->scantable[i] | |
1513 | * | |
1514 | * Driver will fill up SSID, bsstype,IBSS param, Physical Param, | |
1515 | * probe delay, and cap info. | |
1516 | * | |
1517 | * Firmware will fill up beacon period, DTIM, Basic rates | |
1518 | * and operational rates. | |
1519 | */ | |
1520 | ||
1521 | memset(adhs->ssid, 0, IW_ESSID_MAX_SIZE); | |
1522 | memcpy(adhs->ssid, assoc_req->ssid, assoc_req->ssid_len); | |
1523 | ||
1524 | lbs_deb_join("ADHOC_S_CMD: SSID '%s', ssid length %u\n", | |
1525 | escape_essid(assoc_req->ssid, assoc_req->ssid_len), | |
1526 | assoc_req->ssid_len); | |
1527 | ||
1528 | /* set the BSS type */ | |
1529 | adhs->bsstype = CMD_BSS_TYPE_IBSS; | |
1530 | priv->mode = IW_MODE_ADHOC; | |
1531 | if (priv->beacon_period == 0) | |
1532 | priv->beacon_period = MRVDRV_BEACON_INTERVAL; | |
1533 | adhs->beaconperiod = cpu_to_le16(priv->beacon_period); | |
1534 | ||
1535 | /* set Physical param set */ | |
1536 | #define DS_PARA_IE_ID 3 | |
1537 | #define DS_PARA_IE_LEN 1 | |
1538 | ||
1539 | adhs->phyparamset.dsparamset.elementid = DS_PARA_IE_ID; | |
1540 | adhs->phyparamset.dsparamset.len = DS_PARA_IE_LEN; | |
1541 | ||
1542 | WARN_ON(!assoc_req->channel); | |
1543 | ||
1544 | lbs_deb_join("ADHOC_S_CMD: Creating ADHOC on channel %d\n", | |
1545 | assoc_req->channel); | |
1546 | ||
1547 | adhs->phyparamset.dsparamset.currentchan = assoc_req->channel; | |
1548 | ||
1549 | /* set IBSS param set */ | |
1550 | #define IBSS_PARA_IE_ID 6 | |
1551 | #define IBSS_PARA_IE_LEN 2 | |
1552 | ||
1553 | adhs->ssparamset.ibssparamset.elementid = IBSS_PARA_IE_ID; | |
1554 | adhs->ssparamset.ibssparamset.len = IBSS_PARA_IE_LEN; | |
1555 | adhs->ssparamset.ibssparamset.atimwindow = 0; | |
1556 | ||
1557 | /* set capability info */ | |
1558 | tmpcap = WLAN_CAPABILITY_IBSS; | |
1559 | if (assoc_req->secinfo.wep_enabled) { | |
1560 | lbs_deb_join("ADHOC_S_CMD: WEP enabled, " | |
1561 | "setting privacy on\n"); | |
1562 | tmpcap |= WLAN_CAPABILITY_PRIVACY; | |
1563 | } else { | |
1564 | lbs_deb_join("ADHOC_S_CMD: WEP disabled, " | |
1565 | "setting privacy off\n"); | |
1566 | } | |
1567 | adhs->capability = cpu_to_le16(tmpcap); | |
1568 | ||
1569 | /* probedelay */ | |
1570 | adhs->probedelay = cpu_to_le16(CMD_SCAN_PROBE_DELAY_TIME); | |
1571 | ||
1572 | memset(adhs->rates, 0, sizeof(adhs->rates)); | |
1573 | ratesize = min(sizeof(adhs->rates), sizeof(lbs_bg_rates)); | |
1574 | memcpy(adhs->rates, lbs_bg_rates, ratesize); | |
1575 | ||
1576 | /* Copy the ad-hoc creating rates into Current BSS state structure */ | |
1577 | memset(&priv->curbssparams.rates, 0, sizeof(priv->curbssparams.rates)); | |
1578 | memcpy(&priv->curbssparams.rates, &adhs->rates, ratesize); | |
1579 | ||
1580 | /* Set MSB on basic rates as the firmware requires, but _after_ | |
1581 | * copying to current bss rates. | |
1582 | */ | |
1583 | lbs_set_basic_rate_flags(adhs->rates, ratesize); | |
1584 | ||
1585 | lbs_deb_join("ADHOC_S_CMD: rates=%02x %02x %02x %02x \n", | |
1586 | adhs->rates[0], adhs->rates[1], adhs->rates[2], adhs->rates[3]); | |
1587 | ||
1588 | lbs_deb_join("ADHOC_S_CMD: AD HOC Start command is ready\n"); | |
1589 | ||
1590 | if (lbs_create_dnld_countryinfo_11d(priv)) { | |
1591 | lbs_deb_join("ADHOC_S_CMD: dnld_countryinfo_11d failed\n"); | |
1592 | ret = -1; | |
1593 | goto done; | |
1594 | } | |
1595 | ||
1596 | cmd->size = cpu_to_le16(sizeof(struct cmd_ds_802_11_ad_hoc_start) + | |
1597 | S_DS_GEN + cmdappendsize); | |
1598 | ||
1599 | ret = 0; | |
1600 | done: | |
1601 | lbs_deb_leave_args(LBS_DEB_JOIN, "ret %d", ret); | |
1602 | return ret; | |
1603 | } | |
1604 | ||
1605 | int lbs_cmd_80211_ad_hoc_stop(struct cmd_ds_command *cmd) | |
1606 | { | |
1607 | cmd->command = cpu_to_le16(CMD_802_11_AD_HOC_STOP); | |
1608 | cmd->size = cpu_to_le16(S_DS_GEN); | |
1609 | ||
1610 | return 0; | |
1611 | } | |
1612 | ||
1613 | int lbs_cmd_80211_ad_hoc_join(struct lbs_private *priv, | |
1614 | struct cmd_ds_command *cmd, void *pdata_buf) | |
1615 | { | |
1616 | struct cmd_ds_802_11_ad_hoc_join *join_cmd = &cmd->params.adj; | |
1617 | struct assoc_request *assoc_req = pdata_buf; | |
1618 | struct bss_descriptor *bss = &assoc_req->bss; | |
1619 | int cmdappendsize = 0; | |
1620 | int ret = 0; | |
1621 | u16 ratesize = 0; | |
1622 | DECLARE_MAC_BUF(mac); | |
1623 | ||
1624 | lbs_deb_enter(LBS_DEB_JOIN); | |
1625 | ||
1626 | cmd->command = cpu_to_le16(CMD_802_11_AD_HOC_JOIN); | |
1627 | ||
1628 | join_cmd->bss.type = CMD_BSS_TYPE_IBSS; | |
1629 | join_cmd->bss.beaconperiod = cpu_to_le16(bss->beaconperiod); | |
1630 | ||
1631 | memcpy(&join_cmd->bss.bssid, &bss->bssid, ETH_ALEN); | |
1632 | memcpy(&join_cmd->bss.ssid, &bss->ssid, bss->ssid_len); | |
1633 | ||
1634 | memcpy(&join_cmd->bss.phyparamset, &bss->phyparamset, | |
1635 | sizeof(union ieeetypes_phyparamset)); | |
1636 | ||
1637 | memcpy(&join_cmd->bss.ssparamset, &bss->ssparamset, | |
1638 | sizeof(union IEEEtypes_ssparamset)); | |
1639 | ||
1640 | join_cmd->bss.capability = cpu_to_le16(bss->capability & CAPINFO_MASK); | |
1641 | lbs_deb_join("ADHOC_J_CMD: tmpcap=%4X CAPINFO_MASK=%4X\n", | |
1642 | bss->capability, CAPINFO_MASK); | |
1643 | ||
1644 | /* information on BSSID descriptor passed to FW */ | |
1645 | lbs_deb_join( | |
1646 | "ADHOC_J_CMD: BSSID = %s, SSID = '%s'\n", | |
1647 | print_mac(mac, join_cmd->bss.bssid), | |
1648 | join_cmd->bss.ssid); | |
1649 | ||
1650 | /* failtimeout */ | |
1651 | join_cmd->failtimeout = cpu_to_le16(MRVDRV_ASSOCIATION_TIME_OUT); | |
1652 | ||
1653 | /* probedelay */ | |
1654 | join_cmd->probedelay = cpu_to_le16(CMD_SCAN_PROBE_DELAY_TIME); | |
1655 | ||
1656 | priv->curbssparams.channel = bss->channel; | |
1657 | ||
1658 | /* Copy Data rates from the rates recorded in scan response */ | |
1659 | memset(join_cmd->bss.rates, 0, sizeof(join_cmd->bss.rates)); | |
1660 | ratesize = min_t(u16, sizeof(join_cmd->bss.rates), MAX_RATES); | |
1661 | memcpy(join_cmd->bss.rates, bss->rates, ratesize); | |
1662 | if (get_common_rates(priv, join_cmd->bss.rates, &ratesize)) { | |
1663 | lbs_deb_join("ADHOC_J_CMD: get_common_rates returns error.\n"); | |
1664 | ret = -1; | |
1665 | goto done; | |
1666 | } | |
1667 | ||
1668 | /* Copy the ad-hoc creating rates into Current BSS state structure */ | |
1669 | memset(&priv->curbssparams.rates, 0, sizeof(priv->curbssparams.rates)); | |
1670 | memcpy(&priv->curbssparams.rates, join_cmd->bss.rates, ratesize); | |
1671 | ||
1672 | /* Set MSB on basic rates as the firmware requires, but _after_ | |
1673 | * copying to current bss rates. | |
1674 | */ | |
1675 | lbs_set_basic_rate_flags(join_cmd->bss.rates, ratesize); | |
1676 | ||
1677 | join_cmd->bss.ssparamset.ibssparamset.atimwindow = | |
1678 | cpu_to_le16(bss->atimwindow); | |
1679 | ||
1680 | if (assoc_req->secinfo.wep_enabled) { | |
1681 | u16 tmp = le16_to_cpu(join_cmd->bss.capability); | |
1682 | tmp |= WLAN_CAPABILITY_PRIVACY; | |
1683 | join_cmd->bss.capability = cpu_to_le16(tmp); | |
1684 | } | |
1685 | ||
1686 | if (priv->psmode == LBS802_11POWERMODEMAX_PSP) { | |
1687 | /* wake up first */ | |
1688 | __le32 Localpsmode; | |
1689 | ||
1690 | Localpsmode = cpu_to_le32(LBS802_11POWERMODECAM); | |
1691 | ret = lbs_prepare_and_send_command(priv, | |
1692 | CMD_802_11_PS_MODE, | |
1693 | CMD_ACT_SET, | |
1694 | 0, 0, &Localpsmode); | |
1695 | ||
1696 | if (ret) { | |
1697 | ret = -1; | |
1698 | goto done; | |
1699 | } | |
1700 | } | |
1701 | ||
1702 | if (lbs_parse_dnld_countryinfo_11d(priv, bss)) { | |
1703 | ret = -1; | |
1704 | goto done; | |
1705 | } | |
1706 | ||
1707 | cmd->size = cpu_to_le16(sizeof(struct cmd_ds_802_11_ad_hoc_join) + | |
1708 | S_DS_GEN + cmdappendsize); | |
1709 | ||
1710 | done: | |
1711 | lbs_deb_leave_args(LBS_DEB_JOIN, "ret %d", ret); | |
1712 | return ret; | |
1713 | } | |
1714 | ||
1715 | int lbs_ret_80211_associate(struct lbs_private *priv, | |
1716 | struct cmd_ds_command *resp) | |
1717 | { | |
1718 | int ret = 0; | |
1719 | union iwreq_data wrqu; | |
1720 | struct ieeetypes_assocrsp *passocrsp; | |
1721 | struct bss_descriptor *bss; | |
1722 | u16 status_code; | |
1723 | ||
1724 | lbs_deb_enter(LBS_DEB_ASSOC); | |
1725 | ||
1726 | if (!priv->in_progress_assoc_req) { | |
1727 | lbs_deb_assoc("ASSOC_RESP: no in-progress assoc request\n"); | |
1728 | ret = -1; | |
1729 | goto done; | |
1730 | } | |
1731 | bss = &priv->in_progress_assoc_req->bss; | |
1732 | ||
1733 | passocrsp = (struct ieeetypes_assocrsp *) &resp->params; | |
1734 | ||
1735 | /* | |
1736 | * Older FW versions map the IEEE 802.11 Status Code in the association | |
1737 | * response to the following values returned in passocrsp->statuscode: | |
1738 | * | |
1739 | * IEEE Status Code Marvell Status Code | |
1740 | * 0 -> 0x0000 ASSOC_RESULT_SUCCESS | |
1741 | * 13 -> 0x0004 ASSOC_RESULT_AUTH_REFUSED | |
1742 | * 14 -> 0x0004 ASSOC_RESULT_AUTH_REFUSED | |
1743 | * 15 -> 0x0004 ASSOC_RESULT_AUTH_REFUSED | |
1744 | * 16 -> 0x0004 ASSOC_RESULT_AUTH_REFUSED | |
1745 | * others -> 0x0003 ASSOC_RESULT_REFUSED | |
1746 | * | |
1747 | * Other response codes: | |
1748 | * 0x0001 -> ASSOC_RESULT_INVALID_PARAMETERS (unused) | |
1749 | * 0x0002 -> ASSOC_RESULT_TIMEOUT (internal timer expired waiting for | |
1750 | * association response from the AP) | |
1751 | */ | |
1752 | ||
1753 | status_code = le16_to_cpu(passocrsp->statuscode); | |
1754 | switch (status_code) { | |
1755 | case 0x00: | |
1756 | break; | |
1757 | case 0x01: | |
1758 | lbs_deb_assoc("ASSOC_RESP: invalid parameters\n"); | |
1759 | break; | |
1760 | case 0x02: | |
1761 | lbs_deb_assoc("ASSOC_RESP: internal timer " | |
1762 | "expired while waiting for the AP\n"); | |
1763 | break; | |
1764 | case 0x03: | |
1765 | lbs_deb_assoc("ASSOC_RESP: association " | |
1766 | "refused by AP\n"); | |
1767 | break; | |
1768 | case 0x04: | |
1769 | lbs_deb_assoc("ASSOC_RESP: authentication " | |
1770 | "refused by AP\n"); | |
1771 | break; | |
1772 | default: | |
1773 | lbs_deb_assoc("ASSOC_RESP: failure reason 0x%02x " | |
1774 | " unknown\n", status_code); | |
1775 | break; | |
1776 | } | |
1777 | ||
1778 | if (status_code) { | |
1779 | lbs_mac_event_disconnected(priv); | |
1780 | ret = -1; | |
1781 | goto done; | |
1782 | } | |
1783 | ||
1784 | lbs_deb_hex(LBS_DEB_ASSOC, "ASSOC_RESP", (void *)&resp->params, | |
1785 | le16_to_cpu(resp->size) - S_DS_GEN); | |
1786 | ||
1787 | /* Send a Media Connected event, according to the Spec */ | |
1788 | priv->connect_status = LBS_CONNECTED; | |
1789 | ||
1790 | /* Update current SSID and BSSID */ | |
1791 | memcpy(&priv->curbssparams.ssid, &bss->ssid, IW_ESSID_MAX_SIZE); | |
1792 | priv->curbssparams.ssid_len = bss->ssid_len; | |
1793 | memcpy(priv->curbssparams.bssid, bss->bssid, ETH_ALEN); | |
1794 | ||
1795 | priv->SNR[TYPE_RXPD][TYPE_AVG] = 0; | |
1796 | priv->NF[TYPE_RXPD][TYPE_AVG] = 0; | |
1797 | ||
1798 | memset(priv->rawSNR, 0x00, sizeof(priv->rawSNR)); | |
1799 | memset(priv->rawNF, 0x00, sizeof(priv->rawNF)); | |
1800 | priv->nextSNRNF = 0; | |
1801 | priv->numSNRNF = 0; | |
1802 | ||
1803 | netif_carrier_on(priv->dev); | |
1804 | if (!priv->tx_pending_len) | |
1805 | netif_wake_queue(priv->dev); | |
1806 | ||
1807 | memcpy(wrqu.ap_addr.sa_data, priv->curbssparams.bssid, ETH_ALEN); | |
1808 | wrqu.ap_addr.sa_family = ARPHRD_ETHER; | |
1809 | wireless_send_event(priv->dev, SIOCGIWAP, &wrqu, NULL); | |
1810 | ||
1811 | done: | |
1812 | lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret); | |
1813 | return ret; | |
1814 | } | |
1815 | ||
1816 | int lbs_ret_80211_disassociate(struct lbs_private *priv) | |
1817 | { | |
1818 | lbs_deb_enter(LBS_DEB_JOIN); | |
1819 | ||
1820 | lbs_mac_event_disconnected(priv); | |
1821 | ||
1822 | lbs_deb_leave(LBS_DEB_JOIN); | |
1823 | return 0; | |
1824 | } | |
1825 | ||
1826 | int lbs_ret_80211_ad_hoc_start(struct lbs_private *priv, | |
1827 | struct cmd_ds_command *resp) | |
1828 | { | |
1829 | int ret = 0; | |
1830 | u16 command = le16_to_cpu(resp->command); | |
1831 | u16 result = le16_to_cpu(resp->result); | |
1832 | struct cmd_ds_802_11_ad_hoc_result *padhocresult; | |
1833 | union iwreq_data wrqu; | |
1834 | struct bss_descriptor *bss; | |
1835 | DECLARE_MAC_BUF(mac); | |
1836 | ||
1837 | lbs_deb_enter(LBS_DEB_JOIN); | |
1838 | ||
1839 | padhocresult = &resp->params.result; | |
1840 | ||
1841 | lbs_deb_join("ADHOC_RESP: size = %d\n", le16_to_cpu(resp->size)); | |
1842 | lbs_deb_join("ADHOC_RESP: command = %x\n", command); | |
1843 | lbs_deb_join("ADHOC_RESP: result = %x\n", result); | |
1844 | ||
1845 | if (!priv->in_progress_assoc_req) { | |
1846 | lbs_deb_join("ADHOC_RESP: no in-progress association " | |
1847 | "request\n"); | |
1848 | ret = -1; | |
1849 | goto done; | |
1850 | } | |
1851 | bss = &priv->in_progress_assoc_req->bss; | |
1852 | ||
1853 | /* | |
1854 | * Join result code 0 --> SUCCESS | |
1855 | */ | |
1856 | if (result) { | |
1857 | lbs_deb_join("ADHOC_RESP: failed\n"); | |
1858 | if (priv->connect_status == LBS_CONNECTED) | |
1859 | lbs_mac_event_disconnected(priv); | |
1860 | ret = -1; | |
1861 | goto done; | |
1862 | } | |
1863 | ||
1864 | /* | |
1865 | * Now the join cmd should be successful | |
1866 | * If BSSID has changed use SSID to compare instead of BSSID | |
1867 | */ | |
1868 | lbs_deb_join("ADHOC_RESP: associated to '%s'\n", | |
1869 | escape_essid(bss->ssid, bss->ssid_len)); | |
1870 | ||
1871 | /* Send a Media Connected event, according to the Spec */ | |
1872 | priv->connect_status = LBS_CONNECTED; | |
1873 | ||
1874 | if (command == CMD_RET(CMD_802_11_AD_HOC_START)) { | |
1875 | /* Update the created network descriptor with the new BSSID */ | |
1876 | memcpy(bss->bssid, padhocresult->bssid, ETH_ALEN); | |
1877 | } | |
1878 | ||
1879 | /* Set the BSSID from the joined/started descriptor */ | |
1880 | memcpy(&priv->curbssparams.bssid, bss->bssid, ETH_ALEN); | |
1881 | ||
1882 | /* Set the new SSID to current SSID */ | |
1883 | memcpy(&priv->curbssparams.ssid, &bss->ssid, IW_ESSID_MAX_SIZE); | |
1884 | priv->curbssparams.ssid_len = bss->ssid_len; | |
1885 | ||
1886 | netif_carrier_on(priv->dev); | |
1887 | if (!priv->tx_pending_len) | |
1888 | netif_wake_queue(priv->dev); | |
1889 | ||
1890 | memset(&wrqu, 0, sizeof(wrqu)); | |
1891 | memcpy(wrqu.ap_addr.sa_data, priv->curbssparams.bssid, ETH_ALEN); | |
1892 | wrqu.ap_addr.sa_family = ARPHRD_ETHER; | |
1893 | wireless_send_event(priv->dev, SIOCGIWAP, &wrqu, NULL); | |
1894 | ||
1895 | lbs_deb_join("ADHOC_RESP: - Joined/Started Ad Hoc\n"); | |
1896 | lbs_deb_join("ADHOC_RESP: channel = %d\n", priv->curbssparams.channel); | |
1897 | lbs_deb_join("ADHOC_RESP: BSSID = %s\n", | |
1898 | print_mac(mac, padhocresult->bssid)); | |
1899 | ||
1900 | done: | |
1901 | lbs_deb_leave_args(LBS_DEB_JOIN, "ret %d", ret); | |
1902 | return ret; | |
1903 | } | |
1904 | ||
1905 | int lbs_ret_80211_ad_hoc_stop(struct lbs_private *priv) | |
1906 | { | |
1907 | lbs_deb_enter(LBS_DEB_JOIN); | |
1908 | ||
1909 | lbs_mac_event_disconnected(priv); | |
1910 | ||
1911 | lbs_deb_leave(LBS_DEB_JOIN); | |
1912 | return 0; | |
1913 | } |