1 /* Copyright (C) 2006, Red Hat, Inc. */
3 #include <linux/bitops.h>
4 #include <net/ieee80211.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
;
21 struct bss_descriptor
* bss
;
23 lbs_deb_enter(LBS_DEB_ASSOC
);
25 /* FIXME: take channel into account when picking SSIDs if a channel
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
,
38 lbs_deb_assoc("SSID found in scan list, associating\n");
39 ret
= wlan_associate(priv
, bss
);
41 memcpy(&assoc_req
->bssid
, bss
->bssid
, ETH_ALEN
);
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
,
57 lbs_deb_assoc("SSID found joining\n");
58 libertas_join_adhoc_network(priv
, bss
);
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
);
73 static int assoc_helper_bssid(wlan_private
*priv
,
74 struct assoc_request
* assoc_req
)
76 wlan_adapter
*adapter
= priv
->adapter
;
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
,
87 lbs_deb_assoc("ASSOC: WAP: BSSID " MAC_FMT
" not found, "
88 "cannot associate.\n", MAC_ARG(assoc_req
->bssid
));
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
));
101 lbs_deb_leave_args(LBS_DEB_ASSOC
, "ret %d", 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
);
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
);
127 lbs_deb_assoc("ASSOC: bssid: ret = %d\n", ret
);
135 static int assoc_helper_mode(wlan_private
*priv
,
136 struct assoc_request
* assoc_req
)
138 wlan_adapter
*adapter
= priv
->adapter
;
141 lbs_deb_enter(LBS_DEB_ASSOC
);
143 if (assoc_req
->mode
== adapter
->mode
)
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
,
155 0, cmd_option_waitforrsp
,
156 OID_802_11_INFRASTRUCTURE_MODE
,
157 (void *) (size_t) assoc_req
->mode
);
160 lbs_deb_leave_args(LBS_DEB_ASSOC
, "ret %d", 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
;
179 lbs_deb_enter(LBS_DEB_ASSOC
);
181 ret
= update_channel(priv
);
183 lbs_deb_assoc("ASSOC: channel: error getting channel.");
186 if (assoc_req
->channel
== adapter
->curbssparams
.channel
)
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
);
196 lbs_deb_assoc("ASSOC: channel: error setting channel.");
199 ret
= update_channel(priv
);
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",
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
);
223 lbs_deb_leave_args(LBS_DEB_ASSOC
, "ret %d", ret
);
228 static int assoc_helper_wep_keys(wlan_private
*priv
,
229 struct assoc_request
* assoc_req
)
231 wlan_adapter
*adapter
= priv
->adapter
;
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
,
245 cmd_option_waitforrsp
,
248 ret
= libertas_prepare_and_send_command(priv
,
251 cmd_option_waitforrsp
,
258 /* enable/disable the MAC's WEP packet filter */
259 if (assoc_req
->secinfo
.wep_enabled
)
260 adapter
->currentpacketfilter
|= cmd_act_mac_wep_enable
;
262 adapter
->currentpacketfilter
&= ~cmd_act_mac_wep_enable
;
263 ret
= libertas_set_mac_packet_filter(priv
);
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
);
279 lbs_deb_leave_args(LBS_DEB_ASSOC
, "ret %d", ret
);
283 static int assoc_helper_secinfo(wlan_private
*priv
,
284 struct assoc_request
* assoc_req
)
286 wlan_adapter
*adapter
= priv
->adapter
;
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
);
301 static int assoc_helper_wpa_keys(wlan_private
*priv
,
302 struct assoc_request
* assoc_req
)
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
,
312 cmd_option_waitforrsp
,
317 ret
= libertas_prepare_and_send_command(priv
,
318 cmd_802_11_key_material
,
320 cmd_option_waitforrsp
,
324 lbs_deb_leave_args(LBS_DEB_ASSOC
, "ret %d", ret
);
329 static int assoc_helper_wpa_ie(wlan_private
*priv
,
330 struct assoc_request
* assoc_req
)
332 wlan_adapter
*adapter
= priv
->adapter
;
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
;
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
);
350 static int should_deauth_infrastructure(wlan_adapter
*adapter
,
351 struct assoc_request
* assoc_req
)
353 if (adapter
->connect_status
!= libertas_connected
)
356 if (test_bit(ASSOC_FLAG_SSID
, &assoc_req
->flags
)) {
357 lbs_deb_assoc("Deauthenticating due to new SSID in "
358 " configuration request.\n");
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");
370 if (test_bit(ASSOC_FLAG_BSSID
, &assoc_req
->flags
)) {
371 lbs_deb_assoc("Deauthenticating due to new BSSID in "
372 " configuration request.\n");
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
)
386 static int should_stop_adhoc(wlan_adapter
*adapter
,
387 struct assoc_request
* assoc_req
)
389 if (adapter
->connect_status
!= libertas_connected
)
392 if (adapter
->curbssparams
.ssid
.ssidlength
!= assoc_req
->ssid
.ssidlength
)
394 if (memcmp(adapter
->curbssparams
.ssid
.ssid
, assoc_req
->ssid
.ssid
,
395 adapter
->curbssparams
.ssid
.ssidlength
))
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
)
404 if (test_bit(ASSOC_FLAG_CHANNEL
, &assoc_req
->flags
)) {
405 if (assoc_req
->channel
!= adapter
->curbssparams
.channel
)
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
;
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
);
431 lbs_deb_assoc("ASSOC: starting new association request: flags = 0x%lX\n",
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
)
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
))
449 ret
= libertas_find_best_network_SSID(priv
, &assoc_req
->ssid
,
450 assoc_req
->mode
, &new_mode
);
452 lbs_deb_assoc("Could not find best network\n");
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
);
472 lbs_deb_assoc("Deauthentication due to new "
473 "configuration request failed: %d\n",
477 } else if (adapter
->mode
== IW_MODE_ADHOC
) {
478 if (should_stop_adhoc(adapter
, assoc_req
)) {
479 ret
= libertas_stop_adhoc_network(priv
);
481 lbs_deb_assoc("Teardown of AdHoc network due to "
482 "new configuration request failed: %d\n",
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
);
493 lbs_deb_assoc("ASSOC(:%d) mode: ret = %d\n", __LINE__
, ret
);
498 if (test_bit(ASSOC_FLAG_CHANNEL
, &assoc_req
->flags
)) {
499 ret
= assoc_helper_channel(priv
, assoc_req
);
501 lbs_deb_assoc("ASSOC(:%d) channel: ret = %d\n",
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
);
511 lbs_deb_assoc("ASSOC(:%d) wep_keys: ret = %d\n", __LINE__
, ret
);
516 if (test_bit(ASSOC_FLAG_SECINFO
, &assoc_req
->flags
)) {
517 ret
= assoc_helper_secinfo(priv
, assoc_req
);
519 lbs_deb_assoc("ASSOC(:%d) secinfo: ret = %d\n", __LINE__
, ret
);
524 if (test_bit(ASSOC_FLAG_WPA_IE
, &assoc_req
->flags
)) {
525 ret
= assoc_helper_wpa_ie(priv
, assoc_req
);
527 lbs_deb_assoc("ASSOC(:%d) wpa_ie: ret = %d\n", __LINE__
, ret
);
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
);
536 lbs_deb_assoc("ASSOC(:%d) wpa_keys: ret = %d\n", __LINE__
, ret
);
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
)) {
548 ret
= assoc_helper_associate(priv
, assoc_req
);
550 lbs_deb_assoc("ASSOC: association attempt unsuccessful: %d\n",
555 if (adapter
->connect_status
!= libertas_connected
) {
556 lbs_deb_assoc("ASSOC: assoication attempt unsuccessful, "
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
,
567 0, cmd_option_waitforrsp
, 0, NULL
);
569 libertas_prepare_and_send_command(priv
,
571 0, cmd_option_waitforrsp
, 0, NULL
);
580 lbs_deb_assoc("ASSOC: reconfiguration attempt unsuccessful: %d\n",
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"
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
,
626 if (!test_bit(ASSOC_FLAG_WEP_KEYS
, &assoc_req
->flags
)) {
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
,
655 assoc_req
->wpa_ie_len
= adapter
->wpa_ie_len
;