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