[PATCH] libertas: move channel changing into association framework
[pv_ops_mirror.git] / drivers / net / wireless / libertas / assoc.c
blob3f2dfaf879c57ab2de5d7384c4df44c0701211cd
1 /* Copyright (C) 2006, Red Hat, Inc. */
3 #include <linux/bitops.h>
4 #include <net/ieee80211.h>
6 #include "assoc.h"
7 #include "join.h"
8 #include "decl.h"
9 #include "hostcmd.h"
10 #include "host.h"
13 static const u8 bssid_any[ETH_ALEN] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
14 static const u8 bssid_off[ETH_ALEN] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
16 static int assoc_helper_essid(wlan_private *priv,
17 struct assoc_request * assoc_req)
19 wlan_adapter *adapter = priv->adapter;
20 int ret = 0;
21 struct bss_descriptor * bss;
23 lbs_deb_enter(LBS_DEB_ASSOC);
25 /* FIXME: take channel into account when picking SSIDs if a channel
26 * is set.
29 lbs_deb_assoc("New SSID requested: %s\n", assoc_req->ssid.ssid);
30 if (assoc_req->mode == IW_MODE_INFRA) {
31 if (adapter->prescan) {
32 libertas_send_specific_SSID_scan(priv, &assoc_req->ssid, 0);
35 bss = libertas_find_SSID_in_list(adapter, &assoc_req->ssid,
36 NULL, IW_MODE_INFRA);
37 if (bss != NULL) {
38 lbs_deb_assoc("SSID found in scan list, associating\n");
39 ret = wlan_associate(priv, bss);
40 if (ret == 0) {
41 memcpy(&assoc_req->bssid, bss->bssid, ETH_ALEN);
43 } else {
44 lbs_deb_assoc("SSID '%s' not found; cannot associate\n",
45 assoc_req->ssid.ssid);
47 } else if (assoc_req->mode == IW_MODE_ADHOC) {
48 /* Scan for the network, do not save previous results. Stale
49 * scan data will cause us to join a non-existant adhoc network
51 libertas_send_specific_SSID_scan(priv, &assoc_req->ssid, 1);
53 /* Search for the requested SSID in the scan table */
54 bss = libertas_find_SSID_in_list(adapter, &assoc_req->ssid, NULL,
55 IW_MODE_ADHOC);
56 if (bss != NULL) {
57 lbs_deb_assoc("SSID found joining\n");
58 libertas_join_adhoc_network(priv, bss);
59 } else {
60 /* else send START command */
61 lbs_deb_assoc("SSID not found in list, so creating adhoc"
62 " with SSID '%s'\n", assoc_req->ssid.ssid);
63 libertas_start_adhoc_network(priv, &assoc_req->ssid);
65 memcpy(&assoc_req->bssid, &adapter->current_addr, ETH_ALEN);
68 lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret);
69 return ret;
73 static int assoc_helper_bssid(wlan_private *priv,
74 struct assoc_request * assoc_req)
76 wlan_adapter *adapter = priv->adapter;
77 int ret = 0;
78 struct bss_descriptor * bss;
80 lbs_deb_enter_args(LBS_DEB_ASSOC, "BSSID" MAC_FMT "\n",
81 MAC_ARG(assoc_req->bssid));
83 /* Search for index position in list for requested MAC */
84 bss = libertas_find_BSSID_in_list(adapter, assoc_req->bssid,
85 assoc_req->mode);
86 if (bss == NULL) {
87 lbs_deb_assoc("ASSOC: WAP: BSSID " MAC_FMT " not found, "
88 "cannot associate.\n", MAC_ARG(assoc_req->bssid));
89 goto out;
92 if (assoc_req->mode == IW_MODE_INFRA) {
93 ret = wlan_associate(priv, bss);
94 lbs_deb_assoc("ASSOC: wlan_associate(bssid) returned %d\n", ret);
95 } else if (assoc_req->mode == IW_MODE_ADHOC) {
96 libertas_join_adhoc_network(priv, bss);
98 memcpy(&assoc_req->ssid, &bss->ssid, sizeof(struct WLAN_802_11_SSID));
100 out:
101 lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret);
102 return ret;
106 static int assoc_helper_associate(wlan_private *priv,
107 struct assoc_request * assoc_req)
109 int ret = 0, done = 0;
111 /* If we're given and 'any' BSSID, try associating based on SSID */
113 if (test_bit(ASSOC_FLAG_BSSID, &assoc_req->flags)) {
114 if (memcmp(bssid_any, assoc_req->bssid, ETH_ALEN)
115 && memcmp(bssid_off, assoc_req->bssid, ETH_ALEN)) {
116 ret = assoc_helper_bssid(priv, assoc_req);
117 done = 1;
118 if (ret) {
119 lbs_deb_assoc("ASSOC: bssid: ret = %d\n", ret);
124 if (!done && test_bit(ASSOC_FLAG_SSID, &assoc_req->flags)) {
125 ret = assoc_helper_essid(priv, assoc_req);
126 if (ret) {
127 lbs_deb_assoc("ASSOC: bssid: ret = %d\n", ret);
131 return ret;
135 static int assoc_helper_mode(wlan_private *priv,
136 struct assoc_request * assoc_req)
138 wlan_adapter *adapter = priv->adapter;
139 int ret = 0;
141 lbs_deb_enter(LBS_DEB_ASSOC);
143 if (assoc_req->mode == adapter->mode)
144 goto done;
146 if (assoc_req->mode == IW_MODE_INFRA) {
147 if (adapter->psstate != PS_STATE_FULL_POWER)
148 libertas_ps_wakeup(priv, cmd_option_waitforrsp);
149 adapter->psmode = wlan802_11powermodecam;
152 adapter->mode = assoc_req->mode;
153 ret = libertas_prepare_and_send_command(priv,
154 cmd_802_11_snmp_mib,
155 0, cmd_option_waitforrsp,
156 OID_802_11_INFRASTRUCTURE_MODE,
157 (void *) (size_t) assoc_req->mode);
159 done:
160 lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret);
161 return ret;
165 static int update_channel(wlan_private * priv)
167 /* the channel in f/w could be out of sync, get the current channel */
168 return libertas_prepare_and_send_command(priv, cmd_802_11_rf_channel,
169 cmd_opt_802_11_rf_channel_get,
170 cmd_option_waitforrsp, 0, NULL);
173 static int assoc_helper_channel(wlan_private *priv,
174 struct assoc_request * assoc_req)
176 wlan_adapter *adapter = priv->adapter;
177 int ret = 0;
179 lbs_deb_enter(LBS_DEB_ASSOC);
181 ret = update_channel(priv);
182 if (ret < 0) {
183 lbs_deb_assoc("ASSOC: channel: error getting channel.");
186 if (assoc_req->channel == adapter->curbssparams.channel)
187 goto done;
189 lbs_deb_assoc("ASSOC: channel: %d -> %d\n",
190 adapter->curbssparams.channel, assoc_req->channel);
192 ret = libertas_prepare_and_send_command(priv, cmd_802_11_rf_channel,
193 cmd_opt_802_11_rf_channel_set,
194 cmd_option_waitforrsp, 0, &assoc_req->channel);
195 if (ret < 0) {
196 lbs_deb_assoc("ASSOC: channel: error setting channel.");
199 ret = update_channel(priv);
200 if (ret < 0) {
201 lbs_deb_assoc("ASSOC: channel: error getting channel.");
204 if (assoc_req->channel != adapter->curbssparams.channel) {
205 lbs_deb_assoc("ASSOC: channel: failed to update channel to %d",
206 assoc_req->channel);
207 goto done;
210 if ( assoc_req->secinfo.wep_enabled
211 && (assoc_req->wep_keys[0].len
212 || assoc_req->wep_keys[1].len
213 || assoc_req->wep_keys[2].len
214 || assoc_req->wep_keys[3].len)) {
215 /* Make sure WEP keys are re-sent to firmware */
216 set_bit(ASSOC_FLAG_WEP_KEYS, &assoc_req->flags);
219 /* Must restart/rejoin adhoc networks after channel change */
220 set_bit(ASSOC_FLAG_SSID, &assoc_req->flags);
222 done:
223 lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret);
224 return ret;
228 static int assoc_helper_wep_keys(wlan_private *priv,
229 struct assoc_request * assoc_req)
231 wlan_adapter *adapter = priv->adapter;
232 int i;
233 int ret = 0;
235 lbs_deb_enter(LBS_DEB_ASSOC);
237 /* Set or remove WEP keys */
238 if ( assoc_req->wep_keys[0].len
239 || assoc_req->wep_keys[1].len
240 || assoc_req->wep_keys[2].len
241 || assoc_req->wep_keys[3].len) {
242 ret = libertas_prepare_and_send_command(priv,
243 cmd_802_11_set_wep,
244 cmd_act_add,
245 cmd_option_waitforrsp,
246 0, assoc_req);
247 } else {
248 ret = libertas_prepare_and_send_command(priv,
249 cmd_802_11_set_wep,
250 cmd_act_remove,
251 cmd_option_waitforrsp,
252 0, NULL);
255 if (ret)
256 goto out;
258 /* enable/disable the MAC's WEP packet filter */
259 if (assoc_req->secinfo.wep_enabled)
260 adapter->currentpacketfilter |= cmd_act_mac_wep_enable;
261 else
262 adapter->currentpacketfilter &= ~cmd_act_mac_wep_enable;
263 ret = libertas_set_mac_packet_filter(priv);
264 if (ret)
265 goto out;
267 mutex_lock(&adapter->lock);
269 /* Copy WEP keys into adapter wep key fields */
270 for (i = 0; i < 4; i++) {
271 memcpy(&adapter->wep_keys[i], &assoc_req->wep_keys[i],
272 sizeof(struct WLAN_802_11_KEY));
274 adapter->wep_tx_keyidx = assoc_req->wep_tx_keyidx;
276 mutex_unlock(&adapter->lock);
278 out:
279 lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret);
280 return ret;
283 static int assoc_helper_secinfo(wlan_private *priv,
284 struct assoc_request * assoc_req)
286 wlan_adapter *adapter = priv->adapter;
287 int ret = 0;
289 lbs_deb_enter(LBS_DEB_ASSOC);
291 memcpy(&adapter->secinfo, &assoc_req->secinfo,
292 sizeof(struct wlan_802_11_security));
294 ret = libertas_set_mac_packet_filter(priv);
296 lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret);
297 return ret;
301 static int assoc_helper_wpa_keys(wlan_private *priv,
302 struct assoc_request * assoc_req)
304 int ret = 0;
306 lbs_deb_enter(LBS_DEB_ASSOC);
308 /* enable/Disable RSN */
309 ret = libertas_prepare_and_send_command(priv,
310 cmd_802_11_enable_rsn,
311 cmd_act_set,
312 cmd_option_waitforrsp,
313 0, assoc_req);
314 if (ret)
315 goto out;
317 ret = libertas_prepare_and_send_command(priv,
318 cmd_802_11_key_material,
319 cmd_act_set,
320 cmd_option_waitforrsp,
321 0, assoc_req);
323 out:
324 lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret);
325 return ret;
329 static int assoc_helper_wpa_ie(wlan_private *priv,
330 struct assoc_request * assoc_req)
332 wlan_adapter *adapter = priv->adapter;
333 int ret = 0;
335 lbs_deb_enter(LBS_DEB_ASSOC);
337 if (assoc_req->secinfo.WPAenabled || assoc_req->secinfo.WPA2enabled) {
338 memcpy(&adapter->wpa_ie, &assoc_req->wpa_ie, assoc_req->wpa_ie_len);
339 adapter->wpa_ie_len = assoc_req->wpa_ie_len;
340 } else {
341 memset(&adapter->wpa_ie, 0, MAX_WPA_IE_LEN);
342 adapter->wpa_ie_len = 0;
345 lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret);
346 return ret;
350 static int should_deauth_infrastructure(wlan_adapter *adapter,
351 struct assoc_request * assoc_req)
353 if (adapter->connect_status != libertas_connected)
354 return 0;
356 if (test_bit(ASSOC_FLAG_SSID, &assoc_req->flags)) {
357 lbs_deb_assoc("Deauthenticating due to new SSID in "
358 " configuration request.\n");
359 return 1;
362 if (test_bit(ASSOC_FLAG_SECINFO, &assoc_req->flags)) {
363 if (adapter->secinfo.auth_mode != assoc_req->secinfo.auth_mode) {
364 lbs_deb_assoc("Deauthenticating due to updated security "
365 "info in configuration request.\n");
366 return 1;
370 if (test_bit(ASSOC_FLAG_BSSID, &assoc_req->flags)) {
371 lbs_deb_assoc("Deauthenticating due to new BSSID in "
372 " configuration request.\n");
373 return 1;
376 /* FIXME: deal with 'auto' mode somehow */
377 if (test_bit(ASSOC_FLAG_MODE, &assoc_req->flags)) {
378 if (assoc_req->mode != IW_MODE_INFRA)
379 return 1;
382 return 0;
386 static int should_stop_adhoc(wlan_adapter *adapter,
387 struct assoc_request * assoc_req)
389 if (adapter->connect_status != libertas_connected)
390 return 0;
392 if (adapter->curbssparams.ssid.ssidlength != assoc_req->ssid.ssidlength)
393 return 1;
394 if (memcmp(adapter->curbssparams.ssid.ssid, assoc_req->ssid.ssid,
395 adapter->curbssparams.ssid.ssidlength))
396 return 1;
398 /* FIXME: deal with 'auto' mode somehow */
399 if (test_bit(ASSOC_FLAG_MODE, &assoc_req->flags)) {
400 if (assoc_req->mode != IW_MODE_ADHOC)
401 return 1;
404 if (test_bit(ASSOC_FLAG_CHANNEL, &assoc_req->flags)) {
405 if (assoc_req->channel != adapter->curbssparams.channel)
406 return 1;
409 return 0;
413 void libertas_association_worker(struct work_struct *work)
415 wlan_private *priv = container_of(work, wlan_private, assoc_work.work);
416 wlan_adapter *adapter = priv->adapter;
417 struct assoc_request * assoc_req = NULL;
418 int ret = 0;
419 int find_any_ssid = 0;
421 lbs_deb_enter(LBS_DEB_ASSOC);
423 mutex_lock(&adapter->lock);
424 assoc_req = adapter->assoc_req;
425 adapter->assoc_req = NULL;
426 mutex_unlock(&adapter->lock);
428 if (!assoc_req)
429 goto done;
431 lbs_deb_assoc("ASSOC: starting new association request: flags = 0x%lX\n",
432 assoc_req->flags);
434 /* If 'any' SSID was specified, find an SSID to associate with */
435 if (test_bit(ASSOC_FLAG_SSID, &assoc_req->flags)
436 && !assoc_req->ssid.ssidlength)
437 find_any_ssid = 1;
439 /* But don't use 'any' SSID if there's a valid locked BSSID to use */
440 if (test_bit(ASSOC_FLAG_BSSID, &assoc_req->flags)) {
441 if (memcmp(&assoc_req->bssid, bssid_any, ETH_ALEN)
442 && memcmp(&assoc_req->bssid, bssid_off, ETH_ALEN))
443 find_any_ssid = 0;
446 if (find_any_ssid) {
447 u8 new_mode;
449 ret = libertas_find_best_network_SSID(priv, &assoc_req->ssid,
450 assoc_req->mode, &new_mode);
451 if (ret) {
452 lbs_deb_assoc("Could not find best network\n");
453 ret = -ENETUNREACH;
454 goto out;
457 /* Ensure we switch to the mode of the AP */
458 if (assoc_req->mode == IW_MODE_AUTO) {
459 set_bit(ASSOC_FLAG_MODE, &assoc_req->flags);
460 assoc_req->mode = new_mode;
465 * Check if the attributes being changing require deauthentication
466 * from the currently associated infrastructure access point.
468 if (adapter->mode == IW_MODE_INFRA) {
469 if (should_deauth_infrastructure(adapter, assoc_req)) {
470 ret = libertas_send_deauthentication(priv);
471 if (ret) {
472 lbs_deb_assoc("Deauthentication due to new "
473 "configuration request failed: %d\n",
474 ret);
477 } else if (adapter->mode == IW_MODE_ADHOC) {
478 if (should_stop_adhoc(adapter, assoc_req)) {
479 ret = libertas_stop_adhoc_network(priv);
480 if (ret) {
481 lbs_deb_assoc("Teardown of AdHoc network due to "
482 "new configuration request failed: %d\n",
483 ret);
489 /* Send the various configuration bits to the firmware */
490 if (test_bit(ASSOC_FLAG_MODE, &assoc_req->flags)) {
491 ret = assoc_helper_mode(priv, assoc_req);
492 if (ret) {
493 lbs_deb_assoc("ASSOC(:%d) mode: ret = %d\n", __LINE__, ret);
494 goto out;
498 if (test_bit(ASSOC_FLAG_CHANNEL, &assoc_req->flags)) {
499 ret = assoc_helper_channel(priv, assoc_req);
500 if (ret) {
501 lbs_deb_assoc("ASSOC(:%d) channel: ret = %d\n",
502 __LINE__, ret);
503 goto out;
507 if ( test_bit(ASSOC_FLAG_WEP_KEYS, &assoc_req->flags)
508 || test_bit(ASSOC_FLAG_WEP_TX_KEYIDX, &assoc_req->flags)) {
509 ret = assoc_helper_wep_keys(priv, assoc_req);
510 if (ret) {
511 lbs_deb_assoc("ASSOC(:%d) wep_keys: ret = %d\n", __LINE__, ret);
512 goto out;
516 if (test_bit(ASSOC_FLAG_SECINFO, &assoc_req->flags)) {
517 ret = assoc_helper_secinfo(priv, assoc_req);
518 if (ret) {
519 lbs_deb_assoc("ASSOC(:%d) secinfo: ret = %d\n", __LINE__, ret);
520 goto out;
524 if (test_bit(ASSOC_FLAG_WPA_IE, &assoc_req->flags)) {
525 ret = assoc_helper_wpa_ie(priv, assoc_req);
526 if (ret) {
527 lbs_deb_assoc("ASSOC(:%d) wpa_ie: ret = %d\n", __LINE__, ret);
528 goto out;
532 if (test_bit(ASSOC_FLAG_WPA_MCAST_KEY, &assoc_req->flags)
533 || test_bit(ASSOC_FLAG_WPA_UCAST_KEY, &assoc_req->flags)) {
534 ret = assoc_helper_wpa_keys(priv, assoc_req);
535 if (ret) {
536 lbs_deb_assoc("ASSOC(:%d) wpa_keys: ret = %d\n", __LINE__, ret);
537 goto out;
541 /* SSID/BSSID should be the _last_ config option set, because they
542 * trigger the association attempt.
544 if (test_bit(ASSOC_FLAG_BSSID, &assoc_req->flags)
545 || test_bit(ASSOC_FLAG_SSID, &assoc_req->flags)) {
546 int success = 1;
548 ret = assoc_helper_associate(priv, assoc_req);
549 if (ret) {
550 lbs_deb_assoc("ASSOC: association attempt unsuccessful: %d\n",
551 ret);
552 success = 0;
555 if (adapter->connect_status != libertas_connected) {
556 lbs_deb_assoc("ASSOC: assoication attempt unsuccessful, "
557 "not connected.\n");
558 success = 0;
561 if (success) {
562 lbs_deb_assoc("ASSOC: association attempt successful. "
563 "Associated to '%s' (" MAC_FMT ")\n",
564 assoc_req->ssid.ssid, MAC_ARG(assoc_req->bssid));
565 libertas_prepare_and_send_command(priv,
566 cmd_802_11_rssi,
567 0, cmd_option_waitforrsp, 0, NULL);
569 libertas_prepare_and_send_command(priv,
570 cmd_802_11_get_log,
571 0, cmd_option_waitforrsp, 0, NULL);
572 } else {
574 ret = -1;
578 out:
579 if (ret) {
580 lbs_deb_assoc("ASSOC: reconfiguration attempt unsuccessful: %d\n",
581 ret);
583 kfree(assoc_req);
585 done:
586 lbs_deb_leave(LBS_DEB_ASSOC);
591 * Caller MUST hold any necessary locks
593 struct assoc_request * wlan_get_association_request(wlan_adapter *adapter)
595 struct assoc_request * assoc_req;
597 if (!adapter->assoc_req) {
598 adapter->assoc_req = kzalloc(sizeof(struct assoc_request), GFP_KERNEL);
599 if (!adapter->assoc_req) {
600 lbs_pr_info("Not enough memory to allocate association"
601 " request!\n");
602 return NULL;
606 /* Copy current configuration attributes to the association request,
607 * but don't overwrite any that are already set.
609 assoc_req = adapter->assoc_req;
610 if (!test_bit(ASSOC_FLAG_SSID, &assoc_req->flags)) {
611 memcpy(&assoc_req->ssid, adapter->curbssparams.ssid.ssid,
612 adapter->curbssparams.ssid.ssidlength);
615 if (!test_bit(ASSOC_FLAG_CHANNEL, &assoc_req->flags))
616 assoc_req->channel = adapter->curbssparams.channel;
618 if (!test_bit(ASSOC_FLAG_MODE, &assoc_req->flags))
619 assoc_req->mode = adapter->mode;
621 if (!test_bit(ASSOC_FLAG_BSSID, &assoc_req->flags)) {
622 memcpy(&assoc_req->bssid, adapter->curbssparams.bssid,
623 ETH_ALEN);
626 if (!test_bit(ASSOC_FLAG_WEP_KEYS, &assoc_req->flags)) {
627 int i;
628 for (i = 0; i < 4; i++) {
629 memcpy(&assoc_req->wep_keys[i], &adapter->wep_keys[i],
630 sizeof(struct WLAN_802_11_KEY));
634 if (!test_bit(ASSOC_FLAG_WEP_TX_KEYIDX, &assoc_req->flags))
635 assoc_req->wep_tx_keyidx = adapter->wep_tx_keyidx;
637 if (!test_bit(ASSOC_FLAG_WPA_MCAST_KEY, &assoc_req->flags)) {
638 memcpy(&assoc_req->wpa_mcast_key, &adapter->wpa_mcast_key,
639 sizeof(struct WLAN_802_11_KEY));
642 if (!test_bit(ASSOC_FLAG_WPA_UCAST_KEY, &assoc_req->flags)) {
643 memcpy(&assoc_req->wpa_unicast_key, &adapter->wpa_unicast_key,
644 sizeof(struct WLAN_802_11_KEY));
647 if (!test_bit(ASSOC_FLAG_SECINFO, &assoc_req->flags)) {
648 memcpy(&assoc_req->secinfo, &adapter->secinfo,
649 sizeof(struct wlan_802_11_security));
652 if (!test_bit(ASSOC_FLAG_WPA_IE, &assoc_req->flags)) {
653 memcpy(&assoc_req->wpa_ie, &adapter->wpa_ie,
654 MAX_WPA_IE_LEN);
655 assoc_req->wpa_ie_len = adapter->wpa_ie_len;
658 return assoc_req;