]>
Commit | Line | Data |
---|---|---|
876c9d3a MT |
1 | /* Copyright (C) 2006, Red Hat, Inc. */ |
2 | ||
3 | #include <linux/bitops.h> | |
4 | #include <net/ieee80211.h> | |
3cf20931 | 5 | #include <linux/etherdevice.h> |
876c9d3a MT |
6 | |
7 | #include "assoc.h" | |
8 | #include "join.h" | |
9 | #include "decl.h" | |
10 | #include "hostcmd.h" | |
11 | #include "host.h" | |
12 | ||
13 | ||
14 | static const u8 bssid_any[ETH_ALEN] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; | |
15 | static const u8 bssid_off[ETH_ALEN] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; | |
16 | ||
e76850d6 DW |
17 | static void print_assoc_req(const char * extra, struct assoc_request * assoc_req) |
18 | { | |
0795af57 | 19 | DECLARE_MAC_BUF(mac); |
e76850d6 DW |
20 | lbs_deb_assoc( |
21 | "#### Association Request: %s\n" | |
22 | " flags: 0x%08lX\n" | |
23 | " SSID: '%s'\n" | |
24 | " channel: %d\n" | |
25 | " band: %d\n" | |
26 | " mode: %d\n" | |
0795af57 | 27 | " BSSID: %s\n" |
785e8f26 DW |
28 | " Encryption:%s%s%s\n" |
29 | " auth: %d\n", | |
e76850d6 | 30 | extra, assoc_req->flags, |
d8efea25 | 31 | escape_essid(assoc_req->ssid, assoc_req->ssid_len), |
e76850d6 | 32 | assoc_req->channel, assoc_req->band, assoc_req->mode, |
0795af57 | 33 | print_mac(mac, assoc_req->bssid), |
785e8f26 DW |
34 | assoc_req->secinfo.WPAenabled ? " WPA" : "", |
35 | assoc_req->secinfo.WPA2enabled ? " WPA2" : "", | |
36 | assoc_req->secinfo.wep_enabled ? " WEP" : "", | |
37 | assoc_req->secinfo.auth_mode); | |
e76850d6 DW |
38 | } |
39 | ||
40 | ||
876c9d3a MT |
41 | static int assoc_helper_essid(wlan_private *priv, |
42 | struct assoc_request * assoc_req) | |
43 | { | |
44 | wlan_adapter *adapter = priv->adapter; | |
45 | int ret = 0; | |
fcdb53db | 46 | struct bss_descriptor * bss; |
aeea0ab4 | 47 | int channel = -1; |
876c9d3a | 48 | |
9012b28a | 49 | lbs_deb_enter(LBS_DEB_ASSOC); |
876c9d3a | 50 | |
ef9a264b DW |
51 | /* FIXME: take channel into account when picking SSIDs if a channel |
52 | * is set. | |
53 | */ | |
54 | ||
aeea0ab4 DW |
55 | if (test_bit(ASSOC_FLAG_CHANNEL, &assoc_req->flags)) |
56 | channel = assoc_req->channel; | |
57 | ||
d8efea25 DW |
58 | lbs_deb_assoc("New SSID requested: '%s'\n", |
59 | escape_essid(assoc_req->ssid, assoc_req->ssid_len)); | |
0dc5a290 | 60 | if (assoc_req->mode == IW_MODE_INFRA) { |
6e22a855 HS |
61 | libertas_send_specific_ssid_scan(priv, assoc_req->ssid, |
62 | assoc_req->ssid_len, 0); | |
876c9d3a | 63 | |
717c9339 | 64 | bss = libertas_find_ssid_in_list(adapter, assoc_req->ssid, |
d8efea25 | 65 | assoc_req->ssid_len, NULL, IW_MODE_INFRA, channel); |
fcdb53db DW |
66 | if (bss != NULL) { |
67 | lbs_deb_assoc("SSID found in scan list, associating\n"); | |
e76850d6 DW |
68 | memcpy(&assoc_req->bss, bss, sizeof(struct bss_descriptor)); |
69 | ret = wlan_associate(priv, assoc_req); | |
876c9d3a | 70 | } else { |
d8efea25 | 71 | lbs_deb_assoc("SSID not found; cannot associate\n"); |
876c9d3a | 72 | } |
0dc5a290 | 73 | } else if (assoc_req->mode == IW_MODE_ADHOC) { |
876c9d3a MT |
74 | /* Scan for the network, do not save previous results. Stale |
75 | * scan data will cause us to join a non-existant adhoc network | |
76 | */ | |
717c9339 | 77 | libertas_send_specific_ssid_scan(priv, assoc_req->ssid, |
d8efea25 | 78 | assoc_req->ssid_len, 1); |
876c9d3a MT |
79 | |
80 | /* Search for the requested SSID in the scan table */ | |
717c9339 | 81 | bss = libertas_find_ssid_in_list(adapter, assoc_req->ssid, |
d8efea25 | 82 | assoc_req->ssid_len, NULL, IW_MODE_ADHOC, channel); |
fcdb53db | 83 | if (bss != NULL) { |
d8efea25 | 84 | lbs_deb_assoc("SSID found, will join\n"); |
e76850d6 DW |
85 | memcpy(&assoc_req->bss, bss, sizeof(struct bss_descriptor)); |
86 | libertas_join_adhoc_network(priv, assoc_req); | |
876c9d3a MT |
87 | } else { |
88 | /* else send START command */ | |
d8efea25 | 89 | lbs_deb_assoc("SSID not found, creating adhoc network\n"); |
e76850d6 | 90 | memcpy(&assoc_req->bss.ssid, &assoc_req->ssid, |
d8efea25 DW |
91 | IW_ESSID_MAX_SIZE); |
92 | assoc_req->bss.ssid_len = assoc_req->ssid_len; | |
e76850d6 | 93 | libertas_start_adhoc_network(priv, assoc_req); |
876c9d3a | 94 | } |
876c9d3a MT |
95 | } |
96 | ||
9012b28a | 97 | lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret); |
876c9d3a MT |
98 | return ret; |
99 | } | |
100 | ||
101 | ||
102 | static int assoc_helper_bssid(wlan_private *priv, | |
103 | struct assoc_request * assoc_req) | |
104 | { | |
105 | wlan_adapter *adapter = priv->adapter; | |
fcdb53db DW |
106 | int ret = 0; |
107 | struct bss_descriptor * bss; | |
0795af57 | 108 | DECLARE_MAC_BUF(mac); |
876c9d3a | 109 | |
0795af57 JP |
110 | lbs_deb_enter_args(LBS_DEB_ASSOC, "BSSID %s", |
111 | print_mac(mac, assoc_req->bssid)); | |
876c9d3a MT |
112 | |
113 | /* Search for index position in list for requested MAC */ | |
717c9339 | 114 | bss = libertas_find_bssid_in_list(adapter, assoc_req->bssid, |
876c9d3a | 115 | assoc_req->mode); |
fcdb53db | 116 | if (bss == NULL) { |
0795af57 JP |
117 | lbs_deb_assoc("ASSOC: WAP: BSSID %s not found, " |
118 | "cannot associate.\n", print_mac(mac, assoc_req->bssid)); | |
876c9d3a MT |
119 | goto out; |
120 | } | |
121 | ||
e76850d6 | 122 | memcpy(&assoc_req->bss, bss, sizeof(struct bss_descriptor)); |
0dc5a290 | 123 | if (assoc_req->mode == IW_MODE_INFRA) { |
e76850d6 | 124 | ret = wlan_associate(priv, assoc_req); |
fcdb53db | 125 | lbs_deb_assoc("ASSOC: wlan_associate(bssid) returned %d\n", ret); |
0dc5a290 | 126 | } else if (assoc_req->mode == IW_MODE_ADHOC) { |
e76850d6 | 127 | libertas_join_adhoc_network(priv, assoc_req); |
876c9d3a | 128 | } |
876c9d3a MT |
129 | |
130 | out: | |
9012b28a | 131 | lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret); |
876c9d3a MT |
132 | return ret; |
133 | } | |
134 | ||
135 | ||
136 | static int assoc_helper_associate(wlan_private *priv, | |
137 | struct assoc_request * assoc_req) | |
138 | { | |
139 | int ret = 0, done = 0; | |
140 | ||
141 | /* If we're given and 'any' BSSID, try associating based on SSID */ | |
142 | ||
143 | if (test_bit(ASSOC_FLAG_BSSID, &assoc_req->flags)) { | |
3cf20931 DW |
144 | if (compare_ether_addr(bssid_any, assoc_req->bssid) |
145 | && compare_ether_addr(bssid_off, assoc_req->bssid)) { | |
876c9d3a MT |
146 | ret = assoc_helper_bssid(priv, assoc_req); |
147 | done = 1; | |
148 | if (ret) { | |
9012b28a | 149 | lbs_deb_assoc("ASSOC: bssid: ret = %d\n", ret); |
876c9d3a MT |
150 | } |
151 | } | |
152 | } | |
153 | ||
154 | if (!done && test_bit(ASSOC_FLAG_SSID, &assoc_req->flags)) { | |
155 | ret = assoc_helper_essid(priv, assoc_req); | |
156 | if (ret) { | |
9012b28a | 157 | lbs_deb_assoc("ASSOC: bssid: ret = %d\n", ret); |
876c9d3a MT |
158 | } |
159 | } | |
160 | ||
161 | return ret; | |
162 | } | |
163 | ||
164 | ||
165 | static int assoc_helper_mode(wlan_private *priv, | |
166 | struct assoc_request * assoc_req) | |
167 | { | |
168 | wlan_adapter *adapter = priv->adapter; | |
169 | int ret = 0; | |
170 | ||
9012b28a | 171 | lbs_deb_enter(LBS_DEB_ASSOC); |
876c9d3a | 172 | |
9012b28a HS |
173 | if (assoc_req->mode == adapter->mode) |
174 | goto done; | |
876c9d3a | 175 | |
0dc5a290 | 176 | if (assoc_req->mode == IW_MODE_INFRA) { |
876c9d3a | 177 | if (adapter->psstate != PS_STATE_FULL_POWER) |
0aef64d7 DW |
178 | libertas_ps_wakeup(priv, CMD_OPTION_WAITFORRSP); |
179 | adapter->psmode = WLAN802_11POWERMODECAM; | |
876c9d3a MT |
180 | } |
181 | ||
0dc5a290 | 182 | adapter->mode = assoc_req->mode; |
876c9d3a | 183 | ret = libertas_prepare_and_send_command(priv, |
0aef64d7 DW |
184 | CMD_802_11_SNMP_MIB, |
185 | 0, CMD_OPTION_WAITFORRSP, | |
876c9d3a | 186 | OID_802_11_INFRASTRUCTURE_MODE, |
981f187b | 187 | /* Shoot me now */ (void *) (size_t) assoc_req->mode); |
876c9d3a | 188 | |
9012b28a HS |
189 | done: |
190 | lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret); | |
876c9d3a MT |
191 | return ret; |
192 | } | |
193 | ||
194 | ||
ef9a264b DW |
195 | static int update_channel(wlan_private * priv) |
196 | { | |
197 | /* the channel in f/w could be out of sync, get the current channel */ | |
0aef64d7 DW |
198 | return libertas_prepare_and_send_command(priv, CMD_802_11_RF_CHANNEL, |
199 | CMD_OPT_802_11_RF_CHANNEL_GET, | |
200 | CMD_OPTION_WAITFORRSP, 0, NULL); | |
ef9a264b DW |
201 | } |
202 | ||
b8bedefd LCCR |
203 | void libertas_sync_channel(struct work_struct *work) |
204 | { | |
205 | wlan_private *priv = container_of(work, wlan_private, sync_channel); | |
206 | ||
207 | if (update_channel(priv) != 0) | |
208 | lbs_pr_info("Channel synchronization failed."); | |
209 | } | |
210 | ||
ef9a264b DW |
211 | static int assoc_helper_channel(wlan_private *priv, |
212 | struct assoc_request * assoc_req) | |
213 | { | |
214 | wlan_adapter *adapter = priv->adapter; | |
215 | int ret = 0; | |
216 | ||
217 | lbs_deb_enter(LBS_DEB_ASSOC); | |
218 | ||
219 | ret = update_channel(priv); | |
220 | if (ret < 0) { | |
221 | lbs_deb_assoc("ASSOC: channel: error getting channel."); | |
222 | } | |
223 | ||
224 | if (assoc_req->channel == adapter->curbssparams.channel) | |
225 | goto done; | |
226 | ||
227 | lbs_deb_assoc("ASSOC: channel: %d -> %d\n", | |
228 | adapter->curbssparams.channel, assoc_req->channel); | |
229 | ||
0aef64d7 DW |
230 | ret = libertas_prepare_and_send_command(priv, CMD_802_11_RF_CHANNEL, |
231 | CMD_OPT_802_11_RF_CHANNEL_SET, | |
232 | CMD_OPTION_WAITFORRSP, 0, &assoc_req->channel); | |
ef9a264b DW |
233 | if (ret < 0) { |
234 | lbs_deb_assoc("ASSOC: channel: error setting channel."); | |
235 | } | |
236 | ||
237 | ret = update_channel(priv); | |
238 | if (ret < 0) { | |
239 | lbs_deb_assoc("ASSOC: channel: error getting channel."); | |
240 | } | |
241 | ||
242 | if (assoc_req->channel != adapter->curbssparams.channel) { | |
243 | lbs_deb_assoc("ASSOC: channel: failed to update channel to %d", | |
244 | assoc_req->channel); | |
245 | goto done; | |
246 | } | |
247 | ||
248 | if ( assoc_req->secinfo.wep_enabled | |
249 | && (assoc_req->wep_keys[0].len | |
250 | || assoc_req->wep_keys[1].len | |
251 | || assoc_req->wep_keys[2].len | |
252 | || assoc_req->wep_keys[3].len)) { | |
253 | /* Make sure WEP keys are re-sent to firmware */ | |
254 | set_bit(ASSOC_FLAG_WEP_KEYS, &assoc_req->flags); | |
255 | } | |
256 | ||
257 | /* Must restart/rejoin adhoc networks after channel change */ | |
258 | set_bit(ASSOC_FLAG_SSID, &assoc_req->flags); | |
259 | ||
260 | done: | |
261 | lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret); | |
262 | return ret; | |
263 | } | |
264 | ||
265 | ||
876c9d3a MT |
266 | static int assoc_helper_wep_keys(wlan_private *priv, |
267 | struct assoc_request * assoc_req) | |
268 | { | |
269 | wlan_adapter *adapter = priv->adapter; | |
270 | int i; | |
271 | int ret = 0; | |
272 | ||
9012b28a | 273 | lbs_deb_enter(LBS_DEB_ASSOC); |
876c9d3a MT |
274 | |
275 | /* Set or remove WEP keys */ | |
276 | if ( assoc_req->wep_keys[0].len | |
277 | || assoc_req->wep_keys[1].len | |
278 | || assoc_req->wep_keys[2].len | |
279 | || assoc_req->wep_keys[3].len) { | |
280 | ret = libertas_prepare_and_send_command(priv, | |
0aef64d7 DW |
281 | CMD_802_11_SET_WEP, |
282 | CMD_ACT_ADD, | |
283 | CMD_OPTION_WAITFORRSP, | |
876c9d3a MT |
284 | 0, assoc_req); |
285 | } else { | |
286 | ret = libertas_prepare_and_send_command(priv, | |
0aef64d7 DW |
287 | CMD_802_11_SET_WEP, |
288 | CMD_ACT_REMOVE, | |
289 | CMD_OPTION_WAITFORRSP, | |
876c9d3a MT |
290 | 0, NULL); |
291 | } | |
292 | ||
293 | if (ret) | |
294 | goto out; | |
295 | ||
296 | /* enable/disable the MAC's WEP packet filter */ | |
889c05bd | 297 | if (assoc_req->secinfo.wep_enabled) |
0aef64d7 | 298 | adapter->currentpacketfilter |= CMD_ACT_MAC_WEP_ENABLE; |
876c9d3a | 299 | else |
0aef64d7 | 300 | adapter->currentpacketfilter &= ~CMD_ACT_MAC_WEP_ENABLE; |
876c9d3a MT |
301 | ret = libertas_set_mac_packet_filter(priv); |
302 | if (ret) | |
303 | goto out; | |
304 | ||
305 | mutex_lock(&adapter->lock); | |
306 | ||
307 | /* Copy WEP keys into adapter wep key fields */ | |
308 | for (i = 0; i < 4; i++) { | |
309 | memcpy(&adapter->wep_keys[i], &assoc_req->wep_keys[i], | |
1443b653 | 310 | sizeof(struct enc_key)); |
876c9d3a MT |
311 | } |
312 | adapter->wep_tx_keyidx = assoc_req->wep_tx_keyidx; | |
313 | ||
314 | mutex_unlock(&adapter->lock); | |
315 | ||
316 | out: | |
9012b28a | 317 | lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret); |
876c9d3a MT |
318 | return ret; |
319 | } | |
320 | ||
321 | static int assoc_helper_secinfo(wlan_private *priv, | |
322 | struct assoc_request * assoc_req) | |
323 | { | |
324 | wlan_adapter *adapter = priv->adapter; | |
325 | int ret = 0; | |
18c96c34 DW |
326 | u32 do_wpa; |
327 | u32 rsn = 0; | |
876c9d3a | 328 | |
9012b28a | 329 | lbs_deb_enter(LBS_DEB_ASSOC); |
876c9d3a MT |
330 | |
331 | memcpy(&adapter->secinfo, &assoc_req->secinfo, | |
332 | sizeof(struct wlan_802_11_security)); | |
333 | ||
334 | ret = libertas_set_mac_packet_filter(priv); | |
90a42210 DW |
335 | if (ret) |
336 | goto out; | |
876c9d3a | 337 | |
18c96c34 DW |
338 | /* If RSN is already enabled, don't try to enable it again, since |
339 | * ENABLE_RSN resets internal state machines and will clobber the | |
340 | * 4-way WPA handshake. | |
341 | */ | |
342 | ||
343 | /* Get RSN enabled/disabled */ | |
90a42210 | 344 | ret = libertas_prepare_and_send_command(priv, |
0aef64d7 DW |
345 | CMD_802_11_ENABLE_RSN, |
346 | CMD_ACT_GET, | |
347 | CMD_OPTION_WAITFORRSP, | |
18c96c34 DW |
348 | 0, &rsn); |
349 | if (ret) { | |
350 | lbs_deb_assoc("Failed to get RSN status: %d", ret); | |
351 | goto out; | |
352 | } | |
353 | ||
354 | /* Don't re-enable RSN if it's already enabled */ | |
355 | do_wpa = (assoc_req->secinfo.WPAenabled || assoc_req->secinfo.WPA2enabled); | |
356 | if (do_wpa == rsn) | |
357 | goto out; | |
358 | ||
359 | /* Set RSN enabled/disabled */ | |
360 | rsn = do_wpa; | |
361 | ret = libertas_prepare_and_send_command(priv, | |
0aef64d7 DW |
362 | CMD_802_11_ENABLE_RSN, |
363 | CMD_ACT_SET, | |
364 | CMD_OPTION_WAITFORRSP, | |
18c96c34 | 365 | 0, &rsn); |
90a42210 DW |
366 | |
367 | out: | |
9012b28a | 368 | lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret); |
876c9d3a MT |
369 | return ret; |
370 | } | |
371 | ||
372 | ||
373 | static int assoc_helper_wpa_keys(wlan_private *priv, | |
374 | struct assoc_request * assoc_req) | |
375 | { | |
376 | int ret = 0; | |
377 | ||
9012b28a | 378 | lbs_deb_enter(LBS_DEB_ASSOC); |
876c9d3a | 379 | |
876c9d3a | 380 | ret = libertas_prepare_and_send_command(priv, |
0aef64d7 DW |
381 | CMD_802_11_KEY_MATERIAL, |
382 | CMD_ACT_SET, | |
383 | CMD_OPTION_WAITFORRSP, | |
876c9d3a MT |
384 | 0, assoc_req); |
385 | ||
9012b28a | 386 | lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret); |
876c9d3a MT |
387 | return ret; |
388 | } | |
389 | ||
390 | ||
391 | static int assoc_helper_wpa_ie(wlan_private *priv, | |
392 | struct assoc_request * assoc_req) | |
393 | { | |
394 | wlan_adapter *adapter = priv->adapter; | |
395 | int ret = 0; | |
396 | ||
9012b28a | 397 | lbs_deb_enter(LBS_DEB_ASSOC); |
876c9d3a MT |
398 | |
399 | if (assoc_req->secinfo.WPAenabled || assoc_req->secinfo.WPA2enabled) { | |
400 | memcpy(&adapter->wpa_ie, &assoc_req->wpa_ie, assoc_req->wpa_ie_len); | |
401 | adapter->wpa_ie_len = assoc_req->wpa_ie_len; | |
402 | } else { | |
403 | memset(&adapter->wpa_ie, 0, MAX_WPA_IE_LEN); | |
404 | adapter->wpa_ie_len = 0; | |
405 | } | |
406 | ||
9012b28a | 407 | lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret); |
876c9d3a MT |
408 | return ret; |
409 | } | |
410 | ||
411 | ||
412 | static int should_deauth_infrastructure(wlan_adapter *adapter, | |
413 | struct assoc_request * assoc_req) | |
414 | { | |
0aef64d7 | 415 | if (adapter->connect_status != LIBERTAS_CONNECTED) |
876c9d3a MT |
416 | return 0; |
417 | ||
418 | if (test_bit(ASSOC_FLAG_SSID, &assoc_req->flags)) { | |
9012b28a | 419 | lbs_deb_assoc("Deauthenticating due to new SSID in " |
876c9d3a MT |
420 | " configuration request.\n"); |
421 | return 1; | |
422 | } | |
423 | ||
424 | if (test_bit(ASSOC_FLAG_SECINFO, &assoc_req->flags)) { | |
6affe785 | 425 | if (adapter->secinfo.auth_mode != assoc_req->secinfo.auth_mode) { |
9012b28a | 426 | lbs_deb_assoc("Deauthenticating due to updated security " |
876c9d3a MT |
427 | "info in configuration request.\n"); |
428 | return 1; | |
429 | } | |
430 | } | |
431 | ||
432 | if (test_bit(ASSOC_FLAG_BSSID, &assoc_req->flags)) { | |
9012b28a | 433 | lbs_deb_assoc("Deauthenticating due to new BSSID in " |
876c9d3a MT |
434 | " configuration request.\n"); |
435 | return 1; | |
436 | } | |
437 | ||
fff47f10 LCCR |
438 | if (test_bit(ASSOC_FLAG_CHANNEL, &assoc_req->flags)) { |
439 | lbs_deb_assoc("Deauthenticating due to channel switch.\n"); | |
440 | return 1; | |
441 | } | |
442 | ||
876c9d3a MT |
443 | /* FIXME: deal with 'auto' mode somehow */ |
444 | if (test_bit(ASSOC_FLAG_MODE, &assoc_req->flags)) { | |
0dc5a290 | 445 | if (assoc_req->mode != IW_MODE_INFRA) |
876c9d3a MT |
446 | return 1; |
447 | } | |
448 | ||
449 | return 0; | |
450 | } | |
451 | ||
452 | ||
453 | static int should_stop_adhoc(wlan_adapter *adapter, | |
454 | struct assoc_request * assoc_req) | |
455 | { | |
0aef64d7 | 456 | if (adapter->connect_status != LIBERTAS_CONNECTED) |
876c9d3a MT |
457 | return 0; |
458 | ||
717c9339 | 459 | if (libertas_ssid_cmp(adapter->curbssparams.ssid, |
d8efea25 DW |
460 | adapter->curbssparams.ssid_len, |
461 | assoc_req->ssid, assoc_req->ssid_len) != 0) | |
876c9d3a MT |
462 | return 1; |
463 | ||
464 | /* FIXME: deal with 'auto' mode somehow */ | |
465 | if (test_bit(ASSOC_FLAG_MODE, &assoc_req->flags)) { | |
0dc5a290 | 466 | if (assoc_req->mode != IW_MODE_ADHOC) |
876c9d3a MT |
467 | return 1; |
468 | } | |
469 | ||
ef9a264b DW |
470 | if (test_bit(ASSOC_FLAG_CHANNEL, &assoc_req->flags)) { |
471 | if (assoc_req->channel != adapter->curbssparams.channel) | |
472 | return 1; | |
473 | } | |
474 | ||
876c9d3a MT |
475 | return 0; |
476 | } | |
477 | ||
478 | ||
eb3ce631 | 479 | void libertas_association_worker(struct work_struct *work) |
876c9d3a MT |
480 | { |
481 | wlan_private *priv = container_of(work, wlan_private, assoc_work.work); | |
482 | wlan_adapter *adapter = priv->adapter; | |
483 | struct assoc_request * assoc_req = NULL; | |
484 | int ret = 0; | |
485 | int find_any_ssid = 0; | |
0795af57 | 486 | DECLARE_MAC_BUF(mac); |
876c9d3a | 487 | |
9012b28a | 488 | lbs_deb_enter(LBS_DEB_ASSOC); |
876c9d3a MT |
489 | |
490 | mutex_lock(&adapter->lock); | |
e76850d6 DW |
491 | assoc_req = adapter->pending_assoc_req; |
492 | adapter->pending_assoc_req = NULL; | |
493 | adapter->in_progress_assoc_req = assoc_req; | |
876c9d3a MT |
494 | mutex_unlock(&adapter->lock); |
495 | ||
9012b28a HS |
496 | if (!assoc_req) |
497 | goto done; | |
876c9d3a | 498 | |
e76850d6 | 499 | print_assoc_req(__func__, assoc_req); |
876c9d3a MT |
500 | |
501 | /* If 'any' SSID was specified, find an SSID to associate with */ | |
502 | if (test_bit(ASSOC_FLAG_SSID, &assoc_req->flags) | |
d8efea25 | 503 | && !assoc_req->ssid_len) |
876c9d3a MT |
504 | find_any_ssid = 1; |
505 | ||
506 | /* But don't use 'any' SSID if there's a valid locked BSSID to use */ | |
507 | if (test_bit(ASSOC_FLAG_BSSID, &assoc_req->flags)) { | |
3cf20931 DW |
508 | if (compare_ether_addr(assoc_req->bssid, bssid_any) |
509 | && compare_ether_addr(assoc_req->bssid, bssid_off)) | |
876c9d3a MT |
510 | find_any_ssid = 0; |
511 | } | |
512 | ||
513 | if (find_any_ssid) { | |
0dc5a290 | 514 | u8 new_mode; |
876c9d3a | 515 | |
717c9339 | 516 | ret = libertas_find_best_network_ssid(priv, assoc_req->ssid, |
d8efea25 | 517 | &assoc_req->ssid_len, assoc_req->mode, &new_mode); |
876c9d3a | 518 | if (ret) { |
9012b28a | 519 | lbs_deb_assoc("Could not find best network\n"); |
876c9d3a MT |
520 | ret = -ENETUNREACH; |
521 | goto out; | |
522 | } | |
523 | ||
524 | /* Ensure we switch to the mode of the AP */ | |
0dc5a290 | 525 | if (assoc_req->mode == IW_MODE_AUTO) { |
876c9d3a MT |
526 | set_bit(ASSOC_FLAG_MODE, &assoc_req->flags); |
527 | assoc_req->mode = new_mode; | |
528 | } | |
529 | } | |
530 | ||
531 | /* | |
532 | * Check if the attributes being changing require deauthentication | |
533 | * from the currently associated infrastructure access point. | |
534 | */ | |
0dc5a290 | 535 | if (adapter->mode == IW_MODE_INFRA) { |
876c9d3a MT |
536 | if (should_deauth_infrastructure(adapter, assoc_req)) { |
537 | ret = libertas_send_deauthentication(priv); | |
538 | if (ret) { | |
9012b28a | 539 | lbs_deb_assoc("Deauthentication due to new " |
876c9d3a MT |
540 | "configuration request failed: %d\n", |
541 | ret); | |
542 | } | |
543 | } | |
0dc5a290 | 544 | } else if (adapter->mode == IW_MODE_ADHOC) { |
876c9d3a MT |
545 | if (should_stop_adhoc(adapter, assoc_req)) { |
546 | ret = libertas_stop_adhoc_network(priv); | |
547 | if (ret) { | |
9012b28a | 548 | lbs_deb_assoc("Teardown of AdHoc network due to " |
876c9d3a MT |
549 | "new configuration request failed: %d\n", |
550 | ret); | |
551 | } | |
552 | ||
553 | } | |
554 | } | |
555 | ||
556 | /* Send the various configuration bits to the firmware */ | |
557 | if (test_bit(ASSOC_FLAG_MODE, &assoc_req->flags)) { | |
558 | ret = assoc_helper_mode(priv, assoc_req); | |
559 | if (ret) { | |
2950cd26 DW |
560 | lbs_deb_assoc("ASSOC(:%d) mode: ret = %d\n", |
561 | __LINE__, ret); | |
876c9d3a MT |
562 | goto out; |
563 | } | |
564 | } | |
565 | ||
ef9a264b DW |
566 | if (test_bit(ASSOC_FLAG_CHANNEL, &assoc_req->flags)) { |
567 | ret = assoc_helper_channel(priv, assoc_req); | |
568 | if (ret) { | |
569 | lbs_deb_assoc("ASSOC(:%d) channel: ret = %d\n", | |
570 | __LINE__, ret); | |
571 | goto out; | |
572 | } | |
573 | } | |
574 | ||
876c9d3a MT |
575 | if ( test_bit(ASSOC_FLAG_WEP_KEYS, &assoc_req->flags) |
576 | || test_bit(ASSOC_FLAG_WEP_TX_KEYIDX, &assoc_req->flags)) { | |
577 | ret = assoc_helper_wep_keys(priv, assoc_req); | |
578 | if (ret) { | |
2950cd26 DW |
579 | lbs_deb_assoc("ASSOC(:%d) wep_keys: ret = %d\n", |
580 | __LINE__, ret); | |
876c9d3a MT |
581 | goto out; |
582 | } | |
583 | } | |
584 | ||
585 | if (test_bit(ASSOC_FLAG_SECINFO, &assoc_req->flags)) { | |
586 | ret = assoc_helper_secinfo(priv, assoc_req); | |
587 | if (ret) { | |
2950cd26 DW |
588 | lbs_deb_assoc("ASSOC(:%d) secinfo: ret = %d\n", |
589 | __LINE__, ret); | |
876c9d3a MT |
590 | goto out; |
591 | } | |
592 | } | |
593 | ||
594 | if (test_bit(ASSOC_FLAG_WPA_IE, &assoc_req->flags)) { | |
595 | ret = assoc_helper_wpa_ie(priv, assoc_req); | |
596 | if (ret) { | |
2950cd26 DW |
597 | lbs_deb_assoc("ASSOC(:%d) wpa_ie: ret = %d\n", |
598 | __LINE__, ret); | |
876c9d3a MT |
599 | goto out; |
600 | } | |
601 | } | |
602 | ||
603 | if (test_bit(ASSOC_FLAG_WPA_MCAST_KEY, &assoc_req->flags) | |
604 | || test_bit(ASSOC_FLAG_WPA_UCAST_KEY, &assoc_req->flags)) { | |
605 | ret = assoc_helper_wpa_keys(priv, assoc_req); | |
606 | if (ret) { | |
2950cd26 DW |
607 | lbs_deb_assoc("ASSOC(:%d) wpa_keys: ret = %d\n", |
608 | __LINE__, ret); | |
876c9d3a MT |
609 | goto out; |
610 | } | |
611 | } | |
612 | ||
613 | /* SSID/BSSID should be the _last_ config option set, because they | |
614 | * trigger the association attempt. | |
615 | */ | |
616 | if (test_bit(ASSOC_FLAG_BSSID, &assoc_req->flags) | |
617 | || test_bit(ASSOC_FLAG_SSID, &assoc_req->flags)) { | |
618 | int success = 1; | |
619 | ||
620 | ret = assoc_helper_associate(priv, assoc_req); | |
621 | if (ret) { | |
9012b28a | 622 | lbs_deb_assoc("ASSOC: association attempt unsuccessful: %d\n", |
876c9d3a MT |
623 | ret); |
624 | success = 0; | |
625 | } | |
626 | ||
0aef64d7 | 627 | if (adapter->connect_status != LIBERTAS_CONNECTED) { |
81173e34 | 628 | lbs_deb_assoc("ASSOC: association attempt unsuccessful, " |
876c9d3a MT |
629 | "not connected.\n"); |
630 | success = 0; | |
631 | } | |
632 | ||
633 | if (success) { | |
9012b28a | 634 | lbs_deb_assoc("ASSOC: association attempt successful. " |
0795af57 | 635 | "Associated to '%s' (%s)\n", |
d8efea25 DW |
636 | escape_essid(adapter->curbssparams.ssid, |
637 | adapter->curbssparams.ssid_len), | |
0795af57 | 638 | print_mac(mac, adapter->curbssparams.bssid)); |
876c9d3a | 639 | libertas_prepare_and_send_command(priv, |
0aef64d7 DW |
640 | CMD_802_11_RSSI, |
641 | 0, CMD_OPTION_WAITFORRSP, 0, NULL); | |
876c9d3a MT |
642 | |
643 | libertas_prepare_and_send_command(priv, | |
0aef64d7 DW |
644 | CMD_802_11_GET_LOG, |
645 | 0, CMD_OPTION_WAITFORRSP, 0, NULL); | |
876c9d3a | 646 | } else { |
876c9d3a MT |
647 | ret = -1; |
648 | } | |
649 | } | |
650 | ||
651 | out: | |
652 | if (ret) { | |
9012b28a | 653 | lbs_deb_assoc("ASSOC: reconfiguration attempt unsuccessful: %d\n", |
876c9d3a MT |
654 | ret); |
655 | } | |
e76850d6 DW |
656 | |
657 | mutex_lock(&adapter->lock); | |
658 | adapter->in_progress_assoc_req = NULL; | |
659 | mutex_unlock(&adapter->lock); | |
876c9d3a | 660 | kfree(assoc_req); |
9012b28a HS |
661 | |
662 | done: | |
663 | lbs_deb_leave(LBS_DEB_ASSOC); | |
876c9d3a MT |
664 | } |
665 | ||
666 | ||
667 | /* | |
668 | * Caller MUST hold any necessary locks | |
669 | */ | |
670 | struct assoc_request * wlan_get_association_request(wlan_adapter *adapter) | |
671 | { | |
672 | struct assoc_request * assoc_req; | |
673 | ||
e76850d6 DW |
674 | if (!adapter->pending_assoc_req) { |
675 | adapter->pending_assoc_req = kzalloc(sizeof(struct assoc_request), | |
676 | GFP_KERNEL); | |
677 | if (!adapter->pending_assoc_req) { | |
876c9d3a MT |
678 | lbs_pr_info("Not enough memory to allocate association" |
679 | " request!\n"); | |
680 | return NULL; | |
681 | } | |
682 | } | |
683 | ||
684 | /* Copy current configuration attributes to the association request, | |
685 | * but don't overwrite any that are already set. | |
686 | */ | |
e76850d6 | 687 | assoc_req = adapter->pending_assoc_req; |
876c9d3a | 688 | if (!test_bit(ASSOC_FLAG_SSID, &assoc_req->flags)) { |
e76850d6 | 689 | memcpy(&assoc_req->ssid, &adapter->curbssparams.ssid, |
d8efea25 DW |
690 | IW_ESSID_MAX_SIZE); |
691 | assoc_req->ssid_len = adapter->curbssparams.ssid_len; | |
876c9d3a MT |
692 | } |
693 | ||
694 | if (!test_bit(ASSOC_FLAG_CHANNEL, &assoc_req->flags)) | |
695 | assoc_req->channel = adapter->curbssparams.channel; | |
696 | ||
e76850d6 DW |
697 | if (!test_bit(ASSOC_FLAG_BAND, &assoc_req->flags)) |
698 | assoc_req->band = adapter->curbssparams.band; | |
699 | ||
876c9d3a | 700 | if (!test_bit(ASSOC_FLAG_MODE, &assoc_req->flags)) |
0dc5a290 | 701 | assoc_req->mode = adapter->mode; |
876c9d3a MT |
702 | |
703 | if (!test_bit(ASSOC_FLAG_BSSID, &assoc_req->flags)) { | |
704 | memcpy(&assoc_req->bssid, adapter->curbssparams.bssid, | |
705 | ETH_ALEN); | |
706 | } | |
707 | ||
708 | if (!test_bit(ASSOC_FLAG_WEP_KEYS, &assoc_req->flags)) { | |
709 | int i; | |
710 | for (i = 0; i < 4; i++) { | |
711 | memcpy(&assoc_req->wep_keys[i], &adapter->wep_keys[i], | |
1443b653 | 712 | sizeof(struct enc_key)); |
876c9d3a MT |
713 | } |
714 | } | |
715 | ||
716 | if (!test_bit(ASSOC_FLAG_WEP_TX_KEYIDX, &assoc_req->flags)) | |
717 | assoc_req->wep_tx_keyidx = adapter->wep_tx_keyidx; | |
718 | ||
719 | if (!test_bit(ASSOC_FLAG_WPA_MCAST_KEY, &assoc_req->flags)) { | |
720 | memcpy(&assoc_req->wpa_mcast_key, &adapter->wpa_mcast_key, | |
1443b653 | 721 | sizeof(struct enc_key)); |
876c9d3a MT |
722 | } |
723 | ||
724 | if (!test_bit(ASSOC_FLAG_WPA_UCAST_KEY, &assoc_req->flags)) { | |
725 | memcpy(&assoc_req->wpa_unicast_key, &adapter->wpa_unicast_key, | |
1443b653 | 726 | sizeof(struct enc_key)); |
876c9d3a MT |
727 | } |
728 | ||
729 | if (!test_bit(ASSOC_FLAG_SECINFO, &assoc_req->flags)) { | |
730 | memcpy(&assoc_req->secinfo, &adapter->secinfo, | |
731 | sizeof(struct wlan_802_11_security)); | |
732 | } | |
733 | ||
734 | if (!test_bit(ASSOC_FLAG_WPA_IE, &assoc_req->flags)) { | |
735 | memcpy(&assoc_req->wpa_ie, &adapter->wpa_ie, | |
736 | MAX_WPA_IE_LEN); | |
737 | assoc_req->wpa_ie_len = adapter->wpa_ie_len; | |
738 | } | |
739 | ||
e76850d6 DW |
740 | print_assoc_req(__func__, assoc_req); |
741 | ||
876c9d3a MT |
742 | return assoc_req; |
743 | } |