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